在 Chrome 中实现 1000Hz 可能吗?绕过事件循环测试鼠标轮询率
发布: (2025年12月25日 GMT+8 10:01)
3 min read
原文: Dev.to
Source: Dev.to

问题:视觉 vs. 输入
大多数网页动画依赖 requestAnimationFrame(rAF)。如果你的显示器是 60 Hz,rAF 大约每 16.6 ms 触发一次。在 rAF 循环中测量 1000 Hz 的鼠标(每 1 ms 发送一次数据)会错过约 94 % 的数据。
我们需要将 输入线程 与 渲染线程 解耦。
解决方案:pointermove + performance.now()
- 监听原始的
pointermove事件,它的触发频率高于屏幕刷新(Chrome/Edge 会暴露“合并事件”)。 - 使用高分辨率时间 API(
performance.now())获取微秒级精度——Date.now()只能到毫秒。
代码
let lastEventTime = 0;
let pollingRateSamples = [];
window.addEventListener('pointermove', (event) => {
// 1. Get the microsecond‑precise timestamp
const currentEventTime = performance.now();
// 2. Calculate the delta (time between two events)
const timeDelta = currentEventTime - lastEventTime;
// 3. Convert to Frequency (Hz)
// Hz = 1000 ms / delta
const instantaneousHz = 1000 / timeDelta;
// 4. Filter out noise (idle or super slow movements)
if (instantaneousHz > 10 && instantaneousHz < 8000) {
pollingRateSamples.push(instantaneousHz);
}
lastEventTime = currentEventTime;
// NOTE: Do NOT update the DOM here!
// Updating the UI every 1 ms will kill browser performance.
// Store the data, and visualize it in a separate requestAnimationFrame loop.
});
“事件循环”陷阱
浏览器会对事件进行分组以节省电量。在 pointermove 监听器内部执行繁重操作会导致节流,使其匹配帧率。
如何避免
- 保持监听器轻量(仅做数学运算和数组 push)。
- 将可视化解耦:使用单独的
requestAnimationFrame循环读取pollingRateSamples并绘制图表。
结果
通过将输入捕获与渲染分离,获得了与原生软件相匹配的一致读数。

你可以在这里尝试在线演示:Mouse Polling Rate Test
该套件还包括游戏手柄测试器(用于摇杆漂移)和死像素测试,全部在浏览器本地运行。
讨论
我构建这个项目是为了证明现代 Web API 已经足够强大,能够取代许多“实用”桌面工具。
你怎么看?我们是否已经可以彻底卸载那些 500 MB 的驱动套件,还是原生应用在硬件交互方面永远占据优势?
欢迎在评论区留言!