From a4aa014e5eaceb5699ea0f0a07fe19b97ffa0aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moki=20=F0=9F=92=A4?= <38482240+mokizzz@users.noreply.github.com> Date: Fri, 29 Aug 2025 07:17:03 +0900 Subject: [PATCH] Fix: Rework wheel and touch handling for pan and zoom (#1956) ## Description: Fixes https://github.com/openfrontio/OpenFrontIO/issues/1827. Summary: - Restore the code of `onScroll()` method which was modified by #1717. - Rework he `wheel` event logic to better distinguish between trackpad pans and mouse wheel zooms. It now uses a heuristic where any scroll event with a horizontal component (`deltaX !== 0`) is treated as a pan, while purely vertical scrolls are treated as a zoom. This is a compromise that fixes mouse wheel behavior, with the trade-off that vertical-only trackpad swipes now also zoom (which is difficult for human fingers to trigger). - Solve the screen jittering problem when touching the screen by 2 fingers (which because when the second finger touches, `lastPointerX` and `lastPointerY` are not recalculated in time.). **Screen recording before fixing:** (macbook, broken scroll zoom) https://github.com/user-attachments/assets/5ba0fc24-2aec-4ecb-ab0f-2b0a0574d57e (iphone, 2-fingers drag works well, but screen jittering exists) https://github.com/user-attachments/assets/374f4f0f-688c-4b75-a20a-177144556c8c **and after fixing:** (macbook, scroll works well) https://github.com/user-attachments/assets/b7e3447f-9936-4971-90c4-8644d0a9619d (iphone, 2-fingers drag works well, no screen jittering) https://github.com/user-attachments/assets/9d952082-a672-42b6-a117-7a9fed6ea5f0 ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: yumika8269 --- src/client/InputHandler.ts | 59 +++++++++++++++----------------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/src/client/InputHandler.ts b/src/client/InputHandler.ts index 15d6bee9d..f6d661f07 100644 --- a/src/client/InputHandler.ts +++ b/src/client/InputHandler.ts @@ -173,9 +173,10 @@ export class InputHandler { this.canvas.addEventListener( "wheel", (e) => { - this.onScroll(e); + if (!this.onTrackpadPan(e)) { + this.onScroll(e); + } this.onShiftScroll(e); - this.onTrackpadPan(e); e.preventDefault(); }, { passive: false }, @@ -413,20 +414,8 @@ export class InputHandler { const realCtrl = this.activeKeys.has("ControlLeft") || this.activeKeys.has("ControlRight"); - - const isZoomGesture = - event.ctrlKey || - event.metaKey || - Math.abs(event.deltaZ) > 0 || - (event.deltaMode === 1 && Math.abs(event.deltaY) > 0) || - (event.deltaMode === 0 && Math.abs(event.deltaY) >= 50); - - if (isZoomGesture) { - const ratio = event.ctrlKey && !realCtrl ? 10 : 1; - this.eventBus.emit( - new ZoomEvent(event.x, event.y, event.deltaY * ratio), - ); - } + const ratio = event.ctrlKey && !realCtrl ? 10 : 1; // Compensate pinch-zoom low sensitivity + this.eventBus.emit(new ZoomEvent(event.x, event.y, event.deltaY * ratio)); } } @@ -438,32 +427,25 @@ export class InputHandler { } } - private onTrackpadPan(event: WheelEvent) { - if (event.shiftKey) { - return; + private onTrackpadPan(event: WheelEvent): boolean { + if (event.shiftKey || event.ctrlKey || event.metaKey) { + return false; } - if (event.ctrlKey || event.metaKey) { - return; + const isTrackpadPan = event.deltaMode === 0 && event.deltaX !== 0; + + if (!isTrackpadPan) { + return false; } - const isTrackpadPan = - event.deltaMode === 0 && - (Math.abs(event.deltaX) > 0 || Math.abs(event.deltaY) > 0) && - ((Math.abs(event.deltaX) > 0 && Math.abs(event.deltaY) > 0) || - event.deltaX % 1 !== 0 || - event.deltaY % 1 !== 0 || - (Math.abs(event.deltaX) < 30 && Math.abs(event.deltaY) < 30)); + const panSensitivity = 1.0; + const deltaX = -event.deltaX * panSensitivity; + const deltaY = -event.deltaY * panSensitivity; - if (isTrackpadPan) { - const panSensitivity = 1.0; - const deltaX = -event.deltaX * panSensitivity; - const deltaY = -event.deltaY * panSensitivity; - - if (Math.abs(deltaX) > 0.5 || Math.abs(deltaY) > 0.5) { - this.eventBus.emit(new DragEvent(deltaX, deltaY)); - } + if (Math.abs(deltaX) > 0.5 || Math.abs(deltaY) > 0.5) { + this.eventBus.emit(new DragEvent(deltaX, deltaY)); } + return true; } private onPointerMove(event: PointerEvent) { @@ -513,6 +495,11 @@ export class InputHandler { private onTouchStart(event: TouchEvent) { if (event.touches.length === 2) { event.preventDefault(); + // Solve screen jittering problem + const touch1 = event.touches[0]; + const touch2 = event.touches[1]; + this.lastPointerX = (touch1.clientX + touch2.clientX) / 2; + this.lastPointerY = (touch1.clientY + touch2.clientY) / 2; } }