Canvas 动态图形性能优化核心是“少画、巧画”,通过分层缓存(静态/动态层分离)、区域级脏区重绘、对象级图形片段预渲染及帧率节流策略协同实现。

Canvas 绘制复杂动态图形时,直接每帧重绘所有内容会导致性能瓶颈,尤其在高分辨率或高频更新场景下。核心解法不是“画得更快”,而是“少画、巧画”——通过缓存复用机制跳过重复计算和绘制,把开销集中在真正变化的部分。
分层缓存:静态层与动态层分离
将画面拆解为多个逻辑层(如背景、UI控件、运动对象),对不常变化的层单独离屏 Canvas 缓存。例如地图底图、网格线、固定图标等可一次性绘制到一个 offscreenCanvas,后续帧只需 ctx.drawImage(offscreenCtx.canvas, 0, 0) 复用,避免重复路径生成和光栅化。
- 静态层建议使用
createImageBitmap转换为图像位图,提升 drawImage 渲染效率 - 动态层仅负责移动、缩放、形变等局部更新,避免污染静态缓存
- 当静态层内容需更新(如主题切换、缩放级别变化)时,才重新生成对应离屏缓存
区域级缓存:只重绘脏区(Dirty Rect)
不全屏清空再重绘,而是记录每个动态元素上一帧的包围矩形(bounding rect),与当前帧位置求并集得到“脏区”,仅在该区域内清除并重绘相关图形。这对粒子系统、拖拽组件、文本编辑器光标动画等场景特别有效。
- 用
ctx.clearRect(x, y, width, height)精确擦除脏区,而非ctx.clearRect(0, 0, w, h) - 脏区合并后若面积小于整屏 15%,通常能带来 2–5 倍绘制耗时下降
- 注意:需同步维护各元素的旧/新位置,避免漏绘或残留残影
对象级缓存:预渲染图形片段
对高频复用但参数可变的图形(如带阴影的圆角按钮、自定义箭头、图标簇),将其抽象为“可缓存图形对象”,内部维护一个带参数哈希键的缓存 Map。相同配置首次绘制到离屏 Canvas,后续直接复用。
- 键值示例:
`${type}-${size}-${color}-${radius}`,确保语义一致且无歧义 - 缓存项应设上限(如 50 个),配合 LRU 策略淘汰冷数据,防止内存泄漏
- 支持运行时监听 canvas 尺寸或设备像素比变化,自动失效并重建缩放适配的缓存
时机控制:跳帧与请求帧策略协同
缓存复用效果依赖于绘制节奏。即使有缓存,盲目每毫秒都调用 requestAnimationFrame 仍会触发无意义的合成与提交。应结合实际动画需求做节流:
- 对 60fps 非必需的动画(如缓慢过渡、鼠标 hover 效果),降为 30fps 或更低,减少缓存读取频次
- 利用
performance.now()计算真实帧间隔,动态调整是否跳过当前帧的绘制(尤其在 CPU/GPU 压力高时) - 对完全静止的画面(如用户未交互超过 2 秒),暂停 rAF,仅在事件触发时唤醒绘制流程


















