migi
Version:
A JavaScript MVVM on JSX
168 lines (149 loc) • 3.86 kB
JavaScript
/**
* Thanks to zepto-touch.js
* https://github.com/madrobby/zepto/blob/master/src/touch.js
*/
var touchList = [];
var touch = {};
var lastTouch;
var tapTimeout;
var swipeTimeout;
var longTapDelay = 750;
var lastTime = 0;
var now;
var delta;
var deltaX = 0;
var deltaY = 0;
var firstTouch;
function swipeDirection(x1, x2, y1, y2) {
return Math.abs(x1 - x2) >= Math.abs(y1 - y2)
? (x1 - x2 > 0 ? 'left' : 'right')
: (y1 - y2 > 0 ? 'up' : 'down');
}
function cancelAll() {
if(tapTimeout){
clearTimeout(tapTimeout);
}
if(swipeTimeout) {
clearTimeout(swipeTimeout);
}
tapTimeout = swipeTimeout = null;
touch = {};
touchList = [];
}
var hasInitGlobal;
function initGlobal() {
document.addEventListener('touchmove', onTouchMove, true);
document.addEventListener('touchend', onTouchEnd, true);
document.addEventListener('touchcancel', cancelAll, true);
window.addEventListener('scroll', cancelAll);
window.addEventListener('blur', cancelAll);
}
function onTouchMove(e) {
if(!touch.vd) {
return;
}
firstTouch = e.touches[0];
touch.x2 = firstTouch.pageX;
touch.y2 = firstTouch.pageY;
deltaX += Math.abs(touch.x1 - touch.x2);
deltaY += Math.abs(touch.y1 - touch.y2);
}
function onTouchEnd(e) {
if(!touch.vd) {
return;
}
// swipe
if((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30)
|| (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30)) {
swipeTimeout = setTimeout(function() {
var type = 'swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2));
if(touch.name == 'swipe' || touch.name == type) {
touch.cb(e);
}
touchList.forEach(function(touch) {
if(touch.name == 'swipe' || touch.name == type) {
touch.cb(e);
}
});
touch = {};
touchList = [];
}, 0);
}
// don't fire tap when delta position changed by more than 30 pixels,
// for instance when moving to a point and back to origin
else if(deltaX < 30 && deltaY < 30) {
tapTimeout = setTimeout(function() {
var isLongTap = (Date.now() - lastTime) > longTapDelay;
if(isLongTap) {
if(touch.name == 'longtap') {
touch.cb(e);
}
touchList.forEach(function(touch) {
if(touch.name == 'longtap') {
touch.cb(e);
}
});
}
// trigger double tap immediately
else if(touch.isDoubleTap && touch.vd == lastTouch.vd) {
if(touch.name == 'doubletap') {
touch.cb(e);
}
touchList.forEach(function(touch) {
if(touch.name == 'doubletap') {
touch.cb(e);
}
});
}
touch = {};
touchList = [];
}, 0);
}
else {
touch = {};
touchList = [];
}
deltaX = deltaY = 0;
}
export default function(vd, name, cb, listener) {
if(!hasInitGlobal) {
hasInitGlobal = true;
initGlobal();
}
listener.push(['touchstart', onTouchStart]);
var elem = vd.element;
elem.addEventListener('touchstart', onTouchStart);
function onTouchStart(e) {
// 有可能组件内父子多个使用了手势,冒泡触发了多个
if(touch.first) {
touchList.push({
vd,
name,
cb
});
return;
}
firstTouch = e.touches[0];
if(e.touches && e.touches.length === 1 && touch.x2) {
// Clear out touch movement data if we have it sticking around
// This can occur if touchcancel doesn't fire due to preventDefault, etc.
touch.x2 = undefined;
touch.y2 = undefined;
}
touch = {
vd,
name,
cb,
first: true,
x1: firstTouch.pageX,
y1: firstTouch.pageY
};
lastTouch = touch;
now = Date.now();
delta = now - lastTime;
lastTime = now;
if(delta > 0 && delta < 250) {
touch.isDoubleTap = true;
}
}
};