怎么用JS准确记录并绘制鼠标移动轨迹?

UE丶景红 阅读 142

我在做一个画板功能,想实时记录鼠标移动的路径然后画出来,但发现轨迹特别卡顿,而且点太稀疏连不成线。我试过在 mousemove 里直接 push 坐标到数组,但效果很差。

有没有办法平滑一点地采集轨迹?或者是不是应该用 requestAnimationFrame 配合?下面是我目前的代码:

const points = [];
document.addEventListener('mousemove', (e) => {
  points.push({ x: e.clientX, y: e.clientY });
  // 然后用 canvas 把这些点连起来
});
我来解答 赞 17 收藏
二维码
手机扫码查看
2 条解答
开发者超霞
直接在 mousemove 事件里 push 坐标确实会导致性能问题,因为鼠标移动太快,事件触发频率高,数据量大。你可以通过 requestAnimationFrame 来优化这个过程,减少绘制次数,同时确保动画流畅。

这里有一个改进的方案,使用一个中间变量来缓存鼠标位置,然后在 requestAnimationFrame 的回调里更新 canvas。这样可以有效降低绘制频率,让轨迹更加平滑。

const points = [];
let lastPoint;

document.addEventListener('mousemove', (e) => {
lastPoint = { x: e.clientX, y: e.clientY };
});

function draw() {
if (lastPoint) {
points.push(lastPoint);
lastPoint = null;
// 用 canvas 把这些点连起来的代码
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.beginPath();
points.forEach((point, index) => {
if (index === 0) {
ctx.moveTo(point.x, point.y);
} else {
ctx.lineTo(point.x, point.y);
}
});
ctx.stroke();
}
requestAnimationFrame(draw);
}

draw();


记得给 canvas 设置宽度和高度,并且初始路径要用 moveTo 开始,不然 lineTo 会从 0,0 开始画线。这样处理后应该能解决卡顿和轨迹不连贯的问题。
点赞
2026-03-24 17:02
爱学习的子晴
你的问题在于直接用直线连点,鼠标移动快的时候点间距大自然会稀疏卡顿。

核心解决思路:记录所有点,绘制时用二次贝塞尔曲线连接相邻点的中点,这样轨迹自动就平滑了。

直接上代码:

// 记录轨迹
const points = [];
document.addEventListener('mousemove', (e) => {
points.push({ x: e.clientX, y: e.clientY });
});

// 绘制函数
function drawPath(ctx, points) {
if (points.length < 2) return;

ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);

// 关键:每个控制点是当前点和下一点的中点
for (let i = 1; i < points.length - 1; i++) {
const xc = (points[i].x + points[i + 1].x) / 2;
const yc = (points[i].y + points[i + 1].y) / 2;
ctx.quadraticCurveTo(points[i].x, points[i].y, xc, yc);
}

// 连上最后一个点
const last = points[points.length - 1];
ctx.lineTo(last.x, last.y);
ctx.stroke();
}

// 每次绘制用 requestAnimationFrame
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPath(ctx, points);
requestAnimationFrame(render);
}
render();


这个方案的原理很简单:取相邻两点的中点作为贝塞尔曲线的控制点,起点是前一个中点,终点是当前点和下一点的中点。这样画出来的线比直接 lineTo 平滑得多。

如果还需要更高级的平滑效果,可以考虑记录时间戳,根据鼠标移动速度动态调整采样率,不过上面这个方案日常使用已经足够了,拿去改改就行。
点赞
2026-03-16 22:50