/*
* Contains the Tap class.
*/
'use strict';
const { Gesture, Point2D } = require('../core');
/**
* Data returned when a Tap is recognized.
*
* @typedef {Object} TapData
* @mixes ReturnTypes.BaseData
*
* @property {number} x - x coordinate of tap point.
* @property {number} y - y coordinate of tap point.
*
* @memberof ReturnTypes
*/
/**
* A Tap is defined as a touchstart to touchend event in quick succession.
*
* @extends westures-core.Gesture
* @see {ReturnTypes.TapData}
* @memberof westures
*
* @param {Element} element - The element to which to associate the gesture.
* @param {Function} handler - The function handler to execute when a gesture
* is recognized on the associated element.
* @param {object} [options] - Gesture customization options.
* @param {westures-core.STATE_KEYS[]} [options.enableKeys=[]] - List of keys
* which will enable the gesture. The gesture will not be recognized unless one
* of these keys is pressed while the interaction occurs. If not specified or an
* empty list, the gesture is treated as though the enable key is always down.
* @param {westures-core.STATE_KEYS[]} [options.disableKeys=[]] - List of keys
* which will disable the gesture. The gesture will not be recognized if one of
* these keys is pressed while the interaction occurs. If not specified or an
* empty list, the gesture is treated as though the disable key is never down.
* @param {number} [options.minInputs=1] - The minimum number of pointers that
* must be active for the gesture to be recognized. Uses >=.
* @param {number} [options.maxInputs=Number.MAX_VALUE] - The maximum number of
* pointers that may be active for the gesture to be recognized. Uses <=.
* @param {number} [options.minDelay=0] - The minimum delay between a touchstart
* and touchend can be configured in milliseconds.
* @param {number} [options.maxDelay=300] - The maximum delay between a
* touchstart and touchend can be configured in milliseconds.
* @param {number} [options.maxRetain=300] - The maximum time after a tap ends
* before it is discarded can be configured in milliseconds. Useful for
* multi-tap gestures, to allow things like slow "double clicks".
* @param {number} [options.numTaps=1] - Number of taps to require.
* @param {number} [options.tolerance=10] - The tolerance in pixels an input can
* move before it will no longer be considered part of a tap.
*/
class Tap extends Gesture {
constructor(element, handler, options = {}) {
super('tap', element, handler, { ...Tap.DEFAULTS, ...options });
/**
* An array of inputs that have ended recently.
*
* @type {Input[]}
*/
this.taps = [];
}
end(state) {
const now = Date.now();
const { minDelay, maxDelay, maxRetain, numTaps, tolerance } = this.options;
// Save the recently ended inputs as taps.
this.taps = this.taps.concat(state.getInputsInPhase('end'))
.filter(input => {
const elapsed = input.elapsedTime;
const tdiff = now - input.current.time;
return (
elapsed <= maxDelay
&& elapsed >= minDelay
&& tdiff <= maxRetain
);
});
// Validate the list of taps.
if (this.taps.length !== numTaps ||
this.taps.some(i => i.totalDistance() > tolerance)) {
return null;
}
const centroid = Point2D.centroid(this.taps.map(i => i.current.point));
this.taps = []; // Critical! Used taps need to be cleared!
return { centroid, ...centroid };
}
}
Tap.DEFAULTS = Object.freeze({
minDelay: 0,
maxDelay: 300,
maxRetain: 300,
numTaps: 1,
tolerance: 10,
});
module.exports = Tap;