输入 · 12
释放速度
Release velocity · velocity-estimator用近期轨迹估计甩动速度,并让停住后的旧速度过期。
只拿最后两个事件相减会被事件间隔和抖动放大;如果用户快速拖动、停住、稍后才松手,还会沿用旧速度继续飞。短窗口平均提供更稳的方向,超过静止时限则把释放速度设为零。
公式
v = (last.position − first.position) / Δt first = oldest sample within window if releaseTime − last.time > stale: v = 0
参数
| 本页默认 | 成熟实现 | |
|---|---|---|
| window | 80 ms | use-gesture 使用事件时间戳取近期多点,而非最后一段。 |
| stale | 32 ms | use-gesture 32 ms |
怎样做好
- 给采样点带时间戳;按真实 Δt 计算 px/s,不把每个事件当成一帧。@use-gesture core/src/engines/Engine.ts:340–353
- 松手前若超过 32 ms 没有新运动,速度归零;use-gesture 明确处理末次事件的这个间隔。@use-gesture core/src/engines/Engine.ts:8–21
- 释放时冻结本次估计,再把速度交给惯性或弹簧。不要在动画帧里继续改写它。
在我们的案例里
24 个案例显式估计速度。momentum-slider、drag-gallery 的质量记录都指出松手时复用了过期速度:快速拖动后停手再放开仍会甩出去。
19 / 69 个案例 · 松手速度没有过期判断来自 Codex 逐案质量标记(按关键词归类,近似值)
出现在 24 个案例
参考实现 · basics.js
/**
* Release velocity from recent pointer samples.
* window: average over the last ~80 ms (one event is too noisy)
* stale: if the pointer has been still for > 32 ms, the user stopped — velocity is 0 (use-gesture)
*/
export class VelocityTracker {
constructor({ window = 0.08, stale = 0.032 } = {}) { this.window = window; this.stale = stale; this.samples = []; }
reset() { this.samples.length = 0; }
add(time, x, y = 0) {
this.samples.push({ time, x, y });
while (this.samples.length > 2 && time - this.samples[0].time > this.window * 2) this.samples.shift();
}
velocity(now) {
const s = this.samples, last = s[s.length - 1];
if (!last || now - last.time > this.stale) return { x: 0, y: 0 };
let first = last;
for (let i = s.length - 2; i >= 0 && last.time - s[i].time <= this.window; i--) first = s[i];
const dt = last.time - first.time;
return dt > 0 ? { x: (last.x - first.x) / dt, y: (last.y - first.y) / dt } : { x: 0, y: 0 };
}
}