1.计算与渲染

把动画的一帧渲染出来,需要经过以下步骤:

  1. 计算:处理电子表格逻辑,计算 workbook 的状态,不涉及 DOM 操作(当然也包含对 Canvas 上下文的操作)。
  2. 渲染:真正把对象绘制出来。
  3. JavaScript 调用 DOM API(包括 Canvas API)以进行渲染。
  4. 浏览器 把渲染后的结果呈现在屏幕上的过程。

Canvas 最佳实践总结2021_软件设计

2.canvas 绘制间隔策略

  • 主动触发刷新 canvas (电子表格)
  • 定时器循环刷新 canvas (动画,游戏推荐), requestAnimationFrame 相对于 setinterval 处理动画有以下几个优势:
    • 经过浏览器优化,动画更流畅
    • 窗口没激活时,动画将停止,省计算资源
    • 更省电,尤其是对移动终端

3.分层 Canvas

  • 分层 Canvas 的出发点是,动画中的元素(层),对渲染和动画的要求是不一样的
  • 实现 UI 重叠的视觉效果(案例:腾讯文档 word 画布)

Canvas 最佳实践总结2021_软件设计_02

4. canvas scale() 天然支持放大缩小

Canvas 最佳实践总结2021_软件设计_03

5.离屏绘制,使用缓存

绘制同样的一块区域,如果数据源是一张大图上的一部分,性能就会比较差,因为每一次绘制还包含了裁剪工作。可以先把待绘制的区域裁剪好,保存起来,这样每次绘制时就能轻松很多。

drawImage 方法的第一个参数不仅可以接收 Image 对象,也可以接收另一个 Canvas 对象。而且,使用 Canvas 对象绘制的开销与使用 Image 对象的开销几乎完全一致。我们只需要实现将对象绘制在一个未插入页面的 Canvas 中,然后每一帧使用这个 Canvas 来绘制。

并且离屏canvas也不能用的太泛滥,如果用太多离屏canvas也会有性能问题

6. 尽量少调用 canvasAPI ,尽可能集中绘制

提升 canvas 的性能最主要的还是得注意代码的结构,减少不必要的API调用,在每一帧中减少复杂的运算或者把复杂运算由每一帧算一次改成数帧算一次。

7.避免使用高耗能的 API

  • 清除画布尽量使用 clearRect
  • 避免浮点运算

一般情况下的性能: clearRect > fillRect > canvas.width=canvas.width;

8. 避免「阻塞」

偶尔的且较小的阻塞是可以接收的,频繁或较大的阻塞是不可以接受的。也就是说,我们需要解决两种阻塞:

  • 频繁(通常较小)的阻塞。其原因主要是过高的渲染性能开销,在每一帧中做的事情太多。
  • 较大(虽然偶尔发生)的阻塞。其原因主要是运行复杂算法、大规模的 DOM 操作等等。

对前者,我们应当仔细地优化代码,有时不得不降低动画的复杂(炫酷)程度,本文前几节中的优化方案,解决的就是这个问题。

而对于后者,主要有以下两种优化的策略。

  • 使用 Web Worker,在另一个线程里进行计算。
  • 将任务拆分为多个较小的任务,插在多帧中进行。

Web Worker 是好东西,性能很好,兼容性也不错。浏览器用另一个线程来运行 Worker 中的 JavaScript 代码,完全不会阻碍主线程的运行。动画(尤其是游戏)中难免会有一些时间复杂度比较高的算法,用 Web Worker 来运行再合适不过了。

9. 不要频繁设置绘图上下文的 font 属性。

10. Canvas 视图与事件模型封装