为什么 mousemove 事件在快速移动时会跳过某些坐标?

开发者欧辰 阅读 2

我在做一个画板功能,监听 mousemove 来记录鼠标轨迹,但发现鼠标移动太快的时候,路径会出现断点,不是连续的。明明我每次都在 event 里取了 clientX 和 clientY 啊。

是不是浏览器不会触发每一个像素点的事件?那该怎么让轨迹更平滑呢?

我现在的代码大概是这样:

canvas.addEventListener('mousemove', (e) => {
  const x = e.clientX;
  const y = e.clientY;
  points.push({ x, y });
  drawLine(points);
});
我来解答 赞 8 收藏
二维码
手机扫码查看
1 条解答
司空慧娜
你说得对,浏览器确实不会触发每个像素点的事件。mousemove 的触发频率大概是 60fps 左右,鼠标移动快的时候,相邻两次事件之间的物理距离可能很大,直接把这些点连起来就会出现锯齿和断点。

最简单的解决办法是在相邻的两个点之间做线性插值,补上中间的点。比如这样:

canvas.addEventListener('mousemove', (e) => {
const x = e.clientX;
const y = e.clientY;

if (lastX !== undefined) {
// 在上一个点和当前点之间插值
const dist = Math.hypot(x - lastX, y - lastY);
const step = 2; // 每隔2像素插一个点

for (let i = 0; i < dist; i += step) {
const t = i / dist;
const px = lastX + (x - lastX) * t;
const py = lastY + (y - lastY) * t;
points.push({ x: px, y: py });
}
}

points.push({ x, y });
lastX = x;
lastY = y;

drawLine(points);
});


如果想更平滑,可以用二次贝塞尔曲线来连接断点,而不是直接用直线。原理是取两个断点的中点作为控制点,画抛物线。

另外提一下,现在更推荐用 Pointer Events API,pointermove 事件对触摸和触控笔也通用,而且规范里有提到事件触发的精度问题,不过浏览器实现上该跳过的点还是会跳过,插值这层还是得自己处理。
点赞
2026-03-19 18:23