阿里巴巴技术文章:使用 HTML5 Canvas 实现移动平台动画(游戏)的一些痛点和思路

来自: http://yq.aliyun.com/articles/3165?spm=5176.100239.yqblog1.20.nRIiAk

前言

最近一直在做一些HTML5 Canvas加速方面的事情,HTML5已经出来相当长一段时间,各浏览器厂商也基本上已经支持!从目前来看,HTML5的大规模普及还是雷声大,雨点小;但从长远来看,HTML5由于其诸多优点,比如本文提到到Canvas元素支持,将会逐渐成为主流,特别是在复杂场景应用中! 本文主要从目前HTML5 Canvas 在实现动画(游戏)中遇到的部分痛点入手,尝试提供一些相应的解决办法和思路!

痛点

  1. 性能问题:目前性能问题HTML5 Canvas的性能问题在Android中表现得尤为突出

  2. 耗电,CPU,内存占用等问题:由于动画,游戏的特殊性,耗电等一直是此类应用中比较突出的问题,并非HTML5 Canvas特有的问题

  3. 品质问题:由于HTML 5是偏Web的一类应用,因此在使用者的固有印象中,一直认为HTML Canvas只能做一些比较初级的动画和游戏,比较复杂的一些游戏,往往就觉得HTML Canvas无法胜任,转而寻求Native的解决方案

  4. 兼容性/适配性问题:这一方面是由于平台造成,比如Android平台的碎片化;另一方面是浏览器的分裂实现,造成同一套标准多个不同版本的不同实现,进而造成开发者要兼顾各种各样的浏览器!

  5. 调试问题:由于移动终端的特殊性,目前在移动终端上调试 JavaScript应用还是比较困难的一件事情!

从不同关注点做一个简单分类:

思路

针对上述问题,下面提供了一些方法和思路,可以根据不同的场景和应用做参考:

1. 性能

1). JavaScript 语言层面优化

(1). 设计优化

A. 文字绘制使用缓存Canvas

说明:在动画(游戏)场景中,文字使用一般占比都比较少,但文字的绘制比起图片等来说,其实是最复杂,也最耗时间的!因此大多游戏如果不是出于有文字交互,或动态文字绘制等需求的话,一般会直接使用位图来代替!但也有很多场合需要直接绘制文字,对应于Canvas 的Api主要就是fillText! 在这种情况下,如果直接fillText到显示的Canvas上,会严重降低fps!

方法:创建一个不可见的缓冲Canvas,文字首先绘制到此Canvas上,当需要显示的时候,通过drawImage(canvas...)绘制到显示的Canvas上,这样可有效避免直接调用 fillText 带来的性能降低问题

例子:cocos2d-js,phaser等游戏框架中对文字的处理主要用了此方法

B. 语言层面的设计优化还有很多方式,可以从下面两个链接中找到更多方法:

(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 )

作者:许凡

</div>