242 lines
6.7 KiB
JavaScript
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;
|