touchイベント、tap、click、press、swipe


自動回転http://blog.mobiscroll.com/working-with-touch-events/
Working with touch イベント
Posted on アプリ16,2014  by Isti in R&D、 Tutoris touch事件,tap,click,press,slide,swipe_第1张图片
Last moth I was tensing the jQuery Europe conference in Vienna with the Mobiscroll team.The re was a session caled Getting touchywhich gave an insight into touch events and talked about why we need the m.The is a lot of ground that the presentation covers,so make sure to check out the sland.I would like to build to to to to to to to to excle and and and and and and and and and shence and and and and and and pance.
So why do we need touch events?
We don’t always need to use touch events.Turns out that mostly we can get away of using the reglar click becants because mobile browsers emulate the mouse the mousements.ramitware.mants.
The usual problems are:
Delayed event dispatch:mouse events are usualy fired with a delay,which makes the ap feel unreponsive.Mousemove doesn’t track,only a single emulated mousevent is fired.This makes impossible to make complex UI interfaces with gestures.The re are a ton of reources on the web targing these ises、so I’m not reinvent ting the where、I will just share my own personice on how I commbined and exted different solunt solunt solight.Moreediediediediediedble.
The click delay
As YOU probably know,there is a delay between the actual tap and the firing of the click event.I'm not going into details on why happens,you can read about it in the sledesmantioned before.
A common technique to prevent the delay is to bind the handler to both  touchend and  click events.The challenge here is to prevent the so caled「phantom click」、meaning that if your handled was caled on touch end、don’t execute it again when the click is emulated bythe brever.soure.soure.soure.sour provent  e.preventDefault() ether on  touchstart or touchend which will prevent the firing of emulated mouse events.
However I find this problematic because:
Calling it on  touchstart will kill native page scroll Calling it on  touchend will kill momenum scroll on some devices It does not prevent at AL emulated events on Android 4.x stock browses The solution we are using in Mobiscroll does the follwing:
Attach  touchstart、  touchend and  click events Remember start and end coordination Call the handler  e.preventDefault() オン  touchend,but only if movement was less then 20 px in any direct(so the user did not have the intent to scroll)Set a flagt to prevent executing the handler again in the emulated click event Start a timeout which will clear the flagh in case when the click event was not emulated Putting everrything together:
var startX,
    startY,
    tap;
 
function getCoord(e, c) {
    return /touch/.test(e.type) ? (e.originalEvent || e).changedTouches[0]['page' + c] : e['page' + c];
}
 
function setTap() {
    tap = true;
    setTimeout(function () {
        tap = false;
    }, 500);
}
 
element.on('touchstart', function (ev) {
    startX = getCoord(ev, 'X');
    startY = getCoord(ev, 'Y');
}).on('touchend', function (ev) {
    // If movement is less than 20px, execute the handler
    if (Math.abs(getCoord(ev, 'X') - startX) < 20 && Math.abs(getCoord(ev, 'Y') - startY) < 20) {
        // Prevent emulated mouse events
        ev.preventDefault();
        handler.call(this, ev);
    }
    setTap();
}).on('click', function (ev) {
    if (!tap) {
        // If handler was not called on touchend, call it on click;
        handler.call(this, ev);
    }
    ev.preventDefault();
});
Working with gestures
When we started building the Listview,we imingied a widget with heavy gesture support.And this is where it shines.
We need support of follwing:
Use native vertical scrolling Handle left and right swipe gestures,disable page scroll during swipe gestures Handle tap Handle tap and hold Native scroll
If we want to keep using native touch scrolling,this involves that we cannot call e.preventDefault() on any of the touch events unconditionaly.
Horizontal swipe
ウェneed to listen to the  touchmove event and decide on the fly whether it will be a vertical or horizontal swipe.If it’s horizontal swipe,kill the page scroll with call e.preventDefault().The following with ress holds ared:
If the horizontal movement is more than 7 px,we consider it a swipe If the vertical movement is more than 10 px,we consider it a scroll Tap
We need to decide if the user is tapping on a list item or intens to swipe or scroll.If movement was less than 5 px iny direction tap is being hold.We also to to take care of the duplicate the ventine the the ventings,so
Tap&hold
On  touchstart we will start a timer which activates the「sort/reorder」mode after a delay.This timer is being reet in case of a scroll,swipe,or  touchend.
Let’s take a look at our event handles.Also note that we atch the touch events and the mousedown to the element itself,while the  mousemove and  mouseup events aratached to the document element dynamically and removed at the end.That is because the y behave differently:the  touchmove and  touchend will still be fired if the finger leaves the element,while the mousemove and  mouseend event will not fire once the mouse pointer has left the element.
var touch,
    action,
    diffX,
    diffY,
    endX,
    endY,
    scroll,
    sort,
    startX,
    startY,
    swipe,
 
function testTouch(e) {
    if (e.type == 'touchstart') {
        touch = true;
    } else if (touch) {
        touch = false;
        return false;
    }
    return true;
}
 
function onStart(ev) {
    if (testTouch(ev) && !action) {
        action = true;
 
        startX = getCoord(ev, 'X');
        startY = getCoord(ev, 'Y');
        diffX = 0;
        diffY = 0;
 
        sortTimer = setTimeout(function () {
            sort = true;
        }, 200);
 
        if (ev.type == 'mousedown') {
            $(document).on('mousemove', onMove).on('mouseup', onEnd);
        }
    }
}
 
function onMove(ev) {
    if (action) {
        endX = getCoord(ev, 'X');
        endY = getCoord(ev, 'Y');
        diffX = endX - startX;
        diffY = endY - startY;
 
        if (!sort && !swipe && !scroll) {
            if (Math.abs(diffY) > 10) { // It's a scroll
                scroll = true;
                // Android 4.0 will not fire touchend event
                $(this).trigger('touchend');
            } else if (Math.abs(diffX) > 7) { // It's a swipe
                swipe = true;
            }
        }
 
        if (swipe) {
            ev.preventDefault(); // Kill page scroll
            // Handle swipe
            // ...
        }
 
        if (sort) {
            ev.preventDefault(); // Kill page scroll
            // Handle sort
            // ....
        }
 
        if (Math.abs(diffX) > 5 || Math.abs(diffY) > 5) {
            clearTimeout(sortTimer);
        }
    }
}
 
function onEnd(ev) {
    if (action) {
        action = false;
 
        if (swipe) {
            // Handle swipe end
            // ...
        } else if (sort) {
            // Handle sort end
            // ...
        } else if (!scroll && Math.abs(diffX) < 5 && Math.abs(diffY) < 5) { // Tap
            if (ev.type === 'touchend') { // Prevent phantom clicks
                ev.preventDefault();
            }
            // Handle tap
            // ...
        }
 
        swipe = false;
        sort = false;
        scroll = false;
 
        clearTimeout(sortTimer);
 
        if (ev.type == 'mouseup') {
            $(document).off('mousemove', onMove).off('mouseup', onEnd);
        }
    }
}
 
element
    .on('touchstart mousedown', onStart)
    .on('touchmove', onMove)
    .on('touchend touchcancel', onEnd)
Problems that are still unsolived
Android ICS
On Android ICS if no  preventDefault is caled on  touchstart or the first  touchmove、フテルーtouchmove events and the  touchend will not be fired.As a workound we ned to decide in the first  touchmove if this is a scroll(so we don't call)  preventDefault)and then manaualy trigger  touchend – see the code above.
Windows Phone
In WP 8 there isのway to prevent native scroll on the fly.To be able to listen to touch events,you need to set the following css property:touch-action: none;However this will kill default behavior、like native page scroll.Fortunately this can be fine tuned、so for the Listview we the follwing:touch-action: pan-y;Which tells to broowser that vertical swipe will be handed by the browser、while our code will Tale care of the horizontal swipe.Ufortutututututututututuney sorting won't t be woking、becausitwill will not preveveveventatttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttch has the  touch-action: none; rule apple.So when the user“picks”up an item from the sort handle,we know he intens to reorder.
Firefox Mobile
In Firefox Mobile the native scroll can be killed only if preventDefault()is caled on the touchstart event.Unfortunaryat  touchstart we don’t really know if we want scroll or not.This has two consequences:
sort will work with the aboove mentioned sort handle only(by calling)  preventDefault() on  touchstart if dragged by the handle)while swiping an item left or right vertical scroll will still work The ises can easure disappear in up comming browser udates or releass、until then we need to come up with worlounds.
What are your hacks and workounds for dealing with complex gestures?Let us know in the comment section below.
Tags: gestures Javascript、 listview touch events