From 7b97c6f7caa68c097bf5a1da7ba5b0192f31e770 Mon Sep 17 00:00:00 2001 From: darkloops Date: Wed, 29 Jan 2014 13:10:14 +0100 Subject: [PATCH 1/3] Add strong touch event handling for scrollable.js Use changedTouches instead of touches. Determine if moves are vertical or horizontal. Ignore horizontal moves on vertical scrollable, respectively vertical moves on horizontal scrollable (this allow the user to scroll the page on horizontal scrollable). Do not scroll on tiny moves. Tested successfully on Android default browser, Chrome mobile and Safari mobile. Firefox should be ok. @see https://github.com/jquerytools/jquerytools/issues/711 --- src/scrollable/scrollable.js | 201 ++++++++++++++++++++++++++++++++--- 1 file changed, 189 insertions(+), 12 deletions(-) diff --git a/src/scrollable/scrollable.js b/src/scrollable/scrollable.js index eb834d7..f231925 100644 --- a/src/scrollable/scrollable.js +++ b/src/scrollable/scrollable.js @@ -289,27 +289,204 @@ } // touch event - if (conf.touch) { + if (conf.touch && ('ontouchstart' in document)) { var touch = {}; + // yes, you want a limit to not launch scrollable on tiny moves + // this value can come from configuration, but I do not want to be too intrusive + var touchMinDelta = conf.touchMinDelta || 25; + + // find best way to get the TouchList prototype + // attemp to find the TouchList prototype in use + var touchListProto = ( function(){ + // standard method + if (window.TouchList) { + return TouchList.prototype; + // can we look for document.createTouchList (Android) + } else if ('getPrototypeOf' in Object && 'createTouchList' in document) { + // get prototype of this + return Object.getPrototypeOf( document.createTouchList() ); + } else { + // oups... + return false; + } + } )(); + + // prototype this method if not exists + if (touchListProto && !('identifiedTouch' in touchListProto)) { + touchListProto.identifiedTouch = function( id ) { + var i = this.length, t; + while( i-- ) { + t = this.item( i ); + // found, stop and return it + if( t.identifier === id ) return t; + } + // not found + return false; + }; + } itemWrap[0].ontouchstart = function(e) { - var t = e.touches[0]; - touch.x = t.clientX; - touch.y = t.clientY; + // at first, look if it is not a second touch + // touch identifier can be 0 + if(touch.id === undefined) { + // we want the first touch that trigger the event + var t = e.changedTouches[0]; + touch.id = t.identifier; + touch.x = t.clientX; + touch.y = t.clientY; + } }; itemWrap[0].ontouchmove = function(e) { - // only deal with one finger - if (e.touches.length == 1 && !itemWrap.is(":animated")) { - var t = e.touches[0], - deltaX = touch.x - t.clientX, - deltaY = touch.y - t.clientY; - - self[vertical && deltaY > 0 || !vertical && deltaX > 0 ? 'next' : 'prev'](); - e.preventDefault(); + // do the current touch still on contact + // - first contact (registered) + // - second contact (not regisetred) + // - remove first contact (unregistred) + // - move second contact + // = will trigger a touchmove event from the second contact, but the + // first one is not available anymore + // touch identifier can be 0 + if (touch.id !== undefined && !itemWrap.is(":animated")) { + + // look if the current touch has trigger the event + var t = e.changedTouches.identifiedTouch(touch.id); + if (t) { + // ok, we can do the job now + // >0 from right to left, <0 from left to right + var deltaX = touch.x - t.clientX; + var absdeltaX = Math.abs( deltaX ); + // >0 from bottom to top, <0 from top to bottom + var deltaY = touch.y - t.clientY; + var absdeltaY = Math.abs( deltaY ); + + // look for the biggest one to determine vertical or horizontal move + if (absdeltaX > absdeltaY) { + // horizontal move + + // if the scrollable is vertical, do not do anything + if(vertical) return true; + + // prevent tiny move + if(absdeltaX > touchMinDelta){ + // we are ok, we can process move + // >0 from right to left: move right, next + // <0 from left to right: move left, prev + self[ deltaX > 0 ? 'next' : 'prev'](); + + // reassign x/y + touch.x = t.clientX; + touch.y = t.clientY; + } + + // prevent default, even on tiny moves + e.preventDefault(); + + } else if (absdeltaX < absdeltaY) { + // vertical move + + // if the scrollable is not vertical, do not do anything + if (!vertical) return true; + + // prevent tiny move + if(absdeltaY > touchMinDelta){ + // we are ok, we can process move + // >0 from bottom to top: move down, prev + // <0 from top to bottom: move up, next + self[ deltaY > 0 ? 'prev' : 'next'](); + + // reassign x/y + touch.x = t.clientX; + touch.y = t.clientY; + } + + // prevent default, even on tiny moves + e.preventDefault(); + + } else { + // can be both + if (vertical) { + // consider vertical + + // prevent tiny move + if(absdeltaY > touchMinDelta){ + // we are ok, we can process move + // >0 from bottom to top: move down, prev + // <0 from top to bottom: move up, next + self[ deltaY > 0 ? 'prev' : 'next'](); + + // reassign x/y + touch.x = t.clientX; + touch.y = t.clientY; + } + + // prevent default, even on tiny moves + e.preventDefault(); + } else { + // consider horizontal + + // prevent tiny move + if(absdeltaX > touchMinDelta){ + // we are ok, we can process move + // >0 from right to left: move right, next + // <0 from left to right: move left, prev + self[ deltaX > 0 ? 'next' : 'prev'](); + + // reassign x/y + touch.x = t.clientX; + touch.y = t.clientY; + } + + // prevent default, even on tiny moves + e.preventDefault(); + } + } + } } }; + + itemWrap[0].ontouchend = function(e) { + // do the current touch still on contact + // touch identifier can be 0 + if (touch.id !== undefined) { + // look if the current touch has trigger the event + if(e.changedTouches.identifiedTouch(touch.id)) { + // ok, unregister + touch = {}; + // if there was a move, a touchmove should have + // been trigger before this event + } + } + }; + + itemWrap[0].ontouchcancel = function(e) { + // do the current touch still on contact + // touch identifier can be 0 + if (touch.id !== undefined) { + // look if the current touch has trigger the event + if(e.changedTouches.identifiedTouch(touch.id)) { + // ok, unregister + touch = {}; + // if there was a move, a touchmove should have + // been trigger before this event + } + } + }; + + itemWrap[0].ontouchleave = function(e) { + // do the current touch still on contact + // touch identifier can be 0 + if (touch.id !== undefined) { + // look if the current touch has trigger the event + if(e.changedTouches.identifiedTouch(touch.id)) { + // ok, unregister + touch = {}; + // if there was a move, a touchmove should have + // been trigger before this event + } + } + }; + } if (conf.keyboard) { From a8b16657527a641118d4b04d4902d681ceb71350 Mon Sep 17 00:00:00 2001 From: darkloops Date: Wed, 29 Jan 2014 14:21:25 +0100 Subject: [PATCH 2/3] Update polyfill part I was not so happy with the previous version. --- src/scrollable/scrollable.js | 56 +++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/scrollable/scrollable.js b/src/scrollable/scrollable.js index f231925..d651f9e 100644 --- a/src/scrollable/scrollable.js +++ b/src/scrollable/scrollable.js @@ -295,35 +295,45 @@ // this value can come from configuration, but I do not want to be too intrusive var touchMinDelta = conf.touchMinDelta || 25; - // find best way to get the TouchList prototype - // attemp to find the TouchList prototype in use - var touchListProto = ( function(){ + // polyfill part + ( function(){ + // find best way to get the TouchList prototype + var proto; + // standard method - if (window.TouchList) { - return TouchList.prototype; - // can we look for document.createTouchList (Android) - } else if ('getPrototypeOf' in Object && 'createTouchList' in document) { + if( window.TouchList ) { + proto = window.TouchList.prototype; + // can we look for document.createTouchList + } else if( 'createTouchList' in document ) { // get prototype of this - return Object.getPrototypeOf( document.createTouchList() ); + if( 'getPrototypeOf' in window.Object ) { + proto = window.Object.getPrototypeOf( document.createTouchList() ); + } else if( typeof 'xx'.__proto__ === 'object' ) { + proto = document.createTouchList().__proto__; + } else { + // may break + proto = document.createTouchList().constructor.prototype; + } } else { // oups... - return false; + return; + } + + // at this point proto is OK + // prototype the method if not exists + if(!('identifiedTouch' in proto)) { + proto.identifiedTouch = function( id ) { + var i = this.length, t; + while( i-- ) { + t = this.item( i ); + // found, stop and return it + if( t.identifier === id ) return t; + } + // not found + return false; + }; } } )(); - - // prototype this method if not exists - if (touchListProto && !('identifiedTouch' in touchListProto)) { - touchListProto.identifiedTouch = function( id ) { - var i = this.length, t; - while( i-- ) { - t = this.item( i ); - // found, stop and return it - if( t.identifier === id ) return t; - } - // not found - return false; - }; - } itemWrap[0].ontouchstart = function(e) { // at first, look if it is not a second touch From 486006028053844d5e01c0e79ebb7440ade799d5 Mon Sep 17 00:00:00 2001 From: darkloops Date: Mon, 3 Feb 2014 16:44:14 +0100 Subject: [PATCH 3/3] Correct wrong vertical moves Just reverse prev and next call on vertical scrollable. --- src/scrollable/scrollable.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/scrollable/scrollable.js b/src/scrollable/scrollable.js index d651f9e..0d5f267 100644 --- a/src/scrollable/scrollable.js +++ b/src/scrollable/scrollable.js @@ -380,8 +380,8 @@ // prevent tiny move if(absdeltaX > touchMinDelta){ // we are ok, we can process move - // >0 from right to left: move right, next - // <0 from left to right: move left, prev + // >0 from right to left: move left, next + // <0 from left to right: move right, prev self[ deltaX > 0 ? 'next' : 'prev'](); // reassign x/y @@ -401,9 +401,9 @@ // prevent tiny move if(absdeltaY > touchMinDelta){ // we are ok, we can process move - // >0 from bottom to top: move down, prev - // <0 from top to bottom: move up, next - self[ deltaY > 0 ? 'prev' : 'next'](); + // >0 from bottom to top: move up, next + // <0 from top to bottom: move down, prev + self[ deltaY > 0 ? 'next' : 'prev'](); // reassign x/y touch.x = t.clientX; @@ -421,9 +421,9 @@ // prevent tiny move if(absdeltaY > touchMinDelta){ // we are ok, we can process move - // >0 from bottom to top: move down, prev - // <0 from top to bottom: move up, next - self[ deltaY > 0 ? 'prev' : 'next'](); + // >0 from bottom to top: move up, next + // <0 from top to bottom: move down, prev + self[ deltaY > 0 ? 'next' : 'prev'](); // reassign x/y touch.x = t.clientX; @@ -438,8 +438,8 @@ // prevent tiny move if(absdeltaX > touchMinDelta){ // we are ok, we can process move - // >0 from right to left: move right, next - // <0 from left to right: move left, prev + // >0 from right to left: move left, next + // <0 from left to right: move right, prev self[ deltaX > 0 ? 'next' : 'prev'](); // reassign x/y