Canvas动画流畅的关键是构建持续可控的帧循环:需在animate函数末尾递归调用requestAnimationFrame(animate),每帧用clearRect和beginPath清屏重置,用时间戳计算匀速位移,并通过cancelAnimationFrame实现启停控制与资源清理。

要让 Canvas 动画真正流畅,关键不是“调用一次 requestAnimationFrame”,而是构建一个持续、可控、干净的帧循环——它得自己不断叫自己,还得每次画之前清干净、状态重置好、边界算清楚。
必须形成自调用链
requestAnimationFrame 本身只执行一次。想让它动起来,就得在动画函数末尾再调一次自己,形成递归调用链:
- 写成
requestAnimationFrame(animate)是启动; - 但要在
animate()函数最后一行再写一遍这句,才能持续下去; - 漏掉这一步,动画就只闪一下,根本跑不起来。
每帧必须清屏和重置状态
Canvas 不是“覆盖式”绘图,而是“叠加式”。上一帧没擦掉,新帧叠上去就会留残影、变糊、抖动:
- 用
ctx.clearRect(0, 0, canvas.width, canvas.height)清空整块画布; - 每次绘制前调用
ctx.beginPath(),避免路径意外累积; - 如果用了透明度(
globalAlpha)、旋转或缩放,记得每帧重置,比如ctx.setTransform(1, 0, 0, 1, 0, 0)。
用时间戳做匀速运动,别依赖帧率
直接写 x += 5 看似简单,但不同设备帧率不同,动画快慢就不一致。正确做法是利用系统传入的时间戳计算真实经过时间:
立即学习“前端免费学习笔记(深入)”;
- 函数签名写成
function animate(timestamp) { ... }; - 记录上一帧时间
lastTime,算出差值elapsed = timestamp - lastTime; - 位移按速度 × 时间算,比如
x += elapsed * 0.5,这样无论 60fps 还是 30fps,移动速度都一样。
记得启停控制和资源清理
动画不是开弓没有回头箭,尤其页面切走或组件卸载时,得主动停止,否则浪费 CPU、影响性能:
- 保存
requestAnimationFrame返回的 ID,如animationId = requestAnimationFrame(...); - 需要暂停时调
cancelAnimationFrame(animationId); - 在组件
onHide或aboutToDisappear钩子中调用它,防止内存泄漏。



















