prodio
Version:
Simplified project management
213 lines (182 loc) • 8.03 kB
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.
*
* @class Ink.UI.Swipe_1
* @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.
*
* -----
*
* Arguments received by the callbacks
* -----------------------------------
*
* `onStart`, `onMove`, and `onEnd` 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 (storeGesture only) (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)
*
* @sample Ink_UI_Swipe_1.html
*/
function Swipe(el, options) {
el = Common.elOrSelector(el, 'Swipe target');
this._options = Ink.extendObj({
onEnd: undefined,
onStart: undefined,
onMove: undefined,
minDist: undefined, // in pixels
maxDist: undefined,
minDuration: undefined, // in seconds
maxDuration: undefined,
axis: undefined, // x | y
storeGesture: false,
stopEvents: true
}, InkElement.data(el), options || {});
if (typeof options === 'function') {
this._options.onEnd = options;
}
this._handlers = {
down: Ink.bindEvent(this._onDown, this),
move: Ink.bindEvent(this._onMove, this),
up: Ink.bindEvent(this._onUp, this)
};
this._element = el;
this._init();
}
Swipe.prototype = {
version: '0.1',
_supported: ('ontouchstart' in document.documentElement),
_init: function() {
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;
Common.registerInstance(this, this._element);
},
_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
});
}
}
};
return Swipe;
});