Canvas动画本质是逐帧重绘,遵循“清空→计算→绘制→提交”循环:需主动清空画布防拖影,每帧调用beginPath()重置路径,分离数据更新与绘制逻辑,并用requestAnimationFrame实现流畅同步。

Canvas动画不是“修改对象”,而是“重画一帧”。整个生命周期围绕“清空→计算→绘制→提交”四个环节循环进行,每一帧都是一次独立的像素覆盖过程。
清空画布:避免视觉残留的关键起点
Canvas没有图层或对象概念,所有内容一旦绘制就固化为像素。如果不主动清除,上一帧图形会持续叠加,造成拖影、重影或内容错乱。
- 全屏清除用 ctx.clearRect(0, 0, canvas.width, canvas.height),最常用也最稳妥
- 若下一帧会绘制全屏背景(如一张图片或纯色矩形),可跳过清空——新内容自然覆盖旧像素
- 局部动画(如只移动一个小球)可只清除小球原位置区域,提升性能
重置路径:防止意外连接的隐性陷阱
Canvas路径是累积的。如果前一帧画了一条线但没调用 beginPath(),下一帧的 moveTo() 或 lineTo() 可能从上一条线的终点继续延伸,导致图形错连。
- 每次开始新图形绘制前,务必调用 ctx.beginPath()
- 尤其在循环绘制多个形状(如多个圆、多条折线)时,漏掉它极易引发难以调试的视觉异常
- beginPath() 不影响样式设置(fillStyle、strokeStyle等),只重置路径状态
更新与绘制:分离逻辑与渲染
理想结构把“数据变化”和“画面呈现”分开:先更新坐标、角度、颜色等变量,再统一调用绘图API。这样便于调试、复用和扩展。
- 例如小球动画:先算 newx = oldx + vx,newy = oldy + vy;再用 fillRect(newx, newy, size, size)
- 避免在绘制过程中边算边画,否则逻辑混乱且不利于性能优化
- 复杂场景建议分层:背景层(静态/低频)、主体层(每帧更新)、UI层(按需更新)
帧刷新:用 requestAnimationFrame 同步浏览器节奏
不用 setTimeout 或 setInterval。requestAnimationFrame 由浏览器调度,自动匹配屏幕刷新率(通常60Hz),保证动画顺滑且节省资源。
- 标准写法是递归调用:requestAnimationFrame(animate) 放在 animate 函数末尾
- 动画暂停时,必须清除上一次的 requestAnimationFrame ID,否则持续占用资源
- 帧内应尽量控制绘制调用次数,合并同类操作(如批量画同色圆,避免反复设 fillStyle)


















