perf(zoom): only register wheel listener while Ctrl/Meta is held
The zoom-on-Ctrl-wheel handler was registered on `window` for the
lifetime of the app with `{ passive: false, capture: true }`. That
flag combination forces the browser to wait for the handler to return
on EVERY wheel event before applying scroll — even though the handler
exits at line 1 when no modifier is held. The user reported a
characteristic input lag: scrollbar-drag was perfectly smooth, mouse-wheel
was choppy. His description ("como si la ventana principal lo cachara y
lo mandara al div interno y eso causara ese input lag") was literal —
the capture-phase non-passive listener intercepts the event at window
before routing to the scroll container.
Refactor: track Ctrl/Meta state via keydown/keyup and register the
non-passive wheel listener only while a modifier is actually held.
Default state = no wheel listener at all → browser native fast-path.
`window blur` releases the listener if the user alt-tabs with Ctrl held
(otherwise keyup never fires).
cc
This commit is contained in:
1 file changed
+52
-3
@@ -75,10 +75,59 @@ function handleZoomWheel(event: WheelEvent): void {
|
||||
void applyZoomLevel(nextZoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zoom-on-Ctrl-wheel handling.
|
||||
*
|
||||
* Previously this registered a non-passive, capture-phase `wheel` listener
|
||||
* on `window` for the lifetime of the app. The non-passive flag (required
|
||||
* so `preventDefault` actually stops the WebView's native Ctrl+wheel zoom)
|
||||
* also tells the browser "wait for my handler before applying scroll" —
|
||||
* which kills the native fast-path for *every* wheel event in the app,
|
||||
* not just zoom-modified ones. The user perceived this as choppy mouse-wheel
|
||||
* scrolling vs perfectly-smooth scrollbar-drag scrolling.
|
||||
*
|
||||
* New approach: the wheel listener only exists while Ctrl/Meta is held.
|
||||
* Keydown registers it, keyup (or window blur) unregisters it. Net:
|
||||
* - 99% of normal scrolling: no wheel listener at all, browser fast-path
|
||||
* - During zoom (Ctrl held): non-passive listener active, preventDefault
|
||||
* still blocks native zoom and our `applyZoomLevel` runs
|
||||
*/
|
||||
const ZOOM_WHEEL_OPTIONS: AddEventListenerOptions = { passive: false, capture: true };
|
||||
let zoomWheelActive = false;
|
||||
|
||||
function registerZoomWheel(): void {
|
||||
if (zoomWheelActive) return;
|
||||
zoomWheelActive = true;
|
||||
window.addEventListener('wheel', handleZoomWheel, ZOOM_WHEEL_OPTIONS);
|
||||
}
|
||||
|
||||
function unregisterZoomWheel(): void {
|
||||
if (!zoomWheelActive) return;
|
||||
zoomWheelActive = false;
|
||||
window.removeEventListener('wheel', handleZoomWheel, ZOOM_WHEEL_OPTIONS);
|
||||
}
|
||||
|
||||
function handleZoomKeyDown(e: KeyboardEvent): void {
|
||||
if (e.ctrlKey || e.metaKey) registerZoomWheel();
|
||||
}
|
||||
|
||||
function handleZoomKeyUp(e: KeyboardEvent): void {
|
||||
// After any keyup, if neither modifier is held anymore, drop the listener.
|
||||
if (!e.ctrlKey && !e.metaKey) unregisterZoomWheel();
|
||||
}
|
||||
|
||||
export function setupZoomControls(): () => void {
|
||||
const options: AddEventListenerOptions = { passive: false, capture: true };
|
||||
window.addEventListener('wheel', handleZoomWheel, options);
|
||||
return () => window.removeEventListener('wheel', handleZoomWheel, options);
|
||||
window.addEventListener('keydown', handleZoomKeyDown);
|
||||
window.addEventListener('keyup', handleZoomKeyUp);
|
||||
// Releases the listener if the user alt-tabs with Ctrl held — otherwise
|
||||
// the keyup never fires and we stay in the slow path.
|
||||
window.addEventListener('blur', unregisterZoomWheel);
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleZoomKeyDown);
|
||||
window.removeEventListener('keyup', handleZoomKeyUp);
|
||||
window.removeEventListener('blur', unregisterZoomWheel);
|
||||
unregisterZoomWheel();
|
||||
};
|
||||
}
|
||||
|
||||
// ============ Last.fm Session ============
|
||||
|
||||
Reference in new issue
Block a user