UNPKG

react-native-gesture-handler

Version:

Declarative API exposing native platform touch and gesture system to React Native

189 lines (149 loc) 4.78 kB
import type { ActionType } from '../../ActionType'; import { State } from '../../State'; import { SingleGestureName } from '../../v3/types'; import type { AdaptedEvent, Config, PropsRef } from '../interfaces'; import type { GestureHandlerDelegate } from '../tools/GestureHandlerDelegate'; import GestureHandler from './GestureHandler'; import type IGestureHandler from './IGestureHandler'; const DEFAULT_MIN_DURATION_MS = 500; const DEFAULT_MAX_DIST_DP = 10; const SCALING_FACTOR = 10; export default class LongPressGestureHandler extends GestureHandler { private minDurationMs = DEFAULT_MIN_DURATION_MS; private defaultMaxDistSq = DEFAULT_MAX_DIST_DP * SCALING_FACTOR; private maxDistSq = this.defaultMaxDistSq; private numberOfPointers = 1; private startX = 0; private startY = 0; private startTime = 0; private previousTime = 0; private activationTimeout: number | undefined; public constructor( delegate: GestureHandlerDelegate<unknown, IGestureHandler> ) { super(delegate); this.name = SingleGestureName.LongPress; } public override init( ref: number, propsRef: React.RefObject<PropsRef>, actionType: ActionType ) { if (this.enableContextMenu === undefined) { this.enableContextMenu = false; } super.init(ref, propsRef, actionType); } protected override transformNativeEvent() { return { ...super.transformNativeEvent(), duration: Date.now() - this.startTime, }; } public override updateGestureConfig(config: Config): void { super.updateGestureConfig(config); if (config.minDurationMs !== undefined) { this.minDurationMs = config.minDurationMs; } if (config.maxDist !== undefined) { this.maxDistSq = config.maxDist * config.maxDist; } if (config.numberOfPointers !== undefined) { this.numberOfPointers = config.numberOfPointers; } } protected override resetConfig(): void { super.resetConfig(); this.minDurationMs = DEFAULT_MIN_DURATION_MS; this.maxDistSq = this.defaultMaxDistSq; } protected override onStateChange(_newState: State, _oldState: State): void { clearTimeout(this.activationTimeout); } protected override onPointerDown(event: AdaptedEvent): void { if (!this.isButtonInConfig(event.button)) { return; } this.tracker.addToTracker(event); super.onPointerDown(event); this.startX = event.x; this.startY = event.y; this.tryBegin(); this.tryActivate(); } protected override onPointerAdd(event: AdaptedEvent): void { super.onPointerAdd(event); this.tracker.addToTracker(event); if (this.tracker.trackedPointersCount > this.numberOfPointers) { this.fail(); return; } const absoluteCoordsAverage = this.tracker.getAbsoluteCoordsAverage(); this.startX = absoluteCoordsAverage.x; this.startY = absoluteCoordsAverage.y; this.tryActivate(); } protected override onPointerMove(event: AdaptedEvent): void { super.onPointerMove(event); this.tracker.track(event); this.checkDistanceFail(); } protected override onPointerOutOfBounds(event: AdaptedEvent): void { super.onPointerOutOfBounds(event); this.tracker.track(event); this.checkDistanceFail(); } protected override onPointerUp(event: AdaptedEvent): void { super.onPointerUp(event); this.tracker.removeFromTracker(event.pointerId); if (this.state === State.ACTIVE) { this.end(); } else { this.fail(); } } protected override onPointerRemove(event: AdaptedEvent): void { super.onPointerRemove(event); this.tracker.removeFromTracker(event.pointerId); if ( this.tracker.trackedPointersCount < this.numberOfPointers && this.state !== State.ACTIVE ) { this.fail(); } } private tryBegin(): void { if (this.state !== State.UNDETERMINED) { return; } this.previousTime = Date.now(); this.startTime = this.previousTime; this.begin(); } private tryActivate(): void { if (this.tracker.trackedPointersCount !== this.numberOfPointers) { return; } if (this.minDurationMs > 0) { this.activationTimeout = setTimeout(() => { this.activate(); }, this.minDurationMs); } else if (this.minDurationMs === 0) { this.activate(); } } private checkDistanceFail(): void { const absoluteCoordsAverage = this.tracker.getAbsoluteCoordsAverage(); const dx = absoluteCoordsAverage.x - this.startX; const dy = absoluteCoordsAverage.y - this.startY; const distSq = dx * dx + dy * dy; if (distSq <= this.maxDistSq) { return; } if (this.state === State.ACTIVE) { this.cancel(); } else { this.fail(); } } }