这篇文章主要介绍了浅谈利用缓存来优化HTML5 Canvas程序的性能,并且作者给出了使用缓存以及离屏Canvas后性能较原程序的提升对比,需要的朋友可以参考下
canvas玩多了后,就会自动的要开始考虑性能问题了,
浅谈利用缓存来优化HTML5 Canvas程序的性能
。怎么优化canvas的动画呢?【使用缓存】
使用缓存也就是用离屏canvas进行预渲染了,原理很简单,就是先绘制到一个离屏canvas中,然后再通过drawImage把离屏canvas画到主canvas中。可能看到这很多人就会误解,这不是写游戏里面用的很多的双缓冲机制么?
其实不然,双缓冲机制是游戏编程中为了防止画面闪烁,因此会有一个显示在用户面前的画布以及一个后台画布,进行绘制时会先将画面内容绘制到后台画布中,再将后台画布里的数据绘制到前台画布中。这就是双缓冲,但是canvas中是没有双缓冲的,因为现代浏览器基本上都是内置了双缓冲机制。所以,使用离屏canvas并不是双缓冲,而是把离屏canvas当成一个缓存区。把需要重复绘制的画面数据进行缓存起来,减少调用canvas的API的消耗。
众所周知,调用canvas的API很消耗性能,所以,当我们要绘制一些重复的画面数据时,妥善利用离屏canvas对性能方面有很大的提升,可以看下下面的DEMO
1 、 没使用缓存
2、 使用了缓存但是没有设置离屏canvas的宽高
3 、 使用了缓存但是没有设置离屏canvas的宽高
4 、 使用了缓存且设置了离屏canvas的宽高
可以看到上面的DEMO的性能不一样,下面分析一下原因:为了实现每个圈的样式,所以绘制圈圈时我用了循环绘制,如果没用启用缓存,当页面的圈圈数量达到一定时,动画每一帧就要大量调用canvas的API,要进行大量的计算,这样再好的浏览器也会被拖垮啦。
XML/HTML Code复制内容到剪贴板
ctx.save();
varj=0;
ctx.lineWidth=borderWidth;
for(vari=1;i ctx.beginPath(); ctx.strokeStyle=this.color[j]; ctx.arc(this.x , this.y , i , 0 , 2*Math.PI); ctx.stroke(); j++; } ctx.restore();
所以,我的方法很简单,每个圈圈对象里面给他一个离屏canvas作缓存区。
除了创建离屏canvas作为缓存之外,下面的代码中有一点很关键,就是要设置离屏canvas的宽度和高度,canvas生成后的默认大小是300X150;对于我的代码中每个缓存起来圈圈对象半径最大也就不超过80,所以300X150的大小明显会造成很多空白区域,会造成资源浪费,所以就要设置一下离屏canvas的宽度和高度,让它跟缓存起来的元素大小一致,这样也有利于提高动画性能。上面的四个demo很明显的显示出了性能差距,如果没有设置宽高,当页面超过400个圈圈对象时就会卡的不行了,而设置了宽高1000个圈圈对象也不觉得卡。
XML/HTML Code复制内容到剪贴板
varball=function(x , y , vx , vy , useCache){
this.x= x;
this.y= y;
this.vx= vx;
this.vy= vy;
this.r=getZ(getRandom(20,40));
this.color= [];
this.cacheCanvas=document.createElement("canvas");
thisthis.cacheCtx= this.cacheCanvas.getContext("2d");
this.cacheCanvas.width=2*this.r;
this.cacheCanvas.height=2*this.r;
varnum=getZ(this.r/borderWidth);
for(varj=0;j this.color.push("rgba("+getZ(getRandom(0,255))+","+getZ(getRandom(0,255))+","+getZ(getRandom(0,255))+",1)"); } this.useCache= useCache; if(useCache){ this.cache(); } }
当我实例化圈圈对象时,直接调用缓存方法,把复杂的圈圈直接画到圈圈对象的离屏canvas中保存起来。
XML/HTML Code复制内容到剪贴板
cache:function(){
this.cacheCtx.save();
varj=0;
this.cacheCtx.lineWidth=borderWidth;
for(vari=1;i this.cacheCtx.beginPath(); thisthis.cacheCtx.strokeStyle= this.color[j]; this.cacheCtx.arc(this.r , this.r , i , 0 , 2*Math.PI); this.cacheCtx.stroke(); j++; } this.cacheCtx.restore(); }
然后在接下来的动画中,我只需要把圈圈对象的离屏canvas画到主canvas中,这样,每一帧调用的canvasAPI就只有这么一句话:
XML/HTML Code复制内容到剪贴板
ctx.drawImage(this.cacheCanvas , this.x-this.r , this.y-this.r);
跟之前的for循环绘制比起来,实在是快太多了。所以当需要重复绘制矢量图的时候或者绘制多个图片的时候,我们都可以合理利用离屏canvas来预先把画面数据缓存起来,在接下来的每一帧中就能减少很多没必要的消耗性能的操作。
下面贴出1000个圈圈对象流畅版代码:
XML/HTML Code复制内容到剪贴板