diff --git a/packages/react-native-gesture-handler/src/web/detectors/RotationGestureDetector.ts b/packages/react-native-gesture-handler/src/web/detectors/RotationGestureDetector.ts index 886cccd25a..a569ce5fc1 100644 --- a/packages/react-native-gesture-handler/src/web/detectors/RotationGestureDetector.ts +++ b/packages/react-native-gesture-handler/src/web/detectors/RotationGestureDetector.ts @@ -84,15 +84,23 @@ export default class RotationGestureDetector this.onRotationEnd(this); } - private setKeyPointers(tracker: PointerTracker): void { + private setKeyPointers(tracker: PointerTracker, excludeId?: number): void { if (this.keyPointers[0] && this.keyPointers[1]) { return; } - const pointerIDs: IterableIterator = tracker.trackedPointers.keys(); + let assigned = 0; - this.keyPointers[0] = pointerIDs.next().value as number; - this.keyPointers[1] = pointerIDs.next().value as number; + for (const id of tracker.trackedPointers.keys()) { + if (id === excludeId) { + continue; + } + + this.keyPointers[assigned++] = id; + if (assigned === 2) { + break; + } + } } public onTouchEvent(event: AdaptedEvent, tracker: PointerTracker): boolean { @@ -132,7 +140,13 @@ export default class RotationGestureDetector } if (this.keyPointers.indexOf(event.pointerId) >= 0) { - this.finish(); + if (tracker.trackedPointersCount <= 2) { + this.reset(); + } else { + this.keyPointers = [NaN, NaN]; + this.setKeyPointers(tracker, event.pointerId); + this.previousAngle = NaN; + } } break; diff --git a/packages/react-native-gesture-handler/src/web/detectors/ScaleGestureDetector.ts b/packages/react-native-gesture-handler/src/web/detectors/ScaleGestureDetector.ts index f2778bff97..1d74057874 100644 --- a/packages/react-native-gesture-handler/src/web/detectors/ScaleGestureDetector.ts +++ b/packages/react-native-gesture-handler/src/web/detectors/ScaleGestureDetector.ts @@ -44,10 +44,16 @@ export default class ScaleGestureDetector implements ScaleGestureListener { const action: EventTypes = event.eventType; const numOfPointers = tracker.trackedPointersCount; + // When the last second pointer lifts (going down to 1), pause without + // touching span/time state so the gesture resumes cleanly on re-add. + // When 3+ → 2+ pointers, fall through so configChanged resets the span + // baseline to the remaining pointer set and avoids a scale jump. + if (action === EventTypes.ADDITIONAL_POINTER_UP && numOfPointers <= 2) { + return true; + } + const streamComplete: boolean = - action === EventTypes.UP || - action === EventTypes.ADDITIONAL_POINTER_UP || - action === EventTypes.CANCEL; + action === EventTypes.UP || action === EventTypes.CANCEL; if (action === EventTypes.DOWN || streamComplete) { if (this.inProgress) { diff --git a/packages/react-native-gesture-handler/src/web/handlers/PinchGestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/PinchGestureHandler.ts index 3075dc2926..73df3006f1 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/PinchGestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/PinchGestureHandler.ts @@ -91,8 +91,8 @@ export default class PinchGestureHandler extends GestureHandler { protected override onPointerAdd(event: AdaptedEvent): void { this.tracker.addToTracker(event); super.onPointerAdd(event); - this.tryBegin(); this.scaleGestureDetector.onTouchEvent(event, this.tracker); + this.tryBegin(); } protected override onPointerUp(event: AdaptedEvent): void { @@ -113,26 +113,22 @@ export default class PinchGestureHandler extends GestureHandler { super.onPointerRemove(event); this.scaleGestureDetector.onTouchEvent(event, this.tracker); this.tracker.removeFromTracker(event.pointerId); - - if (this.state === State.ACTIVE && this.tracker.trackedPointersCount < 2) { - this.end(); - } } protected override onPointerMove(event: AdaptedEvent): void { + this.tracker.track(event); if (this.tracker.trackedPointersCount < 2) { return; } - this.tracker.track(event); this.scaleGestureDetector.onTouchEvent(event, this.tracker); super.onPointerMove(event); } protected override onPointerOutOfBounds(event: AdaptedEvent): void { + this.tracker.track(event); if (this.tracker.trackedPointersCount < 2) { return; } - this.tracker.track(event); this.scaleGestureDetector.onTouchEvent(event, this.tracker); super.onPointerOutOfBounds(event); diff --git a/packages/react-native-gesture-handler/src/web/handlers/RotationGestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/RotationGestureHandler.ts index 6157aee46a..16216b6312 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/RotationGestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/RotationGestureHandler.ts @@ -100,11 +100,12 @@ export default class RotationGestureHandler extends GestureHandler { this.tracker.addToTracker(event); super.onPointerAdd(event); - this.tryBegin(); this.rotationGestureDetector.onTouchEvent(event, this.tracker); + this.tryBegin(); } protected override onPointerMove(event: AdaptedEvent): void { + this.tracker.track(event); if (this.tracker.trackedPointersCount < 2) { return; } @@ -113,14 +114,13 @@ export default class RotationGestureHandler extends GestureHandler { this.cachedAnchorX = anchor.x; this.cachedAnchorY = anchor.y; - this.tracker.track(event); - this.rotationGestureDetector.onTouchEvent(event, this.tracker); super.onPointerMove(event); } protected override onPointerOutOfBounds(event: AdaptedEvent): void { + this.tracker.track(event); if (this.tracker.trackedPointersCount < 2) { return; } @@ -129,8 +129,6 @@ export default class RotationGestureHandler extends GestureHandler { this.cachedAnchorX = anchor.x; this.cachedAnchorY = anchor.y; - this.tracker.track(event); - this.rotationGestureDetector.onTouchEvent(event, this.tracker); super.onPointerOutOfBounds(event);