canvas性能下降主因是频繁切换绘图状态,如fillstyle等属性赋值会触发渲染管线重建,开销巨大;应按样式分组绘制、提取不变量到循环外、慎用save/restore、结合离屏canvas缓存静态内容。

Canvas 性能下降的常见原因,不是画得太多,而是“状态切得太勤”。fillStyle、strokeStyle、lineWidth、font 这些属性每次赋值,都会触发底层渲染管线重建,开销远超普通 JS 变量赋值——实测 fillStyle 单次赋值约 80ms/百万次,设为 undefined 甚至飙升至 800ms+。频繁切换不仅拖慢帧率,还容易引发隐式同步和缓存刷新。
按样式分组绘制,减少状态变更次数
把视觉属性相同的图形集中处理,是性价比最高的优化动作。不要边改色边画,而要先设好颜色,再一口气画完所有同色图形。
- ❌ 错误写法:循环内反复设置 fillStyle 并绘制单个矩形
- ✅ 正确做法:按颜色归类数据,对每组统一设置 fillStyle 后批量调用 fillRect
- 示例场景:绘制 200 个方块,其中 120 个绿色、80 个橙色 → 分两批绘制,仅两次 fillStyle 赋值
提取不变量到循环外部
如果一组图形共用同一字体、线宽或阴影配置,别在循环里重复写 ctx.font = '14px sans-serif'。把它提到 for 或 forEach 外面,只设一次。
- 尤其注意嵌套循环:内层循环中重复设置外层已确定的样式,属于典型冗余
- font、textAlign、textBaseline 等文本相关属性同样适用该原则
- 若需动态计算某属性(如渐变色),也尽量缓存结果,避免每次重算再赋值
慎用 save() / restore(),优先用显式重置
save() 和 restore() 操作会压入/弹出整个绘图状态栈,层级越深开销越大。它适合复杂变换嵌套,但不适用于简单样式切换。
- 用 ctx.fillStyle = 'red' 直接覆盖,比 save→set→restore 更轻量
- 若必须用 save/restore,确保成对出现且嵌套深度可控(建议 ≤3 层)
- 可考虑用对象临时保存关键状态(如当前 fillStyle),绘制后手动恢复,避开栈操作
结合离屏 Canvas 缓存静态内容
对于反复使用的图标、文字块或装饰元素,提前绘制到 OffscreenCanvas 中,后续直接 drawImage 复用。这样既规避了重复的状态设置,又跳过了实时路径生成开销。
- 适合场景:UI 图标、固定标签、网格背景、带阴影的按钮轮廓
- 注意:离屏 Canvas 尺寸需合理设定,过大浪费内存,过小导致缩放失真
- 可配合精灵图思路,把多个小元素合并进一张离屏画布,按坐标裁剪复用











