首页 > 编程 > HTML > 正文

浅谈利用缓存来优化HTML5 Canvas程序的性能

2020-03-24 18:24:57
字体:
来源:转载
供稿:网友
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.lineWidth=borderWidth; for(vari=1;i this.r;i+=borderWidth){ 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 num;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复制内容到剪贴板
varj=0; this.cacheCtx.lineWidth=borderWidth; for(vari=1;i this.r;i+=borderWidth){ 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复制内容到剪贴板
div canvasid='cas'width= 800 height= 600 浏览器不支持canvas /canvas div '2d'), borderWidth=2, Balls=[]; 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 num;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(); } } functiongetZ(num){ varrounded; rounded=(0.5+num)|0; //Adoublebitwisenot. rounded=~~(0.5+num); //Finally,aleftbitwiseshift. rounded=(0.5+num) 0; returnrounded; } ball.prototype={ paint:function(ctx){ if(!this.useCache){ ctx.save(); varj=0; ctx.lineWidth=borderWidth; for(vari=1;i this.r;i+=borderWidth){ ctx.beginPath(); ctx.strokeStyle=this.color[j]; ctx.arc(this.x,this.y,i,0,2*Math.PI); ctx.stroke(); j++; } ctx.restore(); }else{ ctx.drawImage(this.cacheCanvas,this.x-this.r,this.y-this.r); } }, cache:function(){ this.cacheCtx.save(); varj=0; this.cacheCtx.lineWidth=borderWidth; for(vari=1;i this.r;i+=borderWidth){ 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(); }, move:function(){ this.x+=this.vx; this.y+=this.vy; if(this.x (canvas.width-this.r)||this.x this.r){ thisthis.x=this.x this.r?this.r:(canvas.width-this.r); this.vx=-this.vx; } if(this.y (canvas.height-this.r)||this.y this.r){ thisthis.y=this.y this.r?this.r:(canvas.height-this.r); this.vy=-this.vy; } this.paint(ctx); } } varGame={ init:function(){ for(vari=0;i 1000;i++){ varb=newball(getRandom(0,canvas.width),getRandom(0,canvas.height),getRandom(-10,10),getRandom(-10,10),true) Balls.push(b); } }, update:function(){ ctx.clearRect(0,0,canvas.width,canvas.height); for(vari=0;i Balls.length;i++){ Balls[i].move(); } }, loop:function(){ var_this=this; this.update(); RAF(function(){ _this.loop(); }) }, start:function(){ this.init(); this.loop(); } } window.RAF=(function(){ returnwindow.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(callback){window.setTimeout(callback,1000/60);}; })(); returnGame; }(); functiongetRandom(a,b){ returnMath.random()*(b-a)+a; } window.onload=function(){ testBox.start(); } /script /body /html

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表