《JavaScript高效图形编程(修订版)》——第2章 DHTML基础 2.1创建DHTML sprite-阿里云开发者社区

开发者社区> 异步社区> 正文

《JavaScript高效图形编程(修订版)》——第2章 DHTML基础 2.1创建DHTML sprite

简介: 定义了bouncySprite对象后,我们可以初始一些对象,并在setInterval()或settTimeout()控制下调用它们的 moveAndDraw()方法。更好的方法是创建一个对象可以初始化和处理任意数量的bouncySprite。
+关注继续查看

本节书摘来自异步社区《JavaScript高效图形编程(修订版)》一书中的第2章,第2.1节,作者:【美】Raffaele Cecco著,更多章节内容可以访问云栖社区“异步社区”公众号查看

第2章 DHTML基础

在HTML5 Canvas、SVG和Flash等现代浏览器技术的背景下,DHTML今天看起来有点过时。不过,就像龟兔赛跑中的龟,当更令人激动的方法不能保证可用的情况下,DHTML总是那个更可靠的方案。

实际上,很多时候你只需要DHTML就够了;使用其他方法往往是因为开发者“想要”而不是“需要”。休闲游戏、图像缩放和许多其他特效都不需要借助其他“强力工具”就能完美实现。jQuery这样的库还能使其操作起来更简单。熟练的DOM操作技术加上一点点想法就能保证DHTML图形的快速和流畅。

在本章,我们将用vanilla JavaScript和DHTML开发一个快速sprite系统。出于兼容性考虑,我们会避免使用语言的最新特性,而集中于核心JavaScript的有效使用。

2.1 创建DHTML sprite

在计算机图形学中,sprite是可以用软件控制移动的二维比特图对象。在三维多边形图形学之前,视频游戏几乎无一例外的使用sprite来生成可移动的角色。如今,移动设备上的休闲游戏和其他的用户界面效果等,引起了sprite图形的复兴。你可以用DHTML来模拟sprite功能。下面章节中,我们将创建一个用于不同应用的DHTMLSprite对象。尽管创建sprite效果有更新、更快的方法,如HTML5 Canvas元素,但普通的DHTML可以提供不错的浏览器兼容性,在许多情况下作为Adobe Flash的替代方案是完全可行的。

提示:
本章中的sprite和CSS sprite是有区别的。CSS sprite是一个流行的Web设计技术,指的是仅通过改变HTML元素的CSS背景位置,使得元素显示一个大背景图像的一小部分,一般用于实现动画效果。在计算机图形学术语中,这叫做动态纹理坐标。本章提到的sprite,还是取其原意:一个移动的图形对象。同时我们也将用到CSS sprite技术来改变其图像。
DHTMLSprite应该足够灵活以用在不同应用中,并提供下列功能:

  • 用一个简单的函数调用和图像索引(index)来改变其图像(动画)。
  • 在内部管理自身的DOM元素。
  • 不改变DOM的情况下隐藏和显示自己。
  • 移除其DOM元素并进行必要的清理。

2.1.1 图像动画
没有动画的sprite很没劲,因此我们需要一个简洁的方法来改变sprite中所用的图像。尽管img元素似乎是一个很明显的选择,但它需要对每个动画帧载入不同的图像文件。有一个更好的办法可以使用少量的图像文件,而处理多个sprite图像。

CSS的background-position(背景位置)属性使得HTML元素(如一个div)可以显示图像的一小部分。因此一个大图像可以作为许多小sprite图像的容器。要使用这些sprite图像,我们必须定义background-position属性在div内的水平和垂直位移,以及宽和高。但这种动画方式并不直接,而需要技巧。最好是通过简单的索引就能引用到sprite图像。比如在图2-1中,组成一个齿轮动画的5幅图像可以用索引0、1、2、3和4表示。而第一个正方形用索引5表示,依此类推。

我们需要将索引转化为容器图像内的像素位移。一种方法是手动创建一个表格来记录sprite图像索引和对应的像素位移。尽管这个方法很有效,但手动输入和更新这些位移将很枯燥。更好的方法是通过计算得到这些位移。

将索引转换为水平和垂直像素位移只需要很简单的算术。在图2-1中,容器图像是256像素宽,每个sprite图像(底层除外)是64像素的正方形。像素位移可以用JavaScript这样计算:
screenshot

注意计算出的值是负数。想象div元素是在对准第一个齿轮图像(索引为0)、宽与高各64像素的正方形。为了显示索引为1的下一张图像,容器图像必须向左移64像素(负水平位移)。如果要显示索引为4的最后一个齿轮图像,容器图像必须向上移64像素(负垂直位移)。
screenshot

如何处理不同大小的sprite呢?在图2-1中,在容器图像底部有一些更小的32像素宽、高的sprite图像。

决定像素位移的计算和之前一样,不同的是sprite大小改为32像素:

screenshot

考虑到现在的sprite大小是32像素,图2-1中第一个32像素的sprite图像(底行第一个小黑圈)的索引为32。只要sprite图像的边缘坐标是它们大小的倍数,就可以使用索引计算的方式。

提示:
图2-1中的容器图像是一个32位PNG文件,支持百万颜色和一个用于透明度的alpha通道。不过,32位PNG不适用于IE6,因为透明区域会变成不透明的灰色。一个解决方案是将图像存为8位的调色板PNG。这可以在IE6中正确显示,不过半透明区域会完全消失并显示粗糙的边缘。
2.1.2 封装和画图抽象
将所有DOM操作细节,封装在DHTMLSprite中,隐藏在使用它的应用之外,会使代码更简单更易维护;应用可以集中于应用逻辑而不是画图细节。由于应用逻辑和画图细节的分离,将应用转为另一个画图方法如HTML5 Canvas元素或SVG变得更简单,甚至可以使应用程序根据浏览器能力选择合适的画图方法。

2.1.3 最小化DOM插入和删除
重复的增删和销毁DOM元素对性能会有不利的影响。为了降低性能影响,可以初始化一个隐藏的sprite列表。当需要sprite时,你可以将其从列表中取出并使其可见,而不是真的在DOM中插入新的东西。当sprite不再需要时,你可以将其隐藏并放回列表中。在DHTMLSprite中提供一个show和hide方法将使应用实现这项技术。

如果要永久地移除一个DHTMLSprite,应移去其DOM元素并进行相关的其他清理工作。

2.1.4 sprite代码
与其将若干单独的参数传给sprite,不如将所有设置参数放入叫做params的对象传入。除了避免参数次序的麻烦之外,还使从DHTMLSprite继承的其他对象,可以将它们自己的设置参数加入params中。任何使用params的对象都可以忽略跟它不相关的参数。表2-1显示了params对象中的参数。
screenshot

下面,我们将params属性复制为局部变量。通过局部变量访问参数比通过params对象的属性要快。如此定义的局部变量是私有的,只能从DHTMLSprite内的方法访问。

screenshot

接下来,我们在params.$drawTaget指定的DOM元素后加上一个sprite div元素。$element保存了一个对sprite div的引用。变量和属性名前的$符号用做提醒它们指向jQuery对象。elemStyle直接引用了sprite div的style属性,用于快速更新其CSS属性。
screenshot

现在我们要给sprite div元素设置初始CSS属性。因为我们只进行一次初始化,因此可以使用方便的jQuery css()函数,尽管这也许不是改变属性最快的方式。

screenshot

下面我们要在that中创建并保存一个DHTMLSprite对象。它包含了所有的sprite方法,注意that的方法可以访问前面定义的局部变量。这个that对象创建了一个闭包,它能永久访问前面DHTMLSprite函数里定义的变量。

screenshot

draw方法更新sprite div元素的位置:

screenshot

changeImage()方法改变显示的sprite图像。将索引转为像素位移的方法和前面描述的一样,但有些小的优化:

  • 局部变量mathFloor()指向Math.floor()函数,我们通过前者来调用后者。
  • index变量只乘一次。

screenshot

然后,我们定义隐藏、显示和移除sprite div元素的方法:

screenshot

2.1.5 一个简单的sprite应用程序
下面是一个基本的HTML页面,它初始化并显示了两个sprite。

screenshot

为了创建sprite,我们需要一个包含初始化参数的对象:

screenshot

下面创建两个sprite。因为两个sprite大小相等并使用同一个DOM画图区域,所以不需要改变任何参数。第一个sprite使用默认索引值0,而第二个sprite的图像索引值为5。

screenshot

最后画出这两个sprite。图2-2显示了输出结果。

screenshot

screenshot

这个应用中没有移动也没有动画,让我们在下一个例子中“动”起来。

2.1.6 一个更动态的sprite应用程序
下面的应用展示了sprite的存在价值:动画和移动。之前我们画了两个sprite,而没有控制它们移动。这个例子中我们定义一个新对象:bouncySprite,一个会反弹的DHTMLSprite。实现方法之一是在bouncySprite中创建一个DHTMLSprite,并将其作为单独的实例控制。更简洁的方法是让bouncySprite继承所有DHTMLSprite的能力,并添加自己额外的能力。在JavaScript中这种继承和增强很简单:

screenshot

为了提高速度,我们用局部变量保存设置参数。这里的params对象也包含DHTMLSprite的参数,但这些和bouncySprite无关。表2-2显示了传入的参数。

screenshot

screenshot

animIndex保存了当前动画图像索引:

screenshot

我们在that中创建和引用一个DHTMLSprite。params对象包含了其设置参数。

screenshot

接着给that引用的DHTMLSprite实例加一个moveAndDraw方法,实际上就是创建一个bouncySprite实例:

screenshot

通过增加xDir和yDir变量来移动sprite的x和y位置:
screenshot

下面的代码根据xDir方向对animIndex变量进行增或减,接着用取余操作(%)将其维持在−4到+4之间。如果animIndex是负的,纠正到对应的正索引。

screenshot

接着检查bouncySprite是否超过了maxX和maxY定义的范围。如果超过,对移动的方向取负,使bouncySprite弹回。

screenshot

更新bouncySprite动画索引,并将其画到新位置:

screenshot

返回在that中引用的bouncySprite实例,供应用程序使用:

screenshot

定义了bouncySprite对象后,我们可以初始一些对象,并在setInterval()或settTimeout()控制下调用它们的 moveAndDraw()方法。更好的方法是创建一个对象可以初始化和处理任意数量的bouncySprite。这个对象可以叫做bouncyBoss。bouncyBoss可以传入两个参数,如表2-3所示。

screenshot

创建指定数目的bouncySprite,并放入bouncys数组中。每个bouncySprite给一个随机起始位置和移动方向(xDir和yDir),并根据$drawTarget的宽和高计算最大范围。

screenshot

现在我们定义moveAll方法,它调用了bouncys数组中每个bouncySprite的moveAndDraw方法。每次移动,它创建一个setTimeOut来调用自己,实现连续的循环。

screenshot

下面是使用新bouncyBoss对象的页面布局:

screenshot

一个bouncyBoss创建了50个bouncySprite对象,并连续调用它们的moveAndDraw方法。图2-3显示了输出结果。

screenshot

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
JavaScript创建对象(四)——组合使用构造函数和原型模式
在JavaScript创建对象(三)——原型模式中,我们阐述了原型模式存在的两个问题:一是没办法通过构造函数初始化对象属性,二是共享引用类型的数据导致数据错乱。
813 0
《JavaScript启示录》——第1章 JavaScript对象 1.1创建对象
JavaScript实际上是一种预包装若干原生对象构造函数的语言。这些构造函数用于生成一些表达特定类型值(如数字、字符串、函数、对象、数组等)的复杂对象,同样,也可以通过Function()对象创建自定义的对象构造函数(例如Person())。
1310 0
Eclipse安装图形JFrame,Jswing编程界面
打开eclipse,选择help--->install new software 来源http://www.cnblogs.com/xiaobo-Linux/p/7954274.html 打开网址 http://www.eclipse.org/windowbuilder/ 点击download 在相对应eclipse版本,link右键复制链接,然后复制到这里   然后下一步安装即可。
1376 0
JavaScript创建对象(三)——原型模式
在JavaScript创建对象(二)——构造函数模式中提到,构造函数模式存在相同功能的函数定义多次的问题。本篇文章就来讨论一下该问题的解决方案——原型模式。
1012 0
JavaScript创建对象(二)——构造函数模式
在JavaScript创建对象(一)—— 工厂模式中留下了一个问题,就是创建一个对象怎么判断一个对象的类型。换句话说使用下面这种方式: function createPerson(name, age, job){ var o = new Object(); o.
808 0
ORACLE 11g新特性--延迟段创建
很多数据库都有存在空表的情况,较多的空表会占用大量的磁盘空间,ORACLE 在11gR2版本推出延迟段创建新特性,所谓延迟段创建,顾名思义就是在创建一张新空表的时候,ORACLE默认不会为这张空表分配段(SEGMENTS),也就是不会为这张空表分配空间,这样就避免了空...
1002 0
+关注
异步社区
异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。
11938
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载