本文主介绍canvas的性能优化策略

关于性能问题,实际上,针对于类似管理系统这样的项目而言,不需要关注太多,也没那必要.但是对于图形编程类的项目而言,则是至关重要的指标.关乎生死

先点名canvas的耗时因素

  1. 图形的数量
  2. 图形的大小。
  3. 高质量、大文件的图片(画在画布上)

图形编程类的项目,关注性能问题的原因,在于交互体验。比如我们废了半天劲写了一个web端的类cs射击游戏,当我兴奋的准备往前走两步时,发现页面直接给我来了个帧数级别的慢动作,江湖说法叫做”卡成狗”,用户体验几乎为0。这种完全不能玩的应用,如同垃圾。

我们首先来定义一下, 什么叫做卡?

卡顿原理可以解释为,每一帧js主线程执行的事情过多,导致帧内时间没来得及干完,即当前帧耗时超过分配的帧时间。那么什么是”分配的帧时间?60hz举例,一秒钟60帧,那么一帧的时间大约在16.7ms.所以,如果在一帧内的耗时超过了16.7ms,就会出现视觉层面的”卡”。目前主流的屏幕基本都是60hz,也就是显示器,以每秒60次的频率闪频,但我们人眼无法察觉。

在实践的项目中,我们遇到了一个这样的警告:
图片描述
白话就是,“大哥,您在一帧时间内做的事情太多了,浏览器处理不过来了”。对项目的影响就是,拖拽和缩放视图导致不会很流畅。所以,为了解决卡的问题,我们该做点啥呢?

  1. canvas隔离。
    canvas画布的更新,是以该节点为基础的,所以将需要重排或者重绘频率较高的图形,用单独的canvas隔离开,这样避免不必要的重绘成本。
  2. 事件的防抖节流处理。绘制的动作,不要频繁触发, 该屏蔽的操作一律屏蔽。
  3. 避免浮点运算

    虽然javascript提供了很方便的一些取整方法,像Math.floor,Math.ceil,parseInt,但是,国外友人做过测试,parseInt这个方法做了一些额外的工作(比如检测数据是不是有效
    的数值,parseInt 甚至先将参数转换成了字符串!),所以,直接用parseInt的话相对来说比较消耗性能。

  4. 局部渲染
    第一点中我们说过,canvas更新时以canvas节点为基础的,实际上这话说的不够严谨,因为我们可以有方法可以局部操作渲染,来避免这种更新方式,但需要做些额外的设置。
  5. Offscreen canvas离屏渲染
    将渲染的工作交给worker完成,不会阻塞当前主线程的任务执行。

场景举例:
在画布中我们创建了很多个固定位置的circle,同时,随着鼠标移动的事件,我们需要在当前鼠标出显示一个圆,就是该圆随着鼠标移动而移动。
方案一: 暴力重绘。
每次触发鼠标移动事件,暴力清空整个画布,然后再重新绘制静止圆和鼠标圆。
该方案数据量小的时候,没问题,很丝滑,如下:
图片描述

但是,数据量一上来,那就是灾难。
图片描述

方案二: 就是我们上面提到的局部重绘。
图片描述

其核心优化点在于,不需要每次都操作所有的path,而是尽可能少的绘制。极大抬高性能。当然这里我们只是简单的选取了一块区域直接清空,等同于做了个橡皮擦的功能。找机会详细讲解(如四叉树碰撞检测,这也是重点)。