Repository navigation
Bug: Wheel Zoom-Out Delta Overcompensation #252
Description
Description
When using the mouse wheel to zoom out, the canvas scale decreases too much, sometimes jumping to very small. This happens because the zoom-out calculation depends on the current scale, making the decrease too aggressive.
For example, at 100% scale, a single scroll can drop the scale from 1 to 0, making the canvas disappear. However, zooming in (scroll up) works correctly, increasing the scale smoothly.
Expected result
Zooming out with the mouse wheel should decrease the canvas scale incrementally and symmetrically to zooming in, but using an exponential scale approach.
For example, starting at a scale of 100%:
Scroll up: Increase exponentially by a fixed factor (e.g., 10% → 20% → 40% → 80%).
Scroll down: Decrease exponentially by the same fixed factor (e.g., 80 → 40% → 20% → 10%)
Actual result
Zooming out with the mouse wheel reduces the scale drastically in one step, proportional to the current scale, often resulting in complete zoom-out:
At 100%: Zooms out to 10%
At 50%: Zooms out to 10%
Zooming in works as expected, with exponentially increases.
Which browsers are you seeing the problem on?
Microsoft Edge
Additional details
As a reverse engineer, I decided to investigate this issue using DevTools since I really like this project—but the zooming behavior was frustrating.
Fix Implemented:
I updated wheelChange to use an exponential zooming approach, doubling the scale when zooming in and halving it when zooming out. Instead of applying a scale factor based on the current offset, the new implementation calculates the target scale and adjusts _delta accordingly.
Additionally, I added a safeguard to ensure the scale never drops below 0.1, preventing invalid values or sudden jumps to near-zero. The updated logic ensures smooth and predictable zooming behavior:
https://stately.ai/registry/_next/static/chunks/1339-d0e529723d5c15c1.js
wheelChange(e) {
let t = "uv" in e;
!t && e.cancelable && e.preventDefault();
let n = this.state;
- n._delta = [-eU(e)[1] / 100 * n.offset[0], 0],
+ const direction = -eU(e)[1] > 0 ? 1 : -1; // 1 for zoom in, -1 for zoom out
+ const newScale = direction > 0 ? n.offset[0] * 2 : n.offset[0] / 2; // Double or halve
+ n._delta = [newScale - n.offset[0], 0]; // Difference to reach target scale
+ n._delta[0] = Math.max(n._delta[0], -n.offset[0] + 0.1); // Prevent scale ≤ 0, min 0.1
eE.addTo(n._movement, n._delta),
e4(n),
this.state.origin = [e.clientX, e.clientY],
this.compute(e),
this.emit()
}OR a oneline solution by Grok;
wheelChange(e) {
let t = "uv" in e;
!t && e.cancelable && e.preventDefault();
let n = this.state;
- n._delta = [-eU(e)[1] / 100 * n.offset[0], 0],
+ n._delta = [Math.max((-eU(e)[1] > 0 ? n.offset[0] * 2 : n.offset[0] / 2) - n.offset[0], -n.offset[0] + 0.1), 0]
eE.addTo(n._movement, n._delta);
e4(n);
this.state.origin = [e.clientX, e.clientY];
this.compute(e);
this.emit();
}

Activity
[-]Bug: Wheel Zoom-Out Delta Overcompensatio[/-][+]Bug: Wheel Zoom-Out Delta Overcompensation[/+]