You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

423 lines
10 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 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);