react-native-gesture-handler
Version:
Experimental implementation of a new declarative API for gesture handling in react-native
241 lines (193 loc) • 6 kB
text/typescript
import { AdaptedEvent } from '../interfaces';
import VelocityTracker from './VelocityTracker';
export interface TrackerElement {
lastX: number;
lastY: number;
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 cachedAverages: { x: number; y: number } = { 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 = {
lastX: event.x,
lastY: event.y,
timeStamp: event.time,
velocityX: 0,
velocityY: 0,
};
this.trackedPointers.set(event.pointerId, newElement);
this.mapTouchEventId(event.pointerId);
this.cachedAverages = {
x: this.getLastAvgX(),
y: this.getLastAvgY(),
};
}
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.lastX = event.x;
element.lastY = event.y;
this.trackedPointers.set(event.pointerId, element);
const avgX: number = this.getLastAvgX();
const avgY: number = this.getLastAvgY();
this.cachedAverages = {
x: avgX,
y: avgY,
};
}
//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 getVelocityX(pointerId: number): number {
return this.trackedPointers.get(pointerId)?.velocityX as number;
}
public getVelocityY(pointerId: number): number {
return this.trackedPointers.get(pointerId)?.velocityY as number;
}
/**
* Returns X coordinate of last moved pointer
*/
public getLastX(): number;
/**
*
* @param pointerId
* Returns X coordinate of given pointer
*/
// eslint-disable-next-line @typescript-eslint/unified-signatures
public getLastX(pointerId: number): number;
public getLastX(pointerId?: number): number {
if (pointerId !== undefined) {
return this.trackedPointers.get(pointerId)?.lastX as number;
} else {
return this.trackedPointers.get(this.lastMovedPointerId)?.lastX as number;
}
}
/**
* Returns Y coordinate of last moved pointer
*/
public getLastY(): number;
/**
*
* @param pointerId
* Returns Y coordinate of given pointer
*/
// eslint-disable-next-line @typescript-eslint/unified-signatures
public getLastY(pointerId: number): number;
public getLastY(pointerId?: number): number {
if (pointerId !== undefined) {
return this.trackedPointers.get(pointerId)?.lastY as number;
} else {
return this.trackedPointers.get(this.lastMovedPointerId)?.lastY as number;
}
}
// 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 getLastAvgX(): number {
const avgX: number = this.getSumX() / this.trackedPointers.size;
return isNaN(avgX) ? this.cachedAverages.x : avgX;
}
public getLastAvgY(): number {
const avgY: number = this.getSumY() / this.trackedPointers.size;
return isNaN(avgY) ? this.cachedAverages.y : avgY;
}
public getSumX(ignoredPointer?: number): number {
let sumX = 0;
this.trackedPointers.forEach((value, key) => {
if (key !== ignoredPointer) {
sumX += value.lastX;
}
});
return sumX;
}
public getSumY(ignoredPointer?: number): number {
let sumY = 0;
this.trackedPointers.forEach((value, key) => {
if (key !== ignoredPointer) {
sumY += value.lastY;
}
});
return sumY;
}
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));
}
}