然而,需要注意的是,对于canvas来说存在一个重要的例外情况:若欲绘制的对象的部件中含有小的边界框(例如,垂直的线条或者水平的线条),那么单独的渲染这些线条或许会更加有效(jsperf):
3.AVOID UNNECESSARY CANVAS STATE CHANGES
HTML5 canvas元素是在一个状态机之上实现的。状态机可以跟踪诸如fill、stroke-style以及组成当前路径的previous points等等。在试图优化绘图性能时,我们往往将注意力只放在图形渲染上。实际上,操纵状态机也会导致性能上的开销。
例如,如果你使用多种填充色来渲染一个场景,按照不同的颜色分别渲染要比通过canvas上的布局来进行渲染要更加节省资源。为了渲染一副条纹的图案,你可以这样渲染:用一种颜色渲染一条线条,然后改变颜色,渲染下一条线条,如此反复:
for (var i = 0; i < STRIPES; i++) { context.fillStyle = (i % 2 ? COLOR1 : COLOR2); context.fillRect(i * GAP, 0, GAP, 480); } 也可以先用一种颜色渲染所有的偶数线条再用另外一种染色渲染所有的基数线条: context.fillStyle = COLOR1; for (var i = 0; i < STRIPES/2; i++) { context.fillRect((i*2) * GAP, 0, GAP, 480); } context.fillStyle = COLOR2; for (var i = 0; i < STRIPES/2; i++) { context.fillRect((i*2+1) * GAP, 0, GAP, 480); } 下面的性能测试用例分别用上边两种方法绘制了一副交错的细条纹图案( jsperf ):
正如我们预期的,交错改变状态的方法要慢的多,原因是变化状态机是有额外开销的。
4.RENDER SCREEN DIFFERENCES ONLY, NOT THE WHOLE NEW STATE
这个很容易理解,在屏幕上绘制较少的东西要比绘制大量的东西节省资源。重绘时如果只有少量的差异你可以通过仅仅重绘差异部分来获得显著的性能提升。换句话说,不要在重绘前清除整个画布。:
context.fillRect(0, 0, canvas.width, canvas.height); 跟踪已绘制部分的边界框,仅仅清理这个边界之内的东西:
context.fillRect(last.x, last.y, last.width, last.height); 下面的测试用例说明了这一点。该测试用例中绘制了一个穿过屏幕的白点( jsperf ):
如果您对计算机图形学比较熟悉,你或许应该知道这项技术也叫做“redraw technique”,这项技术会保存前一个渲染操作的边界框,下一次绘制前仅仅清理这一部分的内容。
这项技术也适用于基于像素的渲染环境。这篇名为JavaScript NIntendo emulator tallk的文章说明了这一点。
5.USE MUTIPLE LAYERED CANVASES FOR COMPLEX SCENES
我们前边提到过,绘制一副较大的图片代价是很高昂的因此我们应尽可能的避免。除了前边讲到的利用另外得不可见的canvas进行预渲染外,我们也可以叠在一起的多层canvas。图哦你的过利用前景的透明度,我们可以在渲染时依靠GPU整合不同的alpha值。你可以像如下这么设置,两个绝对定位的canvas一个在另一个的上边:<canvas id="bg" width="640" height="480" style="position: absolute; z-index: 0"> </canvas> <canvas id="fg" width="640" height="480" style="position: absolute; z-index: 1"> </canvas> 相对于仅仅有一个canvas的情况来讲,这个方法的优势在于,当我们需要绘制或者清理前景canvas时,我们不需要每次都修改背景canvas。如果你的游戏或者多媒体应用可以分成前景和背景这样的情况,那么请考虑分贝渲染前景和背景来获取显著的性能提升。下面的图表比较了只有一个canvas的情况和有前景背景两个canvas而你只需要清理和重绘前景的情况( jsperf ):
你可以用相较慢的速度(相对于前景)来渲染背景,这样便可利用人眼的一些视觉特性达到一定程度的立体感,这样会更吸引用户的眼球。比如,你可以在每一帧中渲染前景而仅仅每N帧才渲染背景。
注意,这个方法也可以推广到包含更多canvas曾的复合canvas。如果你的应用利用更多的曾会运行的更好时请利用这种方法。
6.AVOID SHADOWBLUR
跟其他很多绘图环境一样,HTML5 canvas允许开发者对绘图基元使用阴影效果,然而,这项操作是相当耗费资源的。context.shadowOffsetX = 5; context.shadowOffsetY = 5; context.shadowBlur = 4; context.shadowColor = 'rgba(255, 0, 0, 0.5)'; context.fillRect(20, 20, 150, 100); 下面的测试显示了绘制同一场景使用何不使用阴影效果所带来的显著的性能差异( jsperf ):
7.KNOW VARIOUS WAYS TO CLEAR THE CANVAS
因为HTML5 canvas 是一种即时模式(immediate mode)的绘图范式(drawing paradigm),因此场景在每一帧都必需重绘。正因为此,清楚canvas的操作对于 HTML5 应用或者游戏来说有着根本的重要性。
正如在 避免 canvas 状态变化的一节中提到的,清楚整个canvas的操作往往是不可取的。如果你必须这样做的话有两种方法可供选择:调用
context.clearRect(0, 0, width, height) 或者使用 canvas特定的一个技巧