用 canvas 监听 pointermove 事件缓存坐标,requestAnimationFrame 每帧用半透明黑层残影+连线重绘,控制数组长度防内存溢出,避免 CSS 缩放确保清晰流畅。
直接用 canvas 捕获鼠标移动事件,实时画点或线段是最轻量、可控性最强的做法。CSS 动画或伪元素方案无法真正“记录轨迹”,只能做跟随光标的效果(比如小圆点贴着鼠标跑),不是你想要的“拖尾”或“画线”效果。
mousemove 事件,拿到 event.clientX 和 event.clientY
requestAnimationFrame 每帧清空 canvas 并重绘整条轨迹(点连成线,或带透明度渐变)setTimeout 或高频 setInterval,会导致掉帧或轨迹断开pointermove 比 mousemove 更可靠在触屏设备(如 iPad、Surface)或启用了 Windows Ink 的笔输入场景下, mousemove 可能不触发或坐标偏移;pointermove 是 W3C 标准的统一输入事件,兼容鼠标、触摸、触控笔,且自带 pressure 和 ti 等扩展属性。
document.addEventListener('pointermove', handler)
{ passive: false },否则在某些移动端会静默失败mousemove 足够;但要做跨设备一致效果,必须切到 pointermove
globalAlpha
很多人试过设 ctx.globalAlpha = 0.05 再循环画线,结果发现整条轨迹越来越亮——因为 canvas 不是“图层叠加”,而是像素直接混合。正确做法是:每次清空画布前,用半透明黑色矩形盖一层(模拟残影)。
ctx.fillStyle = 'rgba(0, 0, 0, 0.08)'; ctx.fillRect(0, 0, canvas.width, canvas.height);
clearRect() 完全擦除,那是“无痕”效果鼠标轨迹动效卡,90% 是因为没控制好重绘节奏或数据结构。Canvas 本身不慢,慢的是逻辑。
push() + shift(),别用 unshift() 或 splice(0, 1),后者是 O(n) 时间复杂度pointermove 回调里直接调用 draw(),必须节流——用 requestAnimationFrame 统一驱动渲染width: 100%; height: 100%),要设原生 canvas.width/canvas.height 属性,否则像素拉伸导致模糊+掉帧