清空画布应精准清除脏区域而非全屏,记录动态元素上一帧包围盒并计算变化并集,静态内容用离屏canvas缓存复用,避免样式反复切换以提升性能。

Canvas 清空画布不是“越彻底越好”,关键在清得准、清得少、清得快。高频动画里无脑调用 clearRect(0, 0, canvas.width, canvas.height),很容易卡顿掉帧——真正拖慢性能的,往往不是绘制本身,而是你每帧都在擦除两百万像素、切换十几次颜色、重复设置字体。
只清“脏区域”,别清全屏
动态元素移动时,旧位置和新位置之间的并集就是需要清除的最小范围。比如一个 20×20 的图标从 (100, 80) 移到 (105, 82),只需清除 clearRect(100, 80, 25, 22)(取 x 最小、y 最小、宽高并集),而不是整屏重绘。
- 记录上一帧每个动态元素的 bounding box,计算变化区域并集
- 静态背景、固定文字、边框等完全不参与清除,提前画到离屏 canvas 或直接用
drawImage()复用 - 多个相邻小区域可合并为一个包围矩形,减少
clearRect调用次数
避免样式反复切换
每设一次 ctx.fillStyle 或 ctx.font 都有微小开销,100 次就是 30ms 级别的延迟。批量绘制同类型内容,能显著降低状态切换成本。
- 同类图形(如所有蓝色柱状图)统一设色后一口气画完
- 文字渲染前先固定
ctx.font和ctx.textAlign,不要每行都重设 - 颜色、字体、对齐方式等复用已有值,加个判断避免冗余赋值
善用离屏 canvas 缓存静态层
把不变的部分(底图、网格线、标题栏)单独画进一个离屏 canvas,主 canvas 只负责动态内容的局部清除与重绘。
- 创建
const offscreen = document.createElement('canvas'),尺寸与主 canvas 一致 - 首次绘制完成后,主 canvas 每帧只需
ctx.drawImage(offscreen, 0, 0) - 后续仅对运动对象做
clearRect+ 重绘,跳过静态内容重复渲染
注意坐标系与缩放陷阱
clearRect 坐标基于 canvas 的逻辑像素,若页面用 CSS 缩放或 ctx 应用了 scale/transform,直接传原始坐标会清偏。
- 清除前调用
ctx.setTransform(1, 0, 0, 1, 0, 0)重置变换矩阵 - 确保
canvas.width/canvas.height与实际渲染尺寸匹配(尤其高 DPI 设备) - 清除前简单判断区域是否在可视范围内,避免对不可见区域无效调用











