UNPKG

react-native-gesture-handler

Version:

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

167 lines (125 loc) 4.29 kB
import { AdaptedEvent, EventTypes } from '../interfaces'; import PointerTracker from '../tools/PointerTracker'; export interface RotationGestureListener { onRotationBegin: (detector: RotationGestureDetector) => boolean; onRotation: (detector: RotationGestureDetector) => boolean; onRotationEnd: (detector: RotationGestureDetector) => void; } export default class RotationGestureDetector implements RotationGestureListener { onRotationBegin: (detector: RotationGestureDetector) => boolean; onRotation: (detector: RotationGestureDetector) => boolean; onRotationEnd: (detector: RotationGestureDetector) => void; private currentTime = 0; private previousTime = 0; private previousAngle = 0; private rotation = 0; private anchorX = 0; private anchorY = 0; private isInProgress = false; private keyPointers: number[] = [NaN, NaN]; constructor(callbacks: RotationGestureListener) { this.onRotationBegin = callbacks.onRotationBegin; this.onRotation = callbacks.onRotation; this.onRotationEnd = callbacks.onRotationEnd; } private updateCurrent(event: AdaptedEvent, tracker: PointerTracker): void { this.previousTime = this.currentTime; this.currentTime = event.time; const [firstPointerID, secondPointerID] = this.keyPointers; const firstPointerCoords = tracker.getLastAbsoluteCoords(firstPointerID); const secondPointerCoords = tracker.getLastAbsoluteCoords(secondPointerID); const vectorX: number = secondPointerCoords.x - firstPointerCoords.x; const vectorY: number = secondPointerCoords.y - firstPointerCoords.y; this.anchorX = (firstPointerCoords.x + secondPointerCoords.x) / 2; this.anchorY = (firstPointerCoords.y + secondPointerCoords.y) / 2; // Angle diff should be positive when rotating in clockwise direction const angle: number = -Math.atan2(vectorY, vectorX); this.rotation = Number.isNaN(this.previousAngle) ? 0 : this.previousAngle - angle; this.previousAngle = angle; if (this.rotation > Math.PI) { this.rotation -= Math.PI; } else if (this.rotation < -Math.PI) { this.rotation += Math.PI; } if (this.rotation > Math.PI / 2) { this.rotation -= Math.PI; } else if (this.rotation < -Math.PI / 2) { this.rotation += Math.PI; } } private finish(): void { if (!this.isInProgress) { return; } this.isInProgress = false; this.keyPointers = [NaN, NaN]; this.onRotationEnd(this); } private setKeyPointers(tracker: PointerTracker): void { if (this.keyPointers[0] && this.keyPointers[1]) { return; } const pointerIDs: IterableIterator<number> = tracker.getData().keys(); this.keyPointers[0] = pointerIDs.next().value as number; this.keyPointers[1] = pointerIDs.next().value as number; } public onTouchEvent(event: AdaptedEvent, tracker: PointerTracker): boolean { switch (event.eventType) { case EventTypes.DOWN: this.isInProgress = false; break; case EventTypes.ADDITIONAL_POINTER_DOWN: if (this.isInProgress) { break; } this.isInProgress = true; this.previousTime = event.time; this.previousAngle = NaN; this.setKeyPointers(tracker); this.updateCurrent(event, tracker); this.onRotationBegin(this); break; case EventTypes.MOVE: if (!this.isInProgress) { break; } this.updateCurrent(event, tracker); this.onRotation(this); break; case EventTypes.ADDITIONAL_POINTER_UP: if (!this.isInProgress) { break; } if (this.keyPointers.indexOf(event.pointerId) >= 0) { this.finish(); } break; case EventTypes.UP: if (this.isInProgress) { this.finish(); } break; } return true; } public getTimeDelta(): number { return this.currentTime + this.previousTime; } public getAnchorX(): number { return this.anchorX; } public getAnchorY(): number { return this.anchorY; } public getRotation(): number { return this.rotation; } public reset(): void { this.keyPointers = [NaN, NaN]; this.isInProgress = false; } }