209 lines
7.9 KiB
JavaScript
209 lines
7.9 KiB
JavaScript
|
/**
|
||
|
* Swipe gestures
|
||
|
* @module Ink.UI.Swipe_1
|
||
|
* @version 1
|
||
|
*/
|
||
|
Ink.createModule('Ink.UI.Swipe', '1', ['Ink.Dom.Event_1', 'Ink.Dom.Element_1', 'Ink.UI.Common_1'], function(InkEvent, InkElement, Common) {
|
||
|
'use strict';
|
||
|
|
||
|
/**
|
||
|
* Subscribe swipe gestures.
|
||
|
*
|
||
|
* Supports filtering swipes be any combination of the criteria supported in the options.
|
||
|
*
|
||
|
* -----
|
||
|
*
|
||
|
* Arguments received by the callbacks
|
||
|
* -----------------------------------
|
||
|
*
|
||
|
* The `onStart`, `onMove`, and `onEnd` options receive as argument an object containing:
|
||
|
*
|
||
|
* - `event`: the DOMEvent object
|
||
|
* - `element`: the target element
|
||
|
* - `Instance`: the `Ink.UI.Swipe_1` instance
|
||
|
* - `position`: `Array` with `[x, y]` coordinates of current position
|
||
|
* - `dt`: Time passed between now and the first event (onMove only)
|
||
|
* - `gesture`: an Array containing [x,y] coordinates of every touchmove event received (only if options.storeGesture is enabled) (onEnd only)
|
||
|
* - `time`: an Array containing all the `dt` values for every touchmove event (onEnd only)
|
||
|
* - `overallMovement`: X and Y distance traveled by the touch movement (`[x, y]`) (onEnd only)
|
||
|
* - `overallTime`: total time passed (onEnd only)
|
||
|
*
|
||
|
* @class Ink.UI.Swipe
|
||
|
* @constructor
|
||
|
* @param {String|DOMElement} el Element or Selector
|
||
|
* @param {Object} options Options Object
|
||
|
* @param {Function} [options.onEnd] Callback function for the `touchend` event. Gets all the gesture information, and is filtered by min/max Dist and Duration options (see below)
|
||
|
* @param {Function} [options.onStart] Callback function for `touchstart` event.
|
||
|
* @param {Function} [options.onMove] Callback function for every `touchmove` event. Gets current gesture information.
|
||
|
* @param {Number} [options.minDist] Minimum allowed distance, in pixels.
|
||
|
* @param {Number} [options.maxDist] Maximum allowed distance, in pixels.
|
||
|
* @param {Number} [options.minDuration] Minimum allowed duration, in seconds.
|
||
|
* @param {Number} [options.maxDuration] Maximum allowed duration, in seconds.
|
||
|
* @param {String} [options.axis] If either 'x' or 'y' is passed, only swipes where the dominant axis is the given one trigger the callback
|
||
|
* @param {String} [options.storeGesture] If to store gesture information and provide it to the callback. Defaults to true.
|
||
|
* @param {String} [options.stopEvents] Flag to stop (default and propagation) of the received events. Defaults to true.
|
||
|
*
|
||
|
*
|
||
|
* @sample Ink_UI_Swipe_1.html
|
||
|
*/
|
||
|
function Swipe() {
|
||
|
if (typeof arguments[1] === 'function') {
|
||
|
arguments[1] = { onEnd: arguments[1] };
|
||
|
}
|
||
|
|
||
|
Common.BaseUIComponent.apply(this, arguments);
|
||
|
}
|
||
|
|
||
|
Swipe._name = 'Swipe_1';
|
||
|
|
||
|
Swipe._optionDefinition = {
|
||
|
onEnd: ['Function', undefined],
|
||
|
onStart: ['Function', undefined],
|
||
|
onMove: ['Function', undefined],
|
||
|
minDist: ['Number', undefined], // in pixels
|
||
|
maxDist: ['Number', undefined],
|
||
|
minDuration: ['Number', undefined], // in seconds
|
||
|
maxDuration: ['Number', undefined],
|
||
|
axis: ['String', undefined], // x | y
|
||
|
storeGesture: ['Boolean', false],
|
||
|
stopEvents: ['Boolean', true]
|
||
|
};
|
||
|
|
||
|
Swipe.prototype = {
|
||
|
_supported: ('ontouchstart' in document.documentElement),
|
||
|
|
||
|
_init: function() {
|
||
|
this._handlers = {
|
||
|
down: Ink.bindEvent(this._onDown, this),
|
||
|
move: Ink.bindEvent(this._onMove, this),
|
||
|
up: Ink.bindEvent(this._onUp, this)
|
||
|
};
|
||
|
|
||
|
var db = document.body;
|
||
|
InkEvent.observe(db, 'touchstart', this._handlers.down);
|
||
|
if (this._options.storeGesture || this._options.onMove) {
|
||
|
InkEvent.observe(db, 'touchmove', this._handlers.move);
|
||
|
}
|
||
|
InkEvent.observe(db, 'touchend', this._handlers.up);
|
||
|
this._isOn = false;
|
||
|
},
|
||
|
|
||
|
_isMeOrParent: function(el, parentEl) {
|
||
|
if (!el) {return;}
|
||
|
do {
|
||
|
if (el === parentEl) { return true; }
|
||
|
el = el.parentNode;
|
||
|
} while (el);
|
||
|
return false;
|
||
|
},
|
||
|
|
||
|
_pushGesture: function (coords, dt) {
|
||
|
if (this._options.storeGesture) {
|
||
|
this._gesture.push(coords);
|
||
|
this._time.push(dt);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onDown: function(event) {
|
||
|
if (event.changedTouches.length !== 1) { return; }
|
||
|
if (!this._isMeOrParent(event.target, this._element)) { return; }
|
||
|
|
||
|
if( this._options.stopEvents === true ){
|
||
|
InkEvent.stop(event);
|
||
|
}
|
||
|
event = event.changedTouches[0];
|
||
|
this._isOn = true;
|
||
|
this._target = event.target;
|
||
|
|
||
|
this._t0 = +new Date();
|
||
|
this._p0 = [event.pageX, event.pageY];
|
||
|
|
||
|
if (this._options.storeGesture) {
|
||
|
this._gesture = [];
|
||
|
this._time = [];
|
||
|
}
|
||
|
|
||
|
this._pushGesture(this._p0, 0);
|
||
|
|
||
|
if (this._options.onStart) {
|
||
|
this._options.onStart({
|
||
|
event: event,
|
||
|
element: this._element,
|
||
|
instance: this,
|
||
|
position: this._p0,
|
||
|
dt: 0
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onMove: function(event) {
|
||
|
if (!this._isOn || event.changedTouches.length !== 1) { return; }
|
||
|
if( this._options.stopEvents === true ) {
|
||
|
InkEvent.stop(event);
|
||
|
}
|
||
|
|
||
|
event = event.changedTouches[0];
|
||
|
var t1 = +new Date();
|
||
|
var dt = (t1 - this._t0);
|
||
|
|
||
|
var gesture = [event.pageX, event.pageY];
|
||
|
|
||
|
this._pushGesture(gesture, dt);
|
||
|
|
||
|
if (this._options.onMove) {
|
||
|
this._options.onMove({
|
||
|
event: event,
|
||
|
element: this._element,
|
||
|
instance: this,
|
||
|
position: gesture,
|
||
|
dt: dt
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onUp: function(event) {
|
||
|
if (!this._isOn || event.changedTouches.length !== 1) { return; }
|
||
|
|
||
|
if( this._options.stopEvents === true ){
|
||
|
InkEvent.stop(event);
|
||
|
}
|
||
|
event = event.changedTouches[0]; // TODO SHOULD CHECK IT IS THE SAME TOUCH
|
||
|
this._isOn = false;
|
||
|
|
||
|
var t1 = +new Date();
|
||
|
var p1 = [event.pageX, event.pageY];
|
||
|
var dt = (t1 - this._t0);
|
||
|
var dr = [
|
||
|
p1[0] - this._p0[0],
|
||
|
p1[1] - this._p0[1]
|
||
|
];
|
||
|
var dist = Math.sqrt(dr[0]*dr[0] + dr[1]*dr[1]);
|
||
|
var axis = Math.abs(dr[0]) > Math.abs(dr[1]) ? 'x' : 'y';
|
||
|
|
||
|
var o = this._options;
|
||
|
if (o.minDist && dist < o.minDist) { return; }
|
||
|
if (o.maxDist && dist > o.maxDist) { return; }
|
||
|
if (o.minDuration && dt < o.minDuration) { return; }
|
||
|
if (o.maxDuration && dt > o.maxDuration) { return; }
|
||
|
if (o.axis && axis !== o.axis) { return; }
|
||
|
|
||
|
if (this._options.onEnd) {
|
||
|
this._options.onEnd({
|
||
|
event: event,
|
||
|
element: this._element,
|
||
|
instance: this,
|
||
|
gesture: this._gesture,
|
||
|
time: this._time,
|
||
|
axis: axis,
|
||
|
overallMovement: dr,
|
||
|
overallTime: dt
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
Common.createUIComponent(Swipe);
|
||
|
|
||
|
return Swipe;
|
||
|
});
|