UNPKG

react-native-gesture-handler

Version:

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

227 lines (174 loc) 6.03 kB
import { AdaptedEvent, Point } from '../interfaces'; import VelocityTracker from './VelocityTracker'; export interface TrackerElement { abosoluteCoords: Point; relativeCoords: Point; timestamp: number; velocityX: number; velocityY: number; } const MAX_POINTERS = 20; export default class PointerTracker { private velocityTracker = new VelocityTracker(); private trackedPointers: Map<number, TrackerElement> = new Map< number, TrackerElement >(); private touchEventsIds: Map<number, number> = new Map<number, number>(); private lastMovedPointerId: number; private cachedAbsoluteAverages: Point = { x: 0, y: 0 }; private cachedRelativeAverages: Point = { x: 0, y: 0 }; public constructor() { this.lastMovedPointerId = NaN; for (let i = 0; i < MAX_POINTERS; ++i) { this.touchEventsIds.set(i, NaN); } } public addToTracker(event: AdaptedEvent): void { if (this.trackedPointers.has(event.pointerId)) { return; } this.lastMovedPointerId = event.pointerId; const newElement: TrackerElement = { abosoluteCoords: { x: event.x, y: event.y }, relativeCoords: { x: event.offsetX, y: event.offsetY }, timestamp: event.time, velocityX: 0, velocityY: 0, }; this.trackedPointers.set(event.pointerId, newElement); this.mapTouchEventId(event.pointerId); this.cachedAbsoluteAverages = this.getAbsoluteCoordsAverage(); this.cachedRelativeAverages = this.getRelativeCoordsAverage(); } public removeFromTracker(pointerId: number): void { this.trackedPointers.delete(pointerId); this.removeMappedTouchId(pointerId); } public track(event: AdaptedEvent): void { const element: TrackerElement = this.trackedPointers.get( event.pointerId ) as TrackerElement; if (!element) { return; } this.lastMovedPointerId = event.pointerId; this.velocityTracker.add(event); const [velocityX, velocityY] = this.velocityTracker.getVelocity(); element.velocityX = velocityX; element.velocityY = velocityY; element.abosoluteCoords = { x: event.x, y: event.y }; element.relativeCoords = { x: event.offsetX, y: event.offsetY }; this.trackedPointers.set(event.pointerId, element); this.cachedAbsoluteAverages = this.getAbsoluteCoordsAverage(); this.cachedRelativeAverages = this.getRelativeCoordsAverage(); } // Mapping TouchEvents ID private mapTouchEventId(id: number): void { for (const [mappedId, touchId] of this.touchEventsIds) { if (isNaN(touchId)) { this.touchEventsIds.set(mappedId, id); break; } } } private removeMappedTouchId(id: number): void { const mappedId: number = this.getMappedTouchEventId(id); if (!isNaN(mappedId)) { this.touchEventsIds.set(mappedId, NaN); } } public getMappedTouchEventId(touchEventId: number): number { for (const [key, value] of this.touchEventsIds.entries()) { if (value === touchEventId) { return key; } } return NaN; } public getVelocity(pointerId: number) { return { x: this.trackedPointers.get(pointerId)?.velocityX as number, y: this.trackedPointers.get(pointerId)?.velocityY as number, }; } public getLastAbsoluteCoords(pointerId?: number) { return this.trackedPointers.get(pointerId ?? this.lastMovedPointerId) ?.abosoluteCoords as Point; } public getLastRelativeCoords(pointerId?: number) { return this.trackedPointers.get(pointerId ?? this.lastMovedPointerId) ?.relativeCoords as Point; } // Some handlers use these methods to send average values in native event. // This may happen when pointers have already been removed from tracker (i.e. pointerup event). // In situation when NaN would be sent as a response, we return cached value. // That prevents handlers from crashing public getAbsoluteCoordsAverage() { const coordsSum = this.getAbsoluteCoordsSum(); const avgX = coordsSum.x / this.trackedPointers.size; const avgY = coordsSum.y / this.trackedPointers.size; const averages = { x: isNaN(avgX) ? this.cachedAbsoluteAverages.x : avgX, y: isNaN(avgY) ? this.cachedAbsoluteAverages.y : avgY, }; return averages; } public getRelativeCoordsAverage() { const coordsSum = this.getRelativeCoordsSum(); const avgX = coordsSum.x / this.trackedPointers.size; const avgY = coordsSum.y / this.trackedPointers.size; const averages = { x: isNaN(avgX) ? this.cachedRelativeAverages.x : avgX, y: isNaN(avgY) ? this.cachedRelativeAverages.y : avgY, }; return averages; } public getAbsoluteCoordsSum(ignoredPointer?: number) { const sum = { x: 0, y: 0 }; this.trackedPointers.forEach((value, key) => { if (key !== ignoredPointer) { sum.x += value.abosoluteCoords.x; sum.y += value.abosoluteCoords.y; } }); return sum; } public getRelativeCoordsSum(ignoredPointer?: number) { const sum = { x: 0, y: 0 }; this.trackedPointers.forEach((value, key) => { if (key !== ignoredPointer) { sum.x += value.relativeCoords.x; sum.y += value.relativeCoords.y; } }); return sum; } public getTrackedPointersCount(): number { return this.trackedPointers.size; } public getTrackedPointersID(): number[] { const keys: number[] = []; this.trackedPointers.forEach((_value, key) => { keys.push(key); }); return keys; } public getData(): Map<number, TrackerElement> { return this.trackedPointers; } public resetTracker(): void { this.velocityTracker.reset(); this.trackedPointers.clear(); this.lastMovedPointerId = NaN; for (let i = 0; i < MAX_POINTERS; ++i) { this.touchEventsIds.set(i, NaN); } } public static shareCommonPointers( stPointers: number[], ndPointers: number[] ): boolean { return stPointers.some((pointerId) => ndPointers.includes(pointerId)); } }