This repository has been archived on 2022-03-12. You can view files and clone it, but cannot push or open issues or pull requests.
2021-04-02 02:24:13 +03:00

242 lines
6.7 KiB
JavaScript

import Hammer from '@egjs/hammerjs';
import {
MULTI_FINGER_PAN_MAX_PINCH_THRESHOLD,
MULTI_FINGER_PAN_MAX_ROTATION_THRESHOLD,
} from './constants';
import DraggingGestureHandler from './DraggingGestureHandler';
import { isValidNumber, isnan, TEST_MIN_IF_NOT_NAN, VEC_LEN_SQ } from './utils';
import State from '../State';
class PanGestureHandler extends DraggingGestureHandler {
get name() {
return 'pan';
}
get NativeGestureClass() {
return Hammer.Pan;
}
getHammerConfig() {
return {
...super.getHammerConfig(),
direction: this.getDirection(),
};
}
getState(type) {
const nextState = super.getState(type);
// Ensure that the first state sent is `BEGAN` and not `ACTIVE`
if (
this.previousState === State.UNDETERMINED &&
nextState === State.ACTIVE
) {
return State.BEGAN;
}
return nextState;
}
getDirection() {
const config = this.getConfig();
const {
activeOffsetXStart,
activeOffsetXEnd,
activeOffsetYStart,
activeOffsetYEnd,
minDist,
} = config;
let directions = [];
let horizontalDirections = [];
if (!isnan(minDist)) {
return Hammer.DIRECTION_ALL;
}
if (!isnan(activeOffsetXStart))
horizontalDirections.push(Hammer.DIRECTION_LEFT);
if (!isnan(activeOffsetXEnd))
horizontalDirections.push(Hammer.DIRECTION_RIGHT);
if (horizontalDirections.length === 2)
horizontalDirections = [Hammer.DIRECTION_HORIZONTAL];
directions = directions.concat(horizontalDirections);
let verticalDirections = [];
if (!isnan(activeOffsetYStart))
verticalDirections.push(Hammer.DIRECTION_UP);
if (!isnan(activeOffsetYEnd))
verticalDirections.push(Hammer.DIRECTION_DOWN);
if (verticalDirections.length === 2)
verticalDirections = [Hammer.DIRECTION_VERTICAL];
directions = directions.concat(verticalDirections);
if (!directions.length) {
return Hammer.DIRECTION_NONE;
}
if (
directions[0] === Hammer.DIRECTION_HORIZONTAL &&
directions[1] === Hammer.DIRECTION_VERTICAL
) {
return Hammer.DIRECTION_ALL;
}
if (horizontalDirections.length && verticalDirections.length) {
return Hammer.DIRECTION_ALL;
}
return directions[0];
}
getConfig() {
if (!this._hasCustomActivationCriteria) {
// Default config
// If no params have been defined then this config should emulate the native gesture as closely as possible.
return {
minDistSq: 10,
};
}
return this.config;
}
shouldFailUnderCustomCriteria({ deltaX, deltaY }, criteria) {
return (
(!isnan(criteria.failOffsetXStart) &&
deltaX < criteria.failOffsetXStart) ||
(!isnan(criteria.failOffsetXEnd) && deltaX > criteria.failOffsetXEnd) ||
(!isnan(criteria.failOffsetYStart) &&
deltaY < criteria.failOffsetYStart) ||
(!isnan(criteria.failOffsetYEnd) && deltaY > criteria.failOffsetYEnd)
);
}
shouldActivateUnderCustomCriteria({ deltaX, deltaY, velocity }, criteria) {
return (
(!isnan(criteria.activeOffsetXStart) &&
deltaX < criteria.activeOffsetXStart) ||
(!isnan(criteria.activeOffsetXEnd) &&
deltaX > criteria.activeOffsetXEnd) ||
(!isnan(criteria.activeOffsetYStart) &&
deltaY < criteria.activeOffsetYStart) ||
(!isnan(criteria.activeOffsetYEnd) &&
deltaY > criteria.activeOffsetYEnd) ||
TEST_MIN_IF_NOT_NAN(
VEC_LEN_SQ({ x: deltaX, y: deltaY }),
criteria.minDistSq
) ||
TEST_MIN_IF_NOT_NAN(velocity.x, criteria.minVelocityX) ||
TEST_MIN_IF_NOT_NAN(velocity.y, criteria.minVelocityY) ||
TEST_MIN_IF_NOT_NAN(VEC_LEN_SQ(velocity), criteria.minVelocitySq)
);
}
shouldMultiFingerPanFail({ pointerLength, scale, deltaRotation }) {
if (pointerLength <= 1) {
return false;
}
// Test if the pan had too much pinching or rotating.
const deltaScale = Math.abs(scale - 1);
const absDeltaRotation = Math.abs(deltaRotation);
if (deltaScale > MULTI_FINGER_PAN_MAX_PINCH_THRESHOLD) {
// > If the threshold doesn't seem right.
// You can log the value which it failed at here:
return true;
}
if (absDeltaRotation > MULTI_FINGER_PAN_MAX_ROTATION_THRESHOLD) {
// > If the threshold doesn't seem right.
// You can log the value which it failed at here:
return true;
}
return false;
}
updateHasCustomActivationCriteria(criteria) {
return (
isValidNumber(criteria.minDistSq) ||
isValidNumber(criteria.minVelocityX) ||
isValidNumber(criteria.minVelocityY) ||
isValidNumber(criteria.minVelocitySq) ||
isValidNumber(criteria.activeOffsetXStart) ||
isValidNumber(criteria.activeOffsetXEnd) ||
isValidNumber(criteria.activeOffsetYStart) ||
isValidNumber(criteria.activeOffsetYEnd)
);
}
isGestureEnabledForEvent(props, recognizer, inputData) {
if (this.shouldFailUnderCustomCriteria(inputData, props)) {
return { failed: true };
}
const velocity = { x: inputData.velocityX, y: inputData.velocityY };
if (
this._hasCustomActivationCriteria &&
this.shouldActivateUnderCustomCriteria(
{ deltaX: inputData.deltaX, deltaY: inputData.deltaY, velocity },
props
)
) {
if (
this.shouldMultiFingerPanFail({
pointerLength: inputData.maxPointers,
scale: inputData.scale,
deltaRotation: inputData.deltaRotation,
})
) {
return {
failed: true,
};
}
return { success: true };
}
return { success: false };
}
}
function validateConfig(config = {}) {
const isNum = v => isnan(v) || typeof v === 'number';
const isBool = v => typeof v === 'boolean';
const valid = {
enabled: isBool,
minDistSq: isNum,
minVelocityX: isNum,
minVelocityY: isNum,
// TODO: Bacon: remove `minVelocity`
minVelocity: isNum,
minVelocitySq: isNum,
activeOffsetXStart: isNum,
activeOffsetXEnd: isNum,
failOffsetXStart: isNum,
failOffsetXEnd: isNum,
activeOffsetYStart: isNum,
activeOffsetYEnd: isNum,
failOffsetYStart: isNum,
failOffsetYEnd: isNum,
hasCustomActivationCriteria: isBool,
minPointers: isNum,
maxPointers: isNum,
};
const keys = Object.keys(valid);
let invalidKeys = [];
for (const key of Object.keys(config)) {
if (keys.includes(key)) {
if (valid[key](config[key])) {
console.warn('Invalid type: ' + key + ': ' + config[key]);
}
} else {
invalidKeys.push(key);
}
}
if (invalidKeys.length) {
throw new Error('Invalid config props found: ' + invalidKeys.join(', '));
}
return config;
}
export default PanGestureHandler;