前言
最近一直在做一些HTML5 Canvas加速方面的事情,HTML5已经出来相当长一段时间,各浏览器厂商也基本上已经支持!从目前来看,HTML5的大规模普及还是雷声大,雨点小;但从长远来看,HTML5由于其诸多优点,比如本文提到到Canvas元素支持,将会逐渐成为主流,特别是在复杂场景应用中! 本文主要从目前HTML5 Canvas 在实现动画(游戏)中遇到的部分痛点入手,尝试提供一些相应的解决办法和思路!
痛点
性能问题:目前性能问题HTML5 Canvas的性能问题在Android中表现得尤为突出
耗电,CPU,内存占用等问题:由于动画,游戏的特殊性,耗电等一直是此类应用中比较突出的问题,并非HTML5 Canvas特有的问题
品质问题:由于HTML 5是偏Web的一类应用,因此在使用者的固有印象中,一直认为HTML Canvas只能做一些比较初级的动画和游戏,比较复杂的一些游戏,往往就觉得HTML Canvas无法胜任,转而寻求Native的解决方案
兼容性/适配性问题:这一方面是由于平台造成,比如Android平台的碎片化;另一方面是浏览器的分裂实现,造成同一套标准多个不同版本的不同实现,进而造成开发者要兼顾各种各样的浏览器!
调试问题:由于移动终端的特殊性,目前在移动终端上调试 JavaScript应用还是比较困难的一件事情!
思路
针对上述问题,下面提供了一些方法和思路,可以根据不同的场景和应用做参考:
1. 性能
1). JavaScript 语言层面优化
(1). 设计优化
A. 文字绘制使用缓存Canvas
说明:在动画(游戏)场景中,文字使用一般占比都比较少,但文字的绘制比起图片等来说,其实是最复杂,也最耗时间的!因此大多游戏如果不是出于有文字交互,或动态文字绘制等需求的话,一般会直接使用位图来代替!但也有很多场合需要直接绘制文字,对应于Canvas 的Api主要就是fillText! 在这种情况下,如果直接fillText到显示的Canvas上,会严重降低fps!
方法:创建一个不可见的缓冲Canvas,文字首先绘制到此Canvas上,当需要显示的时候,通过drawImage(canvas...)绘制到显示的Canvas上,这样可有效避免直接调用 fillText 带来的性能降低问题
例子:cocos2d-js,phaser等游戏框架中对文字的处理主要用了此方法
B. 语言层面的设计优化还有很多方式,可以从下面两个链接中找到更多方法:
http://www.cnblogs.com/rhcad/archive/2012/11/17/2774794.html
http://www.cnblogs.com/iamzhanglei/archive/2011/11/29/2267868.html
(2). 工具辅助优化
语言层面不仅可以直接从具体的设计上优化,也可以使用辅助工具来帮助排除js性能瓶颈。
2).Runtime层面优化:
(1). 渲染优化
由于各个平台的实现机制不同,或者同一平台下(比如Android)下各个版本提供的浏览器渲染内核也不尽相同,因此造成HTML5 Canvas在许多情况下渲染性能严重降低!鉴于此,许多直接从Runtime层面来解决性能问题的方案也应运而生!并且取得了不错的效果!比如:
A. 使用GPU硬件加速
方法:直接通过Opengl来实现硬件加速,提高渲染速度,
例子:CocoonJS,Intel XDK等;
2. 耗电
由于动画(游戏)的特殊性,需要不停渲染显示,相对于其他应用来说,会消耗比较多的电量。
目前为止,并没有太多减少耗电量的有效办法。下面的建议可能会有一些帮助:
(1). 在游戏流畅度不受影响的情况下,尽量降低fps, 由于fps越高,渲染越频繁,势必消耗越多的电量;但fps达到60帧/s以后,就已经达到显卡的渲染能力上限,因此过渡提高fps只会增加耗电量;
3. 提高画面品质
(1). 提高平滑度
除了画面的视觉效果外,在动画或游戏中,比较重要的就是画面过渡要流畅,没有生硬感,眼睛不会感觉到刺痛!要改善这些,可以使用下面的方法:
A. 两个画面之间补帧的方式
通过一定的补帧算法(比如 easeIn ,easeOut,透明度变化的)等方式,让前一个画面以微小的变化不停过渡到另一个画面,达到改善过渡的效果!这里比较重要的一点就是在以前的javascript动画实现中,通常通过setTimeout 或setInterval来实现时间控制,推荐使用requestAnimationFrame来控制动画的播放,可以实现比较精确的时间控制,效率较setTimeout高,下面这篇文章给出了一些动画的过渡算法例子
http://www.zhangxinxu.com/wordpress/2013/09/css3-animation-requestanimationframe-tween-动画算法/
(2). 特效:在Runtime支持的情况下,可以使用部分特效效果来提高画面品质,比如3D
4. 兼容性处理
由于浏览器内核分裂和平台碎片化问题(比如Android),导致Api在各个浏览器和Android各个版本下实现不尽一样,比较典型的比如bind在android 某些版本上没有实现,请求动画帧方法requestAnimationFrame在各浏览器内核上的实现不同等。这给开发者带来很大烦恼,要针对不同的平台和浏览器做不同的实现。对于此问题,可以自己根据不同版本做不同的判断来处理,更好的处理版本可以使用一种叫做polyfills的方法,通过这种方式,可以不用担心自己所使用的api在不同的平台上找不到。polyfills这个术语比较拗口,详细可以看一看下面这篇文章,对polyfills做了比较全面的总结!
https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills
下面是一个针对requestAnimationFrame和bind的polyfill实现例子:
// Function.bind
// source: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
// window.requestAnimationFrame
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
5. 调试
目前在移动平台上调试javascript相对PC环境来说,相对比较麻烦,下文总结了目前可以使用的一些调试方式,可以参考:
http://blog.csdn.net/alexwang1983/article/details/16882163
该文章来自于阿里巴巴技术协会(ATA)
作者:许凡