canvas 作为前端绘制图形常用的方案,在许多实际场景中都需要考虑性能瓶颈问题,最近在做一个图片转粒子特效的项目,涉及到一些复杂图形以及数据量较大的渲染,会产生一些卡顿情况,所以也在网上寻找了一些解决方案并结合自身项目做了一些整理。
减少 canvas 上下文的切换
canvas 绘制都是调用上下文context实现,频繁修改 context 的属性会有一定的性能开销,绘制图形数量较多时,会有频繁切换 context 的 font 、fillStyle、strokeStyle、lineWidth 的情况。
优化:根据绘制样式进行分组,批量绘制相同样式的图形,可以根据自身项目不同的情况做分组,个人使用较多的还是fillStyle。
局部更新 canvas
只对变更的 canvas 部分进行更新渲染,避免频繁地绘制大范围的内容:
clip: canvas 原生api,代价较为昂贵,但是简单易用;
boundBox + clearReact: 手动实现一个boundBox方法获取2d状态下图形的矩形边框,擦除和绘制都在这个区域内操,具体的实现可以参考 three.js box2d 的源码。
canvas 拆分
简单来说就是动静分离、层叠分离,如游戏背景可以与人物移动分离成两个canvas。
拆分并不是越多越好,维护canvas的开销也是需要考虑的。
离屏渲染
使用一个不可见(或是屏幕外)的 Canvas 对即将渲染的内容的某部分进行提前绘制,然后频繁地将屏幕外图像渲染到主画布上,避免重复生成该部分内容的步骤。
使用 OffscreenCanvas API 将离屏 Canvas 完整地运行在 worker 线程,有效减少主线程的性能开销。
避免浮点运算
canvas都是像素拆分都是整数,浏览器会为了抗锯齿对浮点数进行额外的运算,使用整数可以减少开销。
合理选择绘制API
canvas 绘制圆(arc)相比绘制矩形(rect)会消耗更多的性能,arc 每次绘制都要开启、闭合路径,而 rect 则直接绘制。在图形体积较小时,圆和矩形的视觉差距不大,可以使用性能更好的rec替换。