HTML5 canvas improve performance in several ways

Brief introduction

HTML5 canvas originated in an Apple (Apple) experiment has now become a widely supported 2D web in the fast mode graphics (2D  load immediate the MODE Graphic ) standards. Many developers now use it to achieve a number of multimedia projects, as well as eye-catching visual games. However, with the increasing complexity of the applications we build, we will inevitably encounter the so-called performance issues.

Many ways to optimize the performance of canvas already exists, but no article would organize these methods and systems are analyzed. The purpose of this paper is to organize these methods, so as to consolidate the developers who are more easy to understand, digest, absorb resources. This article covered basic optimization method is applicable to all environments computer graphics (computer graphics environments), and the specific method of optimizing the canvas. With the canvas which may achieve specific update methods vary for optimization of the canvas. In particular, when the browser developers realized when the canvas GPU acceleration, we explore some optimization may not seem particularly effective, these cases we marked out in specific places.

Please note that this article is not to discuss the focus of HTML5 canvas usage. If you want to know the specific usage canvas HTML5 Rocks site can be found in the article related to the canvas . Such as Dive into HTML5 chapter  and the MDN the Tutorial .

Performance Testing

In order to deal with rapidly changing Canvas HTML5, JSPerf ( jsperf.com ) test proved that every method we mentioned in the text are currently still effective. JSPerf is a very useful web applications, web developers can use this program to write JavaScript performance test cases. Each test case concerns only the result of a particular aspect of your attempts to reach (for example, clear the canvas), each such test case contains a number of different ways to achieve the same results. There is significant JSPerf for a short time as many runs each method, did not give a statistic on the number of iterations per second. The mean score higher performance.

Viewers can open JSPerf performance test page in your browser, and lets JSPerf to standardized test results are stored in Browserscope ( broserscope.org ) medium. Because the optimization techniques mentioned in this article have been backed up to the results of JSPerf, so you can re-run view the latest information to determine whether the corresponding method is also effective. I have written a small helper application ( Helper applicatin ) to the results of the tests to chart embedded in the whole article.

Performance test results in this article with a specific browser versions have a very important relationship. Since we do not know to run your browser with the operating system on what is more important is not to know whether or not HTML5 canvas was hardware accelerated when you make these tests. You can use about the Chrome browser address bar: gpu command to see if Chrome's HTML5 canvas is hardware accelerated.

 

1.PRE-RENDER TO AN OFF-SCREEN CANVAS

 We often encounter in the game to write a similar situation when redrawing the object in a plurality of consecutive frames. In this in case, you can obtain significant performance gains by pre-rendered scene of most of the objects. I.e., rendering the temporary pre-rendered images of one or more temporary canvas is not displayed on the screen, and then these invisible canvas rendered visible to the canvas as an image. For computer graphics more familiar friends should all know, this technique has also been called Run the display List .

For example, suppose you redraw Mario running at 60 frames per second. You can either frame redraw his hat, mustache, and "M" on each can also pre-render Mario before running the animation.

No case of pre-rendered:

[javascript]  view plain copy
  1. // canvas, context are defined  
  2. function render() {  
  3.   drawMario (Context);  
  4.   requestAnimationFrame(render);  
  5. }  

Pre situation rendered:

[javascript]  view plain copy
  1. var m_canvas = document.createElement('canvas');  
  2. m_canvas.width = 64;  
  3. m_canvas.height = 64;  
  4. var m_context = m_canvas.getContext(‘2d’);  
  5. drawMario (m_context);  
  6. function render() {  
  7.   context.drawImage(m_canvas, 0, 0);  
  8.   requestAnimationFrame(render);  
  9. }  

RequestAnimationFrame on the use of a detailed described in the subsequent section. The following shows the icon shows the use of pre-rendering technology brings performance improvement. (From JSPerf ):

When rendering operations (e.g. drawmario the example above) a large overhead when the method is very effective. In which the text is very resource-intensive rendering operation is a good example. From the table below you can see the use of pre-rendered strong operating performance improvement brought about. (From JSPerf ):

However, the example we can see observe upper loose pre-rendered (pre-renderde loose) poor performance. When to use pre-rendered, we want to make sure that the temporary canvas ready to render exactly fit the size of your picture, or too big canvas will lead us for the performance increase is to copy a large canvas to another canvas of operation bring performance loss offset.

In the above test a small compact relatively canvas:

[javascript]  view plain copy
  1. can2.width = 100;  
  2. can2.height = 40;  

As loose canvas will result in poor performance:

[javascript]  view plain copy
  1. can3.width = 300;  
  2. can3.height = 100;  

 

2.BATCH CANVAS CALLS TOGETHER

Because the drawing is a costly operation, therefore, with a long set of instructions loaded state machine loading the drawing, and then fall into the video buffer is written to all. This will be better efficiency.

For example, when you need to create a painting of lines containing path when all the lines and then call with a draw than separately draw every line is much more efficient:

[javascript]  view plain copy
  1. or (var i = 0; i < points.length - 1; i++) {  
  2.   var p1 = points[i];  
  3.   var p2 = points[i+1];  
  4.   context.beginPath ();  
  5.   context.moveTo(p1.x, p1.y);  
  6.   context.lineTo(p2.x, p2.y);  
  7.   context.stroke();  
  8. }  

By drawing a path comprising a plurality of lines we can get better performance:

[javascript]  view plain copy
  1. ontext.beginPath ();  
  2. for (var i = 0; i < points.length - 1; i++) {  
  3.   var p1 = points[i];  
  4.   var p2 = points[i+1];  
  5.   context.moveTo(p1.x, p1.y);  
  6.   context.lineTo(p2.x, p2.y);  
  7. }  
  8. context.stroke();  

This approach also applies to HTML5 canvas. For example, when we draw a complex path, into the path of all the points will be much more efficient (than separately rendering each part JSPerf ):

Note, however, that there is an important exception to the canvas is: Ruoyu member drawn object bounding box contained a small (e.g., vertical lines or horizontal lines), then the lines may separate rendering It will be more effective ( JSPerf ):

 

3.AVOID UNNECESSARY CANVAS STATE CHANGES

HTML5 canvas element is implemented on top of a state machine. State machine may track information such as fill, stroke-style and the composition of the current path of the previous points and the like. When trying to optimize graphics performance, we tend to focus only on graphics rendering on. In fact, manipulating the state machine can lead to performance overhead.

例如,如果你使用多种填充色来渲染一个场景,按照不同的颜色分别渲染要比通过canvas上的布局来进行渲染要更加节省资源。为了渲染一副条纹的图案,你可以这样渲染:用一种颜色渲染一条线条,然后改变颜色,渲染下一条线条,如此反复:

[javascript]  view plain copy
  1. for (var i = 0; i < STRIPES; i++) {  
  2.   context.fillStyle = (i % 2 ? COLOR1 : COLOR2);  
  3.   context.fillRect(i * GAP, 0, GAP, 480);  
  4. }  

也可以先用一种颜色渲染所有的偶数线条再用另外一种染色渲染所有的基数线条:

[javascript]  view plain copy
  1. context.fillStyle = COLOR1;  
  2. for (var i = 0; i < STRIPES/2; i++) {  
  3.   context.fillRect((i*2) * GAP, 0, GAP, 480);  
  4. }  
  5. context.fillStyle = COLOR2;  
  6. for (var i = 0; i < STRIPES/2; i++) {  
  7.   context.fillRect((i*2+1) * GAP, 0, GAP, 480);  
  8. }  

下面的性能测试用例分别用上边两种方法绘制了一副交错的细条纹图案(jsperf):

正如我们预期的,交错改变状态的方法要慢的多,原因是变化状态机是有额外开销的。

 

4.RENDER SCREEN DIFFERENCES ONLY, NOT THE WHOLE  NEW STATE

这个很容易理解,在屏幕上绘制较少的东西要比绘制大量的东西节省资源。重绘时如果只有少量的差异你可以通过仅仅重绘差异部分来获得显著的性能提升。换句话说,不要在重绘前清除整个画布。:

[javascript]  view plain copy
  1. context.fillRect(0, 0, canvas.width, canvas.height);  

跟踪已绘制部分的边界框,仅仅清理这个边界之内的东西:

[javascript]  view plain copy
  1. 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一个在另一个的上边:

[javascript]  view plain copy
  1. <canvas id="bg" width="640" height="480" style="position: absolute; z-index: 0">  
  2. </canvas>  
  3. <canvas id="fg" width="640" height="480" style="position: absolute; z-index: 1">  
  4. </canvas>  

相对于仅仅有一个canvas的情况来讲,这个方法的优势在于,当我们需要绘制或者清理前景canvas时,我们不需要每次都修改背景 canvas。如果你的游戏或者多媒体应用可以分成前景和背景这样的情况,那么请考虑分贝渲染前景和背景来获取显著的性能提升。下面的图表比较了只有一个 canvas的情况和有前景背景两个canvas而你只需要清理和重绘前景的情况(jsperf):

你可以用相较慢的速度(相对于前景)来渲染背景,这样便可利用人眼的一些视觉特性达到一定程度的立体感,这样会更吸引用户的眼球。比如,你可以在每一帧中渲染前景而仅仅每N帧才渲染背景。

注意,这个方法也可以推广到包含更多canvas曾的复合canvas。如果你的应用利用更多的曾会运行的更好时请利用这种方法。

 

6.AVOID SHADOWBLUR

跟其他很多绘图环境一样,HTML5 canvas允许开发者对绘图基元使用阴影效果,然而,这项操作是相当耗费资源的。

[javascript]  view plain copy
  1. context.shadowOffsetX = 5;  
  2. context.shadowOffsetY = 5;  
  3. context.shadowBlur = 4;  
  4. context.shadowColor = 'rgba(255, 0, 0, 0.5)';  
  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的操作往往是不可取的。如果你必须这样做的话有两种方法可供选择:调用

[javascript]  view plain copy
  1. context.clearRect(0, 0, width, height)  

或者使用 canvas特定的一个技巧

[javascript]  view plain copy
  1. canvas.width = canvas.width  

在书写本文的时候,cleaRect方法普遍优越于重置canvas宽度的方法。但是,在某些情况下,在Chrome14中使用重置canvas宽度的技巧要比clearRect方法快很多(jsperf):

请谨慎使用这一技巧,因为它很大程度上依赖于底层的canvas实现,因此很容易发生变化,欲了解更多信息请参见 Simon Sarris 的关于清除画布的文章

 

8.AVOID FLOATING POINT COORDINATES

HTML5 canvas 支持子像素渲染(sub-pixel rendering),而且没有办法关闭这一功能。如果你绘制非整数坐标他会自动使用抗锯齿失真以使边缘平滑。以下是相应的视觉效果(参见Seb Lee-Delisle的关于子像素画布性能的文章

如果平滑的精灵并非您期望的效果,那么使用 Math.floor方法或者Math.round方法将你的浮点坐标转换成整数坐标将大大提高运行速度(jsperf):

为使浮点坐标抓换为整数坐标你可以使用许多聪明的技巧,其中性能最优越的方法莫过于将数值加0.5然后对所得结果进行移位运算以消除小数部分。

[javascript]  view plain copy
  1. // With a bitwise or.  
  2. rounded = (0.5 + somenum) | 0;  
  3. // A double bitwise not.  
  4. rounded = ~~ (0.5 + somenum);  
  5. // Finally, a left bitwise shift.  
  6. rounded = (0.5 + somenum) << 0;  

两种方法性能对比如下(jsperf):

9.OPTIMIZE YOUR ANIMATIONS WITH ‘REQUESTANIMATIONFRAME’

相对较新的 requeatAnimationFrame API是在浏览器中实现交互式应用的推荐标准。与传统的以固定频率命令浏览器进行渲染不同,该方法可以更友善的对待浏览器,它会在浏览器可用的时候使其来 渲染。这样带来的另外一个好处是当页面不可见的时候,它会很聪明的停止渲染。

requestAnimationFrame调用的目标是以60帧每秒的速度来调用,但是他并不能保证做到。所以你要跟踪从上一次调用导线在共花了多长时间。这看起来可能如下所示:

[javascript]  view plain copy
  1. var x = 100;  
  2. var y = 100;  
  3. var lastRender = new Date();  
  4. function render() {  
  5.   var delta = new Date() - lastRender;  
  6.   x += delta;  
  7.   y += delta;  
  8.   context.fillRect(x, y, W, H);  
  9.   requestAnimationFrame(render);  
  10. }  
  11. render();  

注意requestAnimationFrame不仅仅适用于canvas 还适用于诸如WebGL的渲染技术。

在书写本文时,这个API仅仅适用于Chrome,Safari以及Firefox,所以你应该使用这一代码片段

MOST MOBILE CANVAS IMPLEMENTATION ARE SLOW

让我们来讨论一下移动平台。不幸的是在写这篇文章的时候,只有IOS 5.0beta 上运行的Safari1.5拥有GPU加速的移动平台canvas实现。如果没有GPU加速,移动平台的浏览器一般没有足够强大的CPU来处理基于 canvas的应用。上述的JSperf测试用例在移动平台的运行结果要比桌面型平台的结果糟糕很多。这极大的限制了跨设备类应用的成功运行。

CONCLUSION

j简要的讲,本文较全面的描述了各种十分有用优化方法以帮助开发者开发住性能优越的基于HTML5 canvas的项目。你已经学会了一些新的东西,赶紧去优化你那令人敬畏的创造吧!如果你还没有创建过一个应用或者游戏,那么请到Chrome Experiment 和Creative JS看看吧,这里能够激发你的灵感。

 

REFFERENCE

 

(Original link: http://www.html5rocks.com/en/tutorials/canvas/performance/ )

Reproduced in: https: //my.oschina.net/zhepama/blog/265034

Guess you like

Origin blog.csdn.net/weixin_34292959/article/details/91927353