在 Chrome 中实现 1000Hz 可能吗?绕过事件循环测试鼠标轮询率

发布: (2025年12月25日 GMT+8 10:01)
3 min read
原文: Dev.to

Source: Dev.to

Cover image for Is 1000Hz possible in Chrome? Bypassing the Event Loop to test Mouse Polling Rate

问题:视觉 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 并绘制图表。

结果

通过将输入捕获与渲染分离,获得了与原生软件相匹配的一致读数。

Demo GIF

你可以在这里尝试在线演示:Mouse Polling Rate Test

该套件还包括游戏手柄测试器(用于摇杆漂移)和死像素测试,全部在浏览器本地运行。

讨论

我构建这个项目是为了证明现代 Web API 已经足够强大,能够取代许多“实用”桌面工具。

你怎么看?我们是否已经可以彻底卸载那些 500 MB 的驱动套件,还是原生应用在硬件交互方面永远占据优势?

欢迎在评论区留言!

Back to Blog

相关文章

阅读更多 »

JavaScript的秘密生活:异步

引言 蒂莫西叹了口气,将额头靠在凉爽的橡木绘图台上。几张 logic diagrams 摊在他面前。 > “我卡住了……”