From b99e3346a3bb30d1c2b4179bed403de0693b9253 Mon Sep 17 00:00:00 2001 From: Yevhenii Viazovskyi Date: Tue, 26 May 2026 13:21:56 +0000 Subject: [PATCH] fix: adjust trackpad detection and wheel zoom scaling --- core/src/helpers.ts | 16 ++++++++-------- core/src/zoompinch.ts | 6 +++++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/core/src/helpers.ts b/core/src/helpers.ts index a397852..3a3c8c7 100644 --- a/core/src/helpers.ts +++ b/core/src/helpers.ts @@ -48,14 +48,14 @@ export function round(value: number, decimals: number) { } export function detectTrackpad(event: WheelEvent): boolean { - // deltaMode === 0 means the browser is reporting pixel-level deltas, which - // is the reliable cross-browser signal for trackpad (and touch) input. - // Mouse wheels report in line units (deltaMode === 1) or produce large - // discrete pixel values that don't use pixel mode. - // We avoid the fragile wheelDeltaY / deltaY * -3 ratio check because it - // breaks during high-velocity momentum scrolling on macOS, causing the - // canvas to jump 25x further than intended. - return event.deltaMode === 0; + // Line/page mode is always a mouse wheel. + if (event.deltaMode !== 0) return false; + + // In pixel mode both inputs show up, so fall back to magnitude: + // trackpads send small deltas (usually under ~30), mouse wheels send + // chunks of ~100+ per notch. 50 sits comfortably between them. + const dominantDelta = Math.max(Math.abs(event.deltaX), Math.abs(event.deltaY)); + return dominantDelta < 50; } export function normalizeWheelDelta(event: WheelEvent): { deltaX: number; deltaY: number } { let { deltaX, deltaY, deltaMode } = event; diff --git a/core/src/zoompinch.ts b/core/src/zoompinch.ts index 3b093e5..5a6a3c8 100644 --- a/core/src/zoompinch.ts +++ b/core/src/zoompinch.ts @@ -237,7 +237,11 @@ export class Zoompinch extends EventTarget { if (ctrlKey) { const zoomSpeed = isTrackpad ? 0.01 * this.zoomSpeedAppleTrackpad : 0.1 * this.zoomSpeed; - const scaleFactor = 1 + -deltaY * zoomSpeed; + // Cap the per-event multiplier so a single oversized wheel event + // cannot drive scaleFactor to 0 or negative — which would clamp newScale to + // minScale on the first tick and silently kill zoom. + const cappedDelta = clamp(-deltaY * zoomSpeed, -0.5, 0.5); + const scaleFactor = 1 + cappedDelta; let newScale = currScale * scaleFactor;