mr-tap
Version:
A tap library, abstracts away from mouse/touch providing source agnostic sync access to the user input.
183 lines (163 loc) • 5.45 kB
JavaScript
import Tap from './tap.js';
import TapMouse from './mouse.js';
import TapTouch from './touch.js';
/**
* @class
* @name Taps
* @description Taps provide source agnostic sync access to input. Either it comes from mouse and/or touch, it is the same API. It assumes multiple instances of taps making your code multi-touch by design. Providing sync access instead of event based, for best usage in real-time applications. Class itself is an iterator for easy access to taps.
* @param {object} [element] - Element to which mouse and touch events to be attached, by default `window`.
* @property {number} dragDistance Distance in pixels for tap to move from start position to enter drag state, default: 20 pixels.
* @property {number} dragDelay Time in milliseconds for tap to enter drag state if held for that long, default: 200 milliseconds.
* @example
* // create taps interface
* const taps = new Taps();
*
* function update() {
* requestAnimationFrame(update);
*
* // update taps on each frame
* taps.update();
*
* // access all taps
* for(const tap of taps) {
* // iterate through all taps
* }
* }
* requestAnimationFrame(update);
*/
class Taps {
constructor(element = window) {
this._element = element;
this._dragDistance = 20; // pixels
this._dragDelay = 200; // ms
this._index = new Map();
this._mouse = new TapMouse(this);
this._touch = new TapTouch(this);
window.addEventListener('blur', () => {
this._onBlur();
}, false);
document.addEventListener('visibilitychange', () => {
this._onBlur();
}, false);
}
*_iterator(property) {
for(const [id, tap] of this._index) {
if (tap[property])
yield tap;
}
}
_onBlur() {
for(const [ id, tap ] of this._index) {
tap._release = true;
}
}
/**
* @function
* @name Taps#update
* @description Update taps states, should be executed in the beginning of an application every update loop.
* @example
* function update() {
* requestAnimationFrame(update);
*
* // update taps on each frame
* taps.update();
*
* // access all taps
* }
* requestAnimationFrame(update);
*/
update() {
for(const [ id, tap ] of this._index) {
tap.update();
}
}
[Symbol.iterator]() {
return this._index.values();
}
set dragDistance(value) {
this._dragDistance = value;
}
get dragDistance() {
return this._dragDistance;
}
set dragDelay(value) {
this._dragDelay = value;
}
get dragDelay() {
return this._dragDelay;
}
/**
* @name Taps#start
* @type {Iterable.<Tap>}
* @description Iterator of taps that just've been created. Tap can be only once in `start` state during its life.
* @example
* for(const tap of taps.start) {
* // same as touchstart or mousedown
* }
*/
get start() {
return this._iterator('start');
}
/**
* @name Taps#up
* @type {Iterable.<Tap>}
* @description Iterator of taps that been released. Tap can be only once in `up` state during its life and will be removed after.
* @example
* for(const tap of taps.up) {
* // same as touchend, touchcancel or mouseup
* }
*/
get up() {
return this._iterator('up');
}
/**
* @name Taps#click
* @type {Iterable.<Tap>}
* @description Iterator of taps that are consider clicks. Tap can be only once in `click` state, and will be removed after.
* @example
* for(const tap of taps.click) {
* // same as click, happens on mouseup or touchend
* // if tap been short lived and/or didn't move too far from initial position
* }
*/
get click() {
return this._iterator('click');
}
/**
* @name Taps#dragstart
* @type {Iterable.<Tap>}
* @description Iterator of taps that started a drag. Tap can be only once in `dragstart` state. It is guaranteed to trigger `dragend` instead of `click` as a last state.
* @example
* for(const tap of taps.dragstart) {
* // held touch/mousedown for long enough or moved far enough from initial tap position
* }
*/
get dragstart() {
return this._iterator('dragstart');
}
/**
* @name Taps#drag
* @type {Iterable.<Tap>}
* @description Iterator of taps that are in drag state. This state will persist from and during `dragstart` and until/during `dragend` states.
* @example
* for(const tap of taps.drag) {
* // tap is dragging
* }
*/
get drag() {
return this._iterator('drag');
}
/**
* @name Taps#dragend
* @type {Iterable.<Tap>}
* @description Iterator of taps that ended a drag. Tap can be only once in `dragend` state, and will be removed after.
* @example
* for(const tap of taps.dragend) {
* // drag is finished
* }
*/
get dragend() {
return this._iterator('dragend');
}
}
export { Taps as default, Tap };