diff --git a/src/clientSideScene/CameraControls.ts b/src/clientSideScene/CameraControls.ts index 6c0f39bca..b52787b7c 100644 --- a/src/clientSideScene/CameraControls.ts +++ b/src/clientSideScene/CameraControls.ts @@ -531,6 +531,10 @@ export class CameraControls { } zoomDirection = (event: WheelEvent): 1 | -1 => { + if (this.interactionGuards.zoom.pinchToZoom && isPinchToZoom(event)) { + return 1 + } + if (!this.interactionGuards.zoom.scrollAllowInvertY) return 1 // Safari provides the updated user setting on every event, so it's more // accurate than our cached value. @@ -1365,11 +1369,17 @@ function _getInteractionType( enableZoom: boolean ): interactionType | 'none' { if (event instanceof WheelEvent) { - if (enablePan && interactionGuards.pan.scrollCallback(event)) return 'pan' - if (enableRotate && interactionGuards.rotate.scrollCallback(event)) - return 'rotate' - if (enableZoom && interactionGuards.zoom.scrollCallback(event)) - return 'zoom' + // If the control scheme accepts pinch-to-zoom, and the event is + // pinch-to-zoom, never consider other interaction types. + if (interactionGuards.zoom.pinchToZoom && isPinchToZoom(event)) { + if (enableZoom) return 'zoom' + } else { + if (enablePan && interactionGuards.pan.scrollCallback(event)) return 'pan' + if (enableRotate && interactionGuards.rotate.scrollCallback(event)) + return 'rotate' + if (enableZoom && interactionGuards.zoom.scrollCallback(event)) + return 'zoom' + } } else { if (enablePan && interactionGuards.pan.callback(event)) return 'pan' if (enableRotate && interactionGuards.rotate.callback(event)) @@ -1379,6 +1389,18 @@ function _getInteractionType( return 'none' } +function isPinchToZoom(event: WheelEvent): boolean { + // Browsers do this hack. A couple issues: + // + // - According to MDN, it doesn't work on iOS. + // - It doesn't differentiate with a user actually holding Control and + // scrolling normally. It's possible to detect this by using onKeyDown and + // onKeyUp to track the state of the Control key. But we currently don't + // care about this since only the Apple Trackpad scheme looks for + // pinch-to-zoom events using interactionGuards.zoom.pinchToZoom. + return event.ctrlKey +} + /** * Tells the engine to fire it's animation waits for it to finish and then requests camera settings * to ensure the client-side camera is synchronized with the engine's camera state. diff --git a/src/lib/cameraControls.ts b/src/lib/cameraControls.ts index a4475bec9..567251ffb 100644 --- a/src/lib/cameraControls.ts +++ b/src/lib/cameraControls.ts @@ -67,6 +67,7 @@ interface MouseGuardZoomHandler { dragCallback: (e: MouseEvent) => boolean scrollCallback: (e: WheelEvent) => boolean scrollAllowInvertY?: boolean + pinchToZoom?: boolean lenientDragStartButton?: number } @@ -158,6 +159,7 @@ export const cameraMouseDragGuards: Record = { !e.altKey && !e.metaKey, scrollAllowInvertY: true, + pinchToZoom: true, }, rotate: { description: `${ALT} + Scroll`,