Free-spin mice can overload mapbox wheel-zoom with events.
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 12.4k
- Forks
- 2.4k
- PR merge metrics
- No merged PRs in 30d
Description
### mapbox-gl-js version
v3.14.0
### Browser and version
Chrome 139
### Expected behavior
A rapid flick of the mousewheel on a "free-spin" mouse should zoom in and out properly, without stalling.
### Actual behavior
I have a Dell MS900 mouse with a "free-spin" mousewheel mode.
When using the mousewheel to zoom in or out rapidly, mapbox gl becomes completely overwhelmed with mousewheel events.
The video below demonstrates the issue. This is following one fast flick of the mousewheel to zoom in, and then one fast flick to zoom out. Each time, everything completely stalls, presumably because the mousewheel handler is being flooded with events. Eventually everything catches up and the zoom in/out actually takes place.
https://github.com/user-attachments/assets/d656f3e2-e844-46fd-8b8b-ea88132d4c89
I've been able to workaround the problem with a hacky gate that prevents mapbox from receiving the events faster than the user's refresh rate allows:
```
// Gate mousewheel events, prevents overloading mapbox gl with too many events
const el = this.map.getCanvas().parentNode;
// Estimate the display's frame interval (EMA-smoothed), default ~60 Hz
let frameInterval = 1000 / 60;
let last = performance.now();
(function tick(t = performance.now()) {
const dt = t - last;
last = t;
// Exponential moving average to avoid jitter
frameInterval = 0.8 * frameInterval + 0.2 * dt;
// Keep sane bounds in case rAF throttles in background tabs
if (frameInterval < 6) frameInterval = 6; // ~166 Hz
if (frameInterval > 20) frameInterval = 20; // ~50 Hz
requestAnimationFrame(tick);
})();
let nextAllowed = 0;
function gateWheel(e) {
const t = e.timeStamp;
if (t < nextAllowed) {
e.preventDefault();
e.stopImmediatePropagation();
return;
}
nextAllowed = t + frameInterval; // allow ~one per frame
// Don't preventDefault here—let Mapbox handle the allowed one
}
el.addEventListener('wheel', gateWheel, { capture: true, passive: false });
```
This hack results in the following experience:
https://github.com/user-attachments/assets/60d1c15c-ec7c-45bc-9493-ff9950876906
I haven't had a chance to delve into the source code yet, but I suspect it just needs some kind of sane limits in the wheel handler to prevent the kind of flood of wheel events that these kinds of mice can output.
Also, I don't have a >60hz display, so i'm not really sure that limiting by refresh rate is the best approach. It might be that locking to a hardcoded 30-60 mousewheel events per second is better, given that mapbox performs it's own zoom animation.
### Link to the demonstration
_No response_
### Steps to trigger the unexpected behavior
_No response_
### Relevant log output
```shell
```
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the issue in Chrome 139 with a Dell MS900 mouse in free-spin mode, then inspect Mapbox GL JS's mousewheel handler and event-processing path. Done means rapid wheel flicks zoom correctly without stalling or overwhelming the map; the issue does not name a source file or test to run.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100