Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion modules/core/src/controllers/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,17 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
}
const {inertia} = this;
const {_lastPinchEvent} = pinchEventWorkaround;
if (this.touchZoom && inertia && _lastPinchEvent && event.scale !== _lastPinchEvent.scale) {
// Skip pinch zoom inertia on touch: the final-lift frame is almost always
// noisy, and any forward projection reads as the camera "flinging" past
// the gesture. Trackpad / mouse pinches are unaffected.
const isTouchPinch = (event.srcEvent as PointerEvent)?.pointerType === 'touch';
if (
this.touchZoom &&
inertia &&
!isTouchPinch &&
_lastPinchEvent &&
event.scale !== _lastPinchEvent.scale
) {
const pos = this.getCenter(event);
let newControllerState = this.controllerState.rotateEnd();
const z = Math.log2(event.scale);
Expand Down
43 changes: 43 additions & 0 deletions test/modules/core/controllers/controllers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,49 @@ test('MapController#inertia', async () => {
});
});

test('MapController skips pinch zoom inertia on touch lift', () => {
// Touch pinches lift with a noisy final frame that can produce a large
// synthetic velocity. The end zoom should equal the last live pinch zoom,
// not the inertia-projected zoom.
const makePinchEvent = (
type: string,
scale: number,
deltaTime: number,
pointerType: 'touch' | 'mouse' = 'touch'
) => ({
type,
offsetCenter: {x: 50, y: 50},
scale,
rotation: 0,
deltaTime,
srcEvent: {preventDefault() {}, pointerType},
stopPropagation() {}
});

const controller = createTestController({
view: new MapView({controller: true}),
initialViewState: {
longitude: -122.45,
latitude: 37.78,
zoom: 10,
pitch: 30,
bearing: -45,
inertia: 300
}
});

controller.handleEvent(makePinchEvent('pinchstart', 1, 0) as any);
controller.handleEvent(makePinchEvent('pinchmove', 1.1, 16) as any);
const zoomAfterMove = controller.props.zoom as number;
// 100x scale spike on the lift frame — pre-fix would fling the zoom way past.
controller.handleEvent(makePinchEvent('pinchend', 100, 17) as any);

expect(
controller.props.zoom,
'touch pinch end stays at the last live zoom (no inertia projection)'
).toBeCloseTo(zoomAfterMove);
});

test('GlobeController', async () => {
await testController(
GlobeView,
Expand Down
Loading