canvas玩多了后,就会自动的要开始考虑性能问题了。怎么优化canvas的动画呢?
【使用缓存】
使用缓存也就是用离屏canvas进行预渲染了,原理很简单,就是先绘制到一个离屏canvas中,然后再通过drawImage把离屏canvas画到主canvas中。可能看到这很多人就会误解,这不是写游戏里面用的很多的双缓冲机制么?
其实不然,双缓冲机制是游戏编程中为了防止画面闪烁,因此会有一个显示在用户面前的画布以及一个后台画布,进行绘制时会先将画面内容绘制到后台画布中,再将后台画布里的数据绘制到前台画布中。这就是双缓冲,但是canvas中是没有双缓冲的,因为现代浏览器基本上都是内置了双缓冲机制。所以,使用离屏canvas并不是双缓冲,而是把离屏canvas当成一个缓存区。把需要重复绘制的画面数据进行缓存起来,减少调用canvas的API的消耗。
众所周知,调用canvas的API很消耗性能,所以,当我们要绘制一些重复的画面数据时,妥善利用离屏canvas对性能方面有很大的提升,可以看下下面的DEMO
可以看到两个的性能完全不一样,下面分析一下代码:为了实现每个圈的样式,所以绘制圈圈时我用了循环绘制,如果没用启用缓存,当页面的圈圈数量达到一定时,动画每一帧就要大量调用canvas的API,要进行大量的计算,这样再好的浏览器也会被拖垮啦。
ctx.save(); var j=0; ctx.lineWidth = borderWidth; for(var i=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作缓存区。
var ball = 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"); this.cacheCtx = this.cacheCanvas.getContext("2d"); var num = getZ(this.r/borderWidth); for(var j=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中保存起来。
cache:function(){ this.cacheCtx.save(); var j=0; this.cacheCtx.lineWidth = borderWidth; for(var i=1;i<this.r;i+=borderWidth){ this.cacheCtx.beginPath(); this.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就只有这么一句话:
ctx.drawImage(this.cacheCanvas , this.x-this.r , this.y-this.r);
跟之前的for循环绘制比起来,实在是快太多了。所以当需要重复绘制矢量图的时候或者绘制多个图片的时候,我们都可以合理利用离屏canvas来预先把画面数据缓存起来,在接下来的每一帧中就能减少很多没必要的消耗性能的操作。
【使用requestAnimationFrame】
这个就不具体解释了,估计很多人都知道,这个才是做动画的最佳循环,而不是setTimeout或者setInterval。直接贴出兼容性写法:
window.RAF = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {window.setTimeout(callback, 1000 / 60); }; })();
【避免浮点运算】
虽然javascript提供了很方便的一些取整方法,像Math.floor,Math.ceil,parseInt,但是,国外友人做过测试,parseInt这个方法做了一些额外的工作(比如检测数据是不是有效的数值,parseInt 甚至先将参数转换成了字符串!),所以,直接用parseInt的话相对来说比较消耗性能,那怎样取整呢,可以直接用老外写的很巧妙的方法了:
rounded = (0.5 + somenum) | 0; rounded = ~~ (0.5 + somenum); rounded = (0.5 + somenum) << 0;
运算符不懂的可以直接戳:http://www.w3school.com.cn/js/pro_js_operators_bitwise.asp 里面有详细解释
还有一些其他注意事项,我就不一一列举了,因为谷歌上一搜也挺多的。我这也算是一个给自己做下记录,主要是记录缓存的用法。想要提升canvas的性能最主要的还是得注意代码的结构,减少不必要的API调用,在每一帧中减少复杂的运算或者把复杂运算由每一帧算一次改成数帧算一次。同时,上面所述的缓存用法,我因为贪图方便,所以是每个对象一个离屏canvas,其实离屏canvas也不能用的太泛滥,如果用太多离屏canvas也会有性能问题,这个就得自己多写多尝试了。
本人前端小菜,写的不好请见谅。
原文:http://www.cnblogs.com/axes/p/3567364.html