lisn.js
Version:
Simply handle user gestures and actions. Includes widgets.
222 lines (215 loc) • 9.46 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getTouchGestureFragment = exports.getTouchDiff = void 0;
var MC = _interopRequireWildcard(require("../globals/minification-constants.cjs"));
var MH = _interopRequireWildcard(require("../globals/minification-helpers.cjs"));
var _directions = require("./directions.cjs");
var _math = require("./math.cjs");
var _xMap = require("../modules/x-map.cjs");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
/**
* @module Utils
*/
/**
* @category Gestures
*/
/**
* Returns a {@link GestureFragment} for the given events. Only "touchmove" events
* will be considered.
*
* If there are less than 2 such events in the given list of events, returns `false`.
*
* If the gesture is to be considered terminated, e.g. because there is
* "touchcancel" in the list, returns `null`.
*
* Note that by default swipe actions follow the natural direction: swipe up
* with scroll intent results in direction down and swipe down results in
* direction up. Drag intent always follows the direction of the gesture.
*
* For zoom intents, which necessarily involves exactly two fingers `deltaZ`
* is based on the relative change in distance between the fingers.
*
* @param [options.deltaThreshold]
* A change of x or y coordinate less than this is
* considered insignificant, for the purposes of
* determining:
* 1) whether the inferred direction is in one of the
* four cardinal ones, or otherwise ambiguous; and
* 2) whether more than two fingers have moved and
* therefore whether the direction could be zoom or
* not
* @param [options.angleDiffThreshold] See {@link getVectorDirection}
* @param [options.reverseScroll]
* If set to `true`, will disable natural scroll
* direction.
* @param [options.dragHoldTime]
* If the user presses and holds for at least the
* given amount of milliseconds before moving the
* finger(s), gestures other than pinch will be
* treated as a drag instead of scroll as long as the
* number of fingers touching the screen is
* `options.dragNumFingers`. Default is 500ms.
* @param [options.dragNumFingers]
* The number of fingers that could be considered a
* drag intent. Default is 1.
*
* @returns `false` if there are less than 2 "touchmove" events in the list,
* `null` if the gesture is terminated, otherwise a {@link GestureFragment}.
*
* @category Gestures
*/
const getTouchGestureFragment = (events, options) => {
var _options$dragHoldTime, _options$dragNumFinge;
if (!MH.isIterableObject(events)) {
events = [events];
}
let moves = getTouchDiff(events, options === null || options === void 0 ? void 0 : options.deltaThreshold);
if (!moves) {
return null; // terminated
}
let numMoves = MH.lengthOf(moves);
const holdTime = getHoldTime(events);
const canBeDrag = holdTime >= ((_options$dragHoldTime = options === null || options === void 0 ? void 0 : options.dragHoldTime) !== null && _options$dragHoldTime !== void 0 ? _options$dragHoldTime : 500) && numMoves === ((_options$dragNumFinge = options === null || options === void 0 ? void 0 : options.dragNumFingers) !== null && _options$dragNumFinge !== void 0 ? _options$dragNumFinge : 1);
const angleDiffThreshold = options === null || options === void 0 ? void 0 : options.angleDiffThreshold;
let deltaX = (0, _math.havingMaxAbs)(...moves.map(m => m.deltaX));
let deltaY = (0, _math.havingMaxAbs)(...moves.map(m => m.deltaY));
let deltaZ = 1;
if (numMoves > 2) {
// Take only the significant ones
moves = MH.filter(moves, d => d.isSignificant);
numMoves = MH.lengthOf(moves);
}
let direction = MC.S_NONE;
let intent = MC.S_UNKNOWN;
if (numMoves === 2) {
// Check if it's a zoom
const vectorA = [moves[0].deltaX, moves[0].deltaY];
const vectorB = [moves[1].deltaX, moves[1].deltaY];
// If either finger is approx stationary, or if they move in opposite directions,
// treat it as zoom.
if (!(0, _math.havingMaxAbs)(...vectorA) ||
// finger A still
!(0, _math.havingMaxAbs)(...vectorB) ||
// finger B still
(0, _math.areAntiParallel)(vectorA, vectorB, angleDiffThreshold)) {
// It's a pinch motion => zoom
const startDistance = (0, _math.distanceBetween)([moves[0].startX, moves[0].startY], [moves[1].startX, moves[1].startY]);
const endDistance = (0, _math.distanceBetween)([moves[0].endX, moves[0].endY], [moves[1].endX, moves[1].endY]);
direction = startDistance < endDistance ? MC.S_IN : MC.S_OUT;
deltaZ = endDistance / startDistance;
deltaX = deltaY = 0;
intent = MC.S_ZOOM;
}
}
const deltaSign = canBeDrag || options !== null && options !== void 0 && options.reverseScroll ? 1 : -1;
// If scrolling, swap the deltas for natural scroll direction.
// Add +0 to force -0 to be +0 since jest doesn't think they're equal
deltaX = deltaSign * deltaX + 0;
deltaY = deltaSign * deltaY + 0;
if (direction === MC.S_NONE) {
// Wasn't a zoom. Check if all moves are aligned.
let isFirst = true;
for (const m of moves) {
// There's at least one significant move, assume scroll or drag intent.
intent = canBeDrag ? MC.S_DRAG : MC.S_SCROLL;
const thisDirection = (0, _directions.getVectorDirection)([deltaSign * m.deltaX, deltaSign * m.deltaY], angleDiffThreshold);
if (thisDirection === MC.S_NONE) {
continue;
}
if (isFirst) {
direction = thisDirection;
} else if (direction !== thisDirection) {
direction = MC.S_AMBIGUOUS;
break;
}
isFirst = false;
}
}
if (direction === MC.S_NONE) {
const lastTouchEvent = MH.lastOf(events.filter(MH.isTouchEvent));
// If all fingers have lifted off, consider it terminated, otherwise wait
// for more events.
return MH.lengthOf(lastTouchEvent === null || lastTouchEvent === void 0 ? void 0 : lastTouchEvent.touches) ? false : null;
}
return {
device: MC.S_TOUCH,
direction,
intent,
deltaX,
deltaY,
deltaZ
};
};
/**
* Returns a description of the changes in each finger between the first and
* the last relevant TouchEvent in the list.
*
* If the gesture is to be considered terminated, e.g. because there is
* "touchcancel" in the list, returns `null`.
*
* Note that, `deltaX`/`deltaY` are the end X/Y coordinate minus the start X/Y
* coordinate. For natural scroll direction you should swap their signs.
*
* @param deltaThreshold If the change of x and y coordinate are both less
* than this, it is marked as not significant.
*
* @category Gestures
*/
exports.getTouchGestureFragment = getTouchGestureFragment;
const getTouchDiff = (events, deltaThreshold = 0) => {
// Group each touch point of each event by identifier, so that we can get the
// start and end coordinate of each finger
const groupedTouches = (0, _xMap.newXMap)(() => []);
for (const event of events) {
if (!MH.isTouchEvent(event)) {
continue;
}
if (event.type === MC.S_TOUCHCANCEL) {
return null; // gesture terminated
}
for (const touch of event.touches) {
groupedTouches.sGet(touch.identifier).push(touch);
}
}
const moves = [];
for (const touchList of groupedTouches.values()) {
const nTouches = MH.lengthOf(touchList);
if (nTouches < 2) {
// Only one event had that finger in it, so there's no move for it
continue;
}
const firstTouch = touchList[0];
const lastTouch = touchList[nTouches - 1];
const startX = firstTouch.clientX;
const startY = firstTouch.clientY;
const endX = lastTouch.clientX;
const endY = lastTouch.clientY;
const deltaX = endX - startX;
const deltaY = endY - startY;
const isSignificant = (0, _math.maxAbs)(deltaX, deltaY) >= deltaThreshold;
// Consider it a move in one of the 4 cardinal ones
moves.push({
startX,
startY,
endX,
endY,
deltaX,
deltaY,
isSignificant
});
}
return moves;
};
// --------------------
exports.getTouchDiff = getTouchDiff;
const getHoldTime = events => {
const firstStart = events.findIndex(e => e.type === MC.S_TOUCHSTART);
const firstMove = events.findIndex(e => e.type === MC.S_TOUCHMOVE);
if (firstStart < 0 || firstMove < 1) {
return 0;
}
return events[firstMove].timeStamp - events[firstStart].timeStamp;
};
//# sourceMappingURL=gesture-touch.cjs.map