Input · 14
Wheel stepper
滚轮分档 · wheel-stepperTreat a stream of wheel events as one page step, advancing once after a threshold.
A trackpad flick emits a burst of wheel events and an inertia tail. Stepping on every event skips several slides; a huge threshold makes a mouse wheel feel unresponsive. Accumulate, lock after one step, and unlock when the stream goes quiet.
Formula
acc += deltaPx if |acc| ≥ threshold and !locked: step(sign(acc)) locked ← true until quiet or new flick
Parameters
| This page | Mature libraries | |
|---|---|---|
| threshold | 40 px | motion-kit 40 px |
| quietMs | 180 ms | motion-kit 180 ms |
| spike | 1.8× | motion-kit 1.8× |
Doing it well
- Accumulate before stepping. motion-kit uses a 40 px threshold and locks the event stream after a step.www-motion/rewrite/lib/motion-kit.js:392–415
- End a gesture after 180 ms of quiet; a sudden 1.8× spike can start another flick early.www-motion/rewrite/lib/motion-kit.js:392–415
- The full motion-kit also admits another step after 950 ms of sustained scrolling, but checks the current magnitude against the peak to reject an inertia tail.www-motion/rewrite/lib/motion-kit.js:385–405
In our cases
11 studies use wheel stepping and 8 use swipe thresholds; their union is 14 studies. iris, cinema-reel and dimension use both, so the decision to change a slide should be consistent.
Used in 14 cases
Reference implementation · basics.js
/**
* One gesture = one step. Accumulate wheel delta until `threshold`, step, then stay locked
* until the stream has been quiet for `quietMs` (trackpad inertia tails keep firing for ~1 s).
* A sudden bigger event (`spike` × the last one) is a new flick and unlocks early.
*/
export function createWheelStepper(onStep, { threshold = 40, quietMs = 180, spike = 1.8 } = {}) {
let acc = 0, locked = false, last = 0, lastMag = 0, timer = 0;
return (delta, now = performance.now()) => {
const mag = Math.abs(delta);
if (locked && mag > lastMag * spike && mag > 8) locked = false;
lastMag = mag; last = now;
clearTimeout(timer);
timer = setTimeout(() => { locked = false; acc = 0; }, quietMs);
if (locked) return;
acc += delta;
if (Math.abs(acc) >= threshold) { onStep(Math.sign(acc)); acc = 0; locked = true; }
};
}