|
|
/**
|
|
|
* mui gestures
|
|
|
* @param {type} $
|
|
|
* @param {type} window
|
|
|
* @returns {undefined}
|
|
|
*/
|
|
|
(function($, window) {
|
|
|
$.gestures = {
|
|
|
session: {}
|
|
|
};
|
|
|
/**
|
|
|
* Gesture preventDefault
|
|
|
* @param {type} e
|
|
|
* @returns {undefined}
|
|
|
*/
|
|
|
$.preventDefault = function(e) {
|
|
|
e.preventDefault();
|
|
|
};
|
|
|
/**
|
|
|
* Gesture stopPropagation
|
|
|
* @param {type} e
|
|
|
* @returns {undefined}
|
|
|
*/
|
|
|
$.stopPropagation = function(e) {
|
|
|
e.stopPropagation();
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* register gesture
|
|
|
* @param {type} gesture
|
|
|
* @returns {$.gestures}
|
|
|
*/
|
|
|
$.addGesture = function(gesture) {
|
|
|
return $.addAction('gestures', gesture);
|
|
|
|
|
|
};
|
|
|
|
|
|
var round = Math.round;
|
|
|
var abs = Math.abs;
|
|
|
var sqrt = Math.sqrt;
|
|
|
var atan = Math.atan;
|
|
|
var atan2 = Math.atan2;
|
|
|
/**
|
|
|
* distance
|
|
|
* @param {type} p1
|
|
|
* @param {type} p2
|
|
|
* @returns {Number}
|
|
|
*/
|
|
|
var getDistance = function(p1, p2, props) {
|
|
|
if (!props) {
|
|
|
props = ['x', 'y'];
|
|
|
}
|
|
|
var x = p2[props[0]] - p1[props[0]];
|
|
|
var y = p2[props[1]] - p1[props[1]];
|
|
|
return sqrt((x * x) + (y * y));
|
|
|
};
|
|
|
/**
|
|
|
* scale
|
|
|
* @param {Object} starts
|
|
|
* @param {Object} moves
|
|
|
*/
|
|
|
var getScale = function(starts, moves) {
|
|
|
if (starts.length >= 2 && moves.length >= 2) {
|
|
|
var props = ['pageX', 'pageY'];
|
|
|
return getDistance(moves[1], moves[0], props) / getDistance(starts[1], starts[0], props);
|
|
|
}
|
|
|
return 1;
|
|
|
};
|
|
|
/**
|
|
|
* angle
|
|
|
* @param {type} p1
|
|
|
* @param {type} p2
|
|
|
* @returns {Number}
|
|
|
*/
|
|
|
var getAngle = function(p1, p2, props) {
|
|
|
if (!props) {
|
|
|
props = ['x', 'y'];
|
|
|
}
|
|
|
var x = p2[props[0]] - p1[props[0]];
|
|
|
var y = p2[props[1]] - p1[props[1]];
|
|
|
return atan2(y, x) * 180 / Math.PI;
|
|
|
};
|
|
|
/**
|
|
|
* direction
|
|
|
* @param {Object} x
|
|
|
* @param {Object} y
|
|
|
*/
|
|
|
var getDirection = function(x, y) {
|
|
|
if (x === y) {
|
|
|
return '';
|
|
|
}
|
|
|
if (abs(x) >= abs(y)) {
|
|
|
return x > 0 ? 'left' : 'right';
|
|
|
}
|
|
|
return y > 0 ? 'up' : 'down';
|
|
|
};
|
|
|
/**
|
|
|
* rotation
|
|
|
* @param {Object} start
|
|
|
* @param {Object} end
|
|
|
*/
|
|
|
var getRotation = function(start, end) {
|
|
|
var props = ['pageX', 'pageY'];
|
|
|
return getAngle(end[1], end[0], props) - getAngle(start[1], start[0], props);
|
|
|
};
|
|
|
/**
|
|
|
* px per ms
|
|
|
* @param {Object} deltaTime
|
|
|
* @param {Object} x
|
|
|
* @param {Object} y
|
|
|
*/
|
|
|
var getVelocity = function(deltaTime, x, y) {
|
|
|
return {
|
|
|
x: x / deltaTime || 0,
|
|
|
y: y / deltaTime || 0
|
|
|
};
|
|
|
};
|
|
|
/**
|
|
|
* detect gestures
|
|
|
* @param {type} event
|
|
|
* @param {type} touch
|
|
|
* @returns {undefined}
|
|
|
*/
|
|
|
var detect = function(event, touch) {
|
|
|
if ($.gestures.stoped) {
|
|
|
return;
|
|
|
}
|
|
|
$.doAction('gestures', function(index, gesture) {
|
|
|
if (!$.gestures.stoped) {
|
|
|
if ($.options.gestureConfig[gesture.name] !== false) {
|
|
|
gesture.handle(event, touch);
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
/**
|
|
|
* 暂时无用
|
|
|
* @param {Object} node
|
|
|
* @param {Object} parent
|
|
|
*/
|
|
|
var hasParent = function(node, parent) {
|
|
|
while (node) {
|
|
|
if (node == parent) {
|
|
|
return true;
|
|
|
}
|
|
|
node = node.parentNode;
|
|
|
}
|
|
|
return false;
|
|
|
};
|
|
|
|
|
|
var uniqueArray = function(src, key, sort) {
|
|
|
var results = [];
|
|
|
var values = [];
|
|
|
var i = 0;
|
|
|
|
|
|
while (i < src.length) {
|
|
|
var val = key ? src[i][key] : src[i];
|
|
|
if (values.indexOf(val) < 0) {
|
|
|
results.push(src[i]);
|
|
|
}
|
|
|
values[i] = val;
|
|
|
i++;
|
|
|
}
|
|
|
|
|
|
if (sort) {
|
|
|
if (!key) {
|
|
|
results = results.sort();
|
|
|
} else {
|
|
|
results = results.sort(function sortUniqueArray(a, b) {
|
|
|
return a[key] > b[key];
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return results;
|
|
|
};
|
|
|
var getMultiCenter = function(touches) {
|
|
|
var touchesLength = touches.length;
|
|
|
if (touchesLength === 1) {
|
|
|
return {
|
|
|
x: round(touches[0].pageX),
|
|
|
y: round(touches[0].pageY)
|
|
|
};
|
|
|
}
|
|
|
|
|
|
var x = 0;
|
|
|
var y = 0;
|
|
|
var i = 0;
|
|
|
while (i < touchesLength) {
|
|
|
x += touches[i].pageX;
|
|
|
y += touches[i].pageY;
|
|
|
i++;
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
x: round(x / touchesLength),
|
|
|
y: round(y / touchesLength)
|
|
|
};
|
|
|
};
|
|
|
var multiTouch = function() {
|
|
|
return $.options.gestureConfig.pinch;
|
|
|
};
|
|
|
var copySimpleTouchData = function(touch) {
|
|
|
var touches = [];
|
|
|
var i = 0;
|
|
|
while (i < touch.touches.length) {
|
|
|
touches[i] = {
|
|
|
pageX: round(touch.touches[i].pageX),
|
|
|
pageY: round(touch.touches[i].pageY)
|
|
|
};
|
|
|
i++;
|
|
|
}
|
|
|
return {
|
|
|
timestamp: $.now(),
|
|
|
gesture: touch.gesture,
|
|
|
touches: touches,
|
|
|
center: getMultiCenter(touch.touches),
|
|
|
deltaX: touch.deltaX,
|
|
|
deltaY: touch.deltaY
|
|
|
};
|
|
|
};
|
|
|
|
|
|
var calDelta = function(touch) {
|
|
|
var session = $.gestures.session;
|
|
|
var center = touch.center;
|
|
|
var offset = session.offsetDelta || {};
|
|
|
var prevDelta = session.prevDelta || {};
|
|
|
var prevTouch = session.prevTouch || {};
|
|
|
|
|
|
if (touch.gesture.type === $.EVENT_START || touch.gesture.type === $.EVENT_END) {
|
|
|
prevDelta = session.prevDelta = {
|
|
|
x: prevTouch.deltaX || 0,
|
|
|
y: prevTouch.deltaY || 0
|
|
|
};
|
|
|
|
|
|
offset = session.offsetDelta = {
|
|
|
x: center.x,
|
|
|
y: center.y
|
|
|
};
|
|
|
}
|
|
|
touch.deltaX = prevDelta.x + (center.x - offset.x);
|
|
|
touch.deltaY = prevDelta.y + (center.y - offset.y);
|
|
|
};
|
|
|
var calTouchData = function(touch) {
|
|
|
var session = $.gestures.session;
|
|
|
var touches = touch.touches;
|
|
|
var touchesLength = touches.length;
|
|
|
|
|
|
if (!session.firstTouch) {
|
|
|
session.firstTouch = copySimpleTouchData(touch);
|
|
|
}
|
|
|
|
|
|
if (multiTouch() && touchesLength > 1 && !session.firstMultiTouch) {
|
|
|
session.firstMultiTouch = copySimpleTouchData(touch);
|
|
|
} else if (touchesLength === 1) {
|
|
|
session.firstMultiTouch = false;
|
|
|
}
|
|
|
|
|
|
var firstTouch = session.firstTouch;
|
|
|
var firstMultiTouch = session.firstMultiTouch;
|
|
|
var offsetCenter = firstMultiTouch ? firstMultiTouch.center : firstTouch.center;
|
|
|
|
|
|
var center = touch.center = getMultiCenter(touches);
|
|
|
touch.timestamp = $.now();
|
|
|
touch.deltaTime = touch.timestamp - firstTouch.timestamp;
|
|
|
|
|
|
touch.angle = getAngle(offsetCenter, center);
|
|
|
touch.distance = getDistance(offsetCenter, center);
|
|
|
|
|
|
calDelta(touch);
|
|
|
|
|
|
touch.offsetDirection = getDirection(touch.deltaX, touch.deltaY);
|
|
|
|
|
|
touch.scale = firstMultiTouch ? getScale(firstMultiTouch.touches, touches) : 1;
|
|
|
touch.rotation = firstMultiTouch ? getRotation(firstMultiTouch.touches, touches) : 0;
|
|
|
|
|
|
calIntervalTouchData(touch);
|
|
|
|
|
|
};
|
|
|
var CAL_INTERVAL = 25;
|
|
|
var calIntervalTouchData = function(touch) {
|
|
|
var session = $.gestures.session;
|
|
|
var last = session.lastInterval || touch;
|
|
|
var deltaTime = touch.timestamp - last.timestamp;
|
|
|
var velocity;
|
|
|
var velocityX;
|
|
|
var velocityY;
|
|
|
var direction;
|
|
|
|
|
|
if (touch.gesture.type != $.EVENT_CANCEL && (deltaTime > CAL_INTERVAL || last.velocity === undefined)) {
|
|
|
var deltaX = last.deltaX - touch.deltaX;
|
|
|
var deltaY = last.deltaY - touch.deltaY;
|
|
|
|
|
|
var v = getVelocity(deltaTime, deltaX, deltaY);
|
|
|
velocityX = v.x;
|
|
|
velocityY = v.y;
|
|
|
velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
|
|
|
direction = getDirection(deltaX, deltaY) || last.direction;
|
|
|
|
|
|
session.lastInterval = touch;
|
|
|
} else {
|
|
|
velocity = last.velocity;
|
|
|
velocityX = last.velocityX;
|
|
|
velocityY = last.velocityY;
|
|
|
direction = last.direction;
|
|
|
}
|
|
|
|
|
|
touch.velocity = velocity;
|
|
|
touch.velocityX = velocityX;
|
|
|
touch.velocityY = velocityY;
|
|
|
touch.direction = direction;
|
|
|
};
|
|
|
var targetIds = {};
|
|
|
var convertTouches = function(touches) {
|
|
|
for (var i = 0; i < touches.length; i++) {
|
|
|
!touches['identifier'] && (touches['identifier'] = 0);
|
|
|
}
|
|
|
return touches;
|
|
|
};
|
|
|
var getTouches = function(event, touch) {
|
|
|
var allTouches = convertTouches($.slice.call(event.touches || [event]));
|
|
|
|
|
|
var type = event.type;
|
|
|
|
|
|
var targetTouches = [];
|
|
|
var changedTargetTouches = [];
|
|
|
|
|
|
//当touchstart或touchmove且touches长度为1,直接获得all和changed
|
|
|
if ((type === $.EVENT_START || type === $.EVENT_MOVE) && allTouches.length === 1) {
|
|
|
targetIds[allTouches[0].identifier] = true;
|
|
|
targetTouches = allTouches;
|
|
|
changedTargetTouches = allTouches;
|
|
|
touch.target = event.target;
|
|
|
} else {
|
|
|
var i = 0;
|
|
|
var targetTouches = [];
|
|
|
var changedTargetTouches = [];
|
|
|
var changedTouches = convertTouches($.slice.call(event.changedTouches || [event]));
|
|
|
|
|
|
touch.target = event.target;
|
|
|
var sessionTarget = $.gestures.session.target || event.target;
|
|
|
targetTouches = allTouches.filter(function(touch) {
|
|
|
return hasParent(touch.target, sessionTarget);
|
|
|
});
|
|
|
|
|
|
if (type === $.EVENT_START) {
|
|
|
i = 0;
|
|
|
while (i < targetTouches.length) {
|
|
|
targetIds[targetTouches[i].identifier] = true;
|
|
|
i++;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
i = 0;
|
|
|
while (i < changedTouches.length) {
|
|
|
if (targetIds[changedTouches[i].identifier]) {
|
|
|
changedTargetTouches.push(changedTouches[i]);
|
|
|
}
|
|
|
if (type === $.EVENT_END || type === $.EVENT_CANCEL) {
|
|
|
delete targetIds[changedTouches[i].identifier];
|
|
|
}
|
|
|
i++;
|
|
|
}
|
|
|
|
|
|
if (!changedTargetTouches.length) {
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
targetTouches = uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true);
|
|
|
var touchesLength = targetTouches.length;
|
|
|
var changedTouchesLength = changedTargetTouches.length;
|
|
|
if (type === $.EVENT_START && touchesLength - changedTouchesLength === 0) { //first
|
|
|
touch.isFirst = true;
|
|
|
$.gestures.touch = $.gestures.session = {
|
|
|
target: event.target
|
|
|
};
|
|
|
}
|
|
|
touch.isFinal = ((type === $.EVENT_END || type === $.EVENT_CANCEL) && (touchesLength - changedTouchesLength === 0));
|
|
|
|
|
|
touch.touches = targetTouches;
|
|
|
touch.changedTouches = changedTargetTouches;
|
|
|
return true;
|
|
|
|
|
|
};
|
|
|
var handleTouchEvent = function(event) {
|
|
|
var touch = {
|
|
|
gesture: event
|
|
|
};
|
|
|
var touches = getTouches(event, touch);
|
|
|
if (!touches) {
|
|
|
return;
|
|
|
}
|
|
|
calTouchData(touch);
|
|
|
detect(event, touch);
|
|
|
$.gestures.session.prevTouch = touch;
|
|
|
if (event.type === $.EVENT_END && !$.isTouchable) {
|
|
|
$.gestures.touch = $.gestures.session = {};
|
|
|
}
|
|
|
};
|
|
|
window.addEventListener($.EVENT_START, handleTouchEvent);
|
|
|
window.addEventListener($.EVENT_MOVE, handleTouchEvent);
|
|
|
window.addEventListener($.EVENT_END, handleTouchEvent);
|
|
|
window.addEventListener($.EVENT_CANCEL, handleTouchEvent);
|
|
|
//fixed hashchange(android)
|
|
|
window.addEventListener($.EVENT_CLICK, function(e) {
|
|
|
//TODO 应该判断当前target是不是在targets.popover内部,而不是非要相等
|
|
|
if (($.os.android || $.os.ios) && (($.targets.popover && e.target === $.targets.popover) || ($.targets.tab) || $.targets.offcanvas || $.targets.modal)) {
|
|
|
e.preventDefault();
|
|
|
}
|
|
|
}, true);
|
|
|
|
|
|
|
|
|
//增加原生滚动识别
|
|
|
$.isScrolling = false;
|
|
|
var scrollingTimeout = null;
|
|
|
window.addEventListener('scroll', function() {
|
|
|
$.isScrolling = true;
|
|
|
scrollingTimeout && clearTimeout(scrollingTimeout);
|
|
|
scrollingTimeout = setTimeout(function() {
|
|
|
$.isScrolling = false;
|
|
|
}, 250);
|
|
|
});
|
|
|
})(mui, window); |