本小节是作者在开发过程中的一些粗浅经验与心得,都是经过具体事务处理后得到的教训,在此作一个总结,希望在开发实践中能够对读者有一些帮助。
1. 回答两个问题
还记得我们在第三章的Cavnas API小节向大家提的问题吗?
问题1:Canvas是不是透明的?
问题2:Canvas可不可以互相堆叠在一起?
在这里,我们要向一些已经学习了Canvas的读者们郑重提醒,在设计应用的时候,做足相关的技术理论准备工作,在一些基本的问题上花费一些时间,可以避免以后的重大缺陷和创造更加令人惊艳的效果。
这里,我们给出两个问题的答案:
- Canvas是没有背景色的,也就是说:Canvas是默认完全透明的,就像一块玻璃,而不是一块画布,如果非要理解成画布,那也应该是一块透明的塑料画布。
- Canvas在浏览器中纳入了DOM体系,是可以通过CSS 的Position和z-index值来操作任意多个Canvas堆叠在一起,合成效果,就像操作DIV标签一样。
结论:
根据上面两个答案,我们可以提出一个解决方案:当一个Canvas有过多的绘图任务时,效率会越来越低下。我们可以利用Canvas的透明属性和可以堆叠的特征,剥离相关资源和绘图任务,使作品的结构更加清晰,更加适合团队开发。
2. 避开浮点运算
当你的作品中有大量的浮点运算时,效率必然就会下降很多。我们建议:对要计算的值 先取整后再绘图,可以提高效率。这个小技巧在开发手机游戏或者需要大量渲染而对图形质量的要求可以降低的话,是非常有用的。
3. 资源载入与双缓冲问题
在网上常见的Canvas开发教程中,有些代码看上去没有问题,但是实际上不能在工程中直接使用的,这样的代码只有教育意义。
例如:
var ctx = mycanvas.getContext('2d');
var img = new image();
img.src=”1.png”;
ctx.drawImage(img);
在开发Canvas游戏时,常常会有很多资源需要载入,而且反复DrawImage时,如果没有做好资源载入控制将带来非常严重的显示问题(有时候显示,有时候不显示,在动画情况下会闪烁)。因此,我们建议在开发中使用数组或JSON等序列化所有的游戏资源,然后将相关资源一次性建立相关对象,存储在内存中,以便在下载完毕后,频繁读取时不必再去读图片资源,而是直接读内存中的资源,这样一来,一是速度快了许多,二是稳定性有了保障。
而通常情况下,我们把上面所说的这种方法称之为双缓冲绘图,也是游戏开发中最常见的方法。
示例代码:
var game = {};//游戏对象
game.imgResource = [
{id: 'a1', src: 'image/1.jpg'},
{id: 'a2', src: 'image/2.png'},
{id: 'a3', src: ' image/3.png'}
];//游戏对象中的图片资源对象序列化
var source={};//资源对象
source.imgs = {};//开辟图片内存缓冲区
for(var i ==0;i<game.imgResource.length;i++){//开始资源预载
source.loadImg(game.imgResource[i].id, game.imgResource[i].src);
}
source.loadImg = function(id, src) {//全部存入内存中
source.imgs[id] = new Image();//开始存入内存中
source.imgs[id].src = src;//指定资源路径
};
当图片全部载入到内存后,就可以从内存中读取相关的资源进行绘图操作了,而这时,已不会发生图片闪烁,忽隐忽现等问题了。这是Canvas新手需要注意的问题。
4. 图片抗锯齿的原理及办法
图片的抗锯齿处理可能是很多人都关心的内容,一些有经验的开发人员都在实际开发和项目中遇到了这些问题而苦于没有解决办法。本小节将对部分浏览器中Canvas里绘入图片后旋转产生锯齿的现象进行解释、处理和优化,达到抗锯齿后的完美效果(此问题,在非Safari的移动设备上尤其突出)。
概念简介
在介绍抗锯齿前,我们需要读者大概了解几个概念常识:
- 位图(又叫:点阵图、光栅图、像素图),简要的讲,就是以密集的点组成的图,从图形说的角度说,图像是光的复杂显示,我们可以把一幅图像的每个点都看成是一个光的格子,我们通常称之为光栅,所以又有光栅图片一说。[ 这里主要向非专业计算机图形学人员进行概念的口头普及,若有不严谨处,望方家们见谅。]
- 矢量图(又叫:向量图),简要的讲,就是以各种复杂信息描述而成的图,记录的内容是点、线、面、颜色等信息。这样的图,放大,缩小,都不会造成锯齿,也不会失真,是最适合做电子地图、标识等显示的方案。
- 锯齿,就是位图在某些角度上显示的时候,图片边缘出现毛边,类似锯齿一般,放大后仔细观察,就像是一个一个小格子。
- 抗锯齿:就是让锯齿消失,在各种角度下,位图都平滑显示。
锯齿问题的产生
关于抗锯齿,有些读者可能已经查阅了大量的资料,研究了大量的算法,但是却一无所获,在这里,我们给出一个旋转的抗锯齿案例,简单而易操作,希望能抛砖引玉。
我们在一次工程案例中,需要动态旋转一个圆形图片,想当然的以为可以非常简单的实现,结果在测试中发现锯齿很严重。除在移动设备iPhone,iTouch,iPad上使用Safari看起来比较平滑能够接受之外,在其它任何浏览器上测试时,都有锯齿。
锯齿问题的分析
在计算机图像处理中,一幅位图的平面角度动态变化(旋转)显示是比较有意思的,图片在旋转后,每个位图中的点都发生了变化,例如:
图的面积是:[x0,y0]->[xn,yn],图中某个点在未旋转前是:1,1,经旋转角度13度后,变化如下:
1->7*(Math.PI * 13/180)
这个点变成了1.588
经过这次计算,每个点都不能互相对应上,照成有的点溢出在不应该显示的地方,而有的点由于位置不对,不再显示。而使用软件渲染器的时候就算是双线性插值也不能完全保证(特别是有弧线的)能够一一对应。这就是锯齿产生的原因。
锯齿问题的解决办法
既然我们找到了原因,我们就可以想出解决办法。一般来说解决的办法有至少四种:
- 使用硬件渲染(以苹果公司移动设备中的safari为代表)。
- 使用向量图(如果可以替代的话)。
- 图像以2的次方放大,尽可能的保证渲染器采样到原图而不是插值。
- 以复杂的抗锯齿算法来解决这个问题(计算量大,而且非专人员看不懂,更别提使用了)。
看到这里,我想读者们已经明白了,我们要采用的,就是第三种办法。
假设:要在设备中显示一幅200*200的图,并旋转。
则:
使原图增大一倍,达到400*400,然后再将此图多设1个像素,相当于手动添加中心围绕点(最关键的一步),为什么要添加1个像素的中心围绕点呢?这是为了方便渲染器计算用的。一幅400*400像素的图,如果旋转起来,半径是多少?答:不是200,而是约等于199.5。因此,我们给此图再手动加一个空白像素,使旋转的半径为整数:200。增加了中心旋转点后,一方面提升了计算效率,另一个方面,使渲染器的每个点都能够比较准确的落在原点上。因此,源图的尺寸达到401*401,至此,锯齿问题能够得到圆满的解决。
本小节是抗锯齿技巧中的一个,具体实例代码在第五章HTML5和移动互联网开发部分中的指南针例子里,请读者自行参考。
5. 粒子系统的使用
什么是粒子系统?很难直接描述。但是大家都看到过火焰,火焰的状态是没有固定形势的,在空气中,火焰翻腾跳跃。如何用HTML5的Canvas去描绘一幅火焰呢?
办法有两种 :一种是用多幅图片,帧式快速播放,可以给人一种火焰在跳动的感觉。但是这样生成的效果非常简单,火焰的形态也很枯躁。另一种 ,就是我们这里要介绍的粒子系统,完全依赖计算和编程,模拟出火焰的形态,给人强烈的真实的感受。
粒子系统原来是计算机3维图形学中的技术,像火焰、爆炸、流水、云等,都可以模拟的非常真实。
我们在实际的工作中,限于移动设备的环境,我们不能进行大量的计算,因此,建议读者在实际开发中,做好衡量和取舍,采取适当的粒子数量。既保证效果,又能让程序高效的运行。
有一点是可以我们是确定的:采用了适应粒子系统效果的游戏和应用,可以节省大量的图片资源、载入时间短、帧数高、速度快。
因此,我们建议有大量图片去堆出效果的游戏和应用采用粒子系统,事半功倍。本书作为基础进阶资料,不对粒子系统进行全面的讲解,有兴趣的读者可以去研究相关的资料。