javascript
《JavaScript高效图形编程(修订版)》——第2章 DHTML基础 2.1创建DHTML sprite
本節(jié)書摘來自異步社區(qū)《JavaScript高效圖形編程(修訂版)》一書中的第2章,第2.1節(jié),作者:【美】Raffaele Cecco著,更多章節(jié)內(nèi)容可以訪問云棲社區(qū)“異步社區(qū)”公眾號查看
第2章 DHTML基礎(chǔ)
在HTML5 Canvas、SVG和Flash等現(xiàn)代瀏覽器技術(shù)的背景下,DHTML今天看起來有點(diǎn)過時。不過,就像龜兔賽跑中的龜,當(dāng)更令人激動的方法不能保證可用的情況下,DHTML總是那個更可靠的方案。
實(shí)際上,很多時候你只需要DHTML就夠了;使用其他方法往往是因?yàn)殚_發(fā)者“想要”而不是“需要”。休閑游戲、圖像縮放和許多其他特效都不需要借助其他“強(qiáng)力工具”就能完美實(shí)現(xiàn)。jQuery這樣的庫還能使其操作起來更簡單。熟練的DOM操作技術(shù)加上一點(diǎn)點(diǎn)想法就能保證DHTML圖形的快速和流暢。
在本章,我們將用vanilla JavaScript和DHTML開發(fā)一個快速sprite系統(tǒng)。出于兼容性考慮,我們會避免使用語言的最新特性,而集中于核心JavaScript的有效使用。
2.1 創(chuàng)建DHTML sprite
在計算機(jī)圖形學(xué)中,sprite是可以用軟件控制移動的二維比特圖對象。在三維多邊形圖形學(xué)之前,視頻游戲幾乎無一例外的使用sprite來生成可移動的角色。如今,移動設(shè)備上的休閑游戲和其他的用戶界面效果等,引起了sprite圖形的復(fù)興。你可以用DHTML來模擬sprite功能。下面章節(jié)中,我們將創(chuàng)建一個用于不同應(yīng)用的DHTMLSprite對象。盡管創(chuàng)建sprite效果有更新、更快的方法,如HTML5 Canvas元素,但普通的DHTML可以提供不錯的瀏覽器兼容性,在許多情況下作為Adobe Flash的替代方案是完全可行的。
提示:
本章中的sprite和CSS sprite是有區(qū)別的。CSS sprite是一個流行的Web設(shè)計技術(shù),指的是僅通過改變HTML元素的CSS背景位置,使得元素顯示一個大背景圖像的一小部分,一般用于實(shí)現(xiàn)動畫效果。在計算機(jī)圖形學(xué)術(shù)語中,這叫做動態(tài)紋理坐標(biāo)。本章提到的sprite,還是取其原意:一個移動的圖形對象。同時我們也將用到CSS sprite技術(shù)來改變其圖像。
DHTMLSprite應(yīng)該足夠靈活以用在不同應(yīng)用中,并提供下列功能:
- 用一個簡單的函數(shù)調(diào)用和圖像索引(index)來改變其圖像(動畫)。
- 在內(nèi)部管理自身的DOM元素。
- 不改變DOM的情況下隱藏和顯示自己。
- 移除其DOM元素并進(jìn)行必要的清理。
2.1.1 圖像動畫
沒有動畫的sprite很沒勁,因此我們需要一個簡潔的方法來改變sprite中所用的圖像。盡管img元素似乎是一個很明顯的選擇,但它需要對每個動畫幀載入不同的圖像文件。有一個更好的辦法可以使用少量的圖像文件,而處理多個sprite圖像。
CSS的background-position(背景位置)屬性使得HTML元素(如一個div)可以顯示圖像的一小部分。因此一個大圖像可以作為許多小sprite圖像的容器。要使用這些sprite圖像,我們必須定義background-position屬性在div內(nèi)的水平和垂直位移,以及寬和高。但這種動畫方式并不直接,而需要技巧。最好是通過簡單的索引就能引用到sprite圖像。比如在圖2-1中,組成一個齒輪動畫的5幅圖像可以用索引0、1、2、3和4表示。而第一個正方形用索引5表示,依此類推。
我們需要將索引轉(zhuǎn)化為容器圖像內(nèi)的像素位移。一種方法是手動創(chuàng)建一個表格來記錄sprite圖像索引和對應(yīng)的像素位移。盡管這個方法很有效,但手動輸入和更新這些位移將很枯燥。更好的方法是通過計算得到這些位移。
將索引轉(zhuǎn)換為水平和垂直像素位移只需要很簡單的算術(shù)。在圖2-1中,容器圖像是256像素寬,每個sprite圖像(底層除外)是64像素的正方形。像素位移可以用JavaScript這樣計算:
注意計算出的值是負(fù)數(shù)。想象div元素是在對準(zhǔn)第一個齒輪圖像(索引為0)、寬與高各64像素的正方形。為了顯示索引為1的下一張圖像,容器圖像必須向左移64像素(負(fù)水平位移)。如果要顯示索引為4的最后一個齒輪圖像,容器圖像必須向上移64像素(負(fù)垂直位移)。
如何處理不同大小的sprite呢?在圖2-1中,在容器圖像底部有一些更小的32像素寬、高的sprite圖像。
決定像素位移的計算和之前一樣,不同的是sprite大小改為32像素:
考慮到現(xiàn)在的sprite大小是32像素,圖2-1中第一個32像素的sprite圖像(底行第一個小黑圈)的索引為32。只要sprite圖像的邊緣坐標(biāo)是它們大小的倍數(shù),就可以使用索引計算的方式。
提示:
圖2-1中的容器圖像是一個32位PNG文件,支持百萬顏色和一個用于透明度的alpha通道。不過,32位PNG不適用于IE6,因?yàn)橥该鲄^(qū)域會變成不透明的灰色。一個解決方案是將圖像存為8位的調(diào)色板PNG。這可以在IE6中正確顯示,不過半透明區(qū)域會完全消失并顯示粗糙的邊緣。
2.1.2 封裝和畫圖抽象
將所有DOM操作細(xì)節(jié),封裝在DHTMLSprite中,隱藏在使用它的應(yīng)用之外,會使代碼更簡單更易維護(hù);應(yīng)用可以集中于應(yīng)用邏輯而不是畫圖細(xì)節(jié)。由于應(yīng)用邏輯和畫圖細(xì)節(jié)的分離,將應(yīng)用轉(zhuǎn)為另一個畫圖方法如HTML5 Canvas元素或SVG變得更簡單,甚至可以使應(yīng)用程序根據(jù)瀏覽器能力選擇合適的畫圖方法。
2.1.3 最小化DOM插入和刪除
重復(fù)的增刪和銷毀DOM元素對性能會有不利的影響。為了降低性能影響,可以初始化一個隱藏的sprite列表。當(dāng)需要sprite時,你可以將其從列表中取出并使其可見,而不是真的在DOM中插入新的東西。當(dāng)sprite不再需要時,你可以將其隱藏并放回列表中。在DHTMLSprite中提供一個show和hide方法將使應(yīng)用實(shí)現(xiàn)這項(xiàng)技術(shù)。
如果要永久地移除一個DHTMLSprite,應(yīng)移去其DOM元素并進(jìn)行相關(guān)的其他清理工作。
2.1.4 sprite代碼
與其將若干單獨(dú)的參數(shù)傳給sprite,不如將所有設(shè)置參數(shù)放入叫做params的對象傳入。除了避免參數(shù)次序的麻煩之外,還使從DHTMLSprite繼承的其他對象,可以將它們自己的設(shè)置參數(shù)加入params中。任何使用params的對象都可以忽略跟它不相關(guān)的參數(shù)。表2-1顯示了params對象中的參數(shù)。
下面,我們將params屬性復(fù)制為局部變量。通過局部變量訪問參數(shù)比通過params對象的屬性要快。如此定義的局部變量是私有的,只能從DHTMLSprite內(nèi)的方法訪問。
接下來,我們在params.$drawTaget指定的DOM元素后加上一個sprite div元素。$element保存了一個對sprite div的引用。變量和屬性名前的$符號用做提醒它們指向jQuery對象。elemStyle直接引用了sprite div的style屬性,用于快速更新其CSS屬性。
現(xiàn)在我們要給sprite div元素設(shè)置初始CSS屬性。因?yàn)槲覀冎贿M(jìn)行一次初始化,因此可以使用方便的jQuery css()函數(shù),盡管這也許不是改變屬性最快的方式。
下面我們要在that中創(chuàng)建并保存一個DHTMLSprite對象。它包含了所有的sprite方法,注意that的方法可以訪問前面定義的局部變量。這個that對象創(chuàng)建了一個閉包,它能永久訪問前面DHTMLSprite函數(shù)里定義的變量。
draw方法更新sprite div元素的位置:
changeImage()方法改變顯示的sprite圖像。將索引轉(zhuǎn)為像素位移的方法和前面描述的一樣,但有些小的優(yōu)化:
- 局部變量mathFloor()指向Math.floor()函數(shù),我們通過前者來調(diào)用后者。
- index變量只乘一次。
然后,我們定義隱藏、顯示和移除sprite div元素的方法:
2.1.5 一個簡單的sprite應(yīng)用程序
下面是一個基本的HTML頁面,它初始化并顯示了兩個sprite。
為了創(chuàng)建sprite,我們需要一個包含初始化參數(shù)的對象:
下面創(chuàng)建兩個sprite。因?yàn)閮蓚€sprite大小相等并使用同一個DOM畫圖區(qū)域,所以不需要改變?nèi)魏螀?shù)。第一個sprite使用默認(rèn)索引值0,而第二個sprite的圖像索引值為5。
最后畫出這兩個sprite。圖2-2顯示了輸出結(jié)果。
這個應(yīng)用中沒有移動也沒有動畫,讓我們在下一個例子中“動”起來。
2.1.6 一個更動態(tài)的sprite應(yīng)用程序
下面的應(yīng)用展示了sprite的存在價值:動畫和移動。之前我們畫了兩個sprite,而沒有控制它們移動。這個例子中我們定義一個新對象:bouncySprite,一個會反彈的DHTMLSprite。實(shí)現(xiàn)方法之一是在bouncySprite中創(chuàng)建一個DHTMLSprite,并將其作為單獨(dú)的實(shí)例控制。更簡潔的方法是讓bouncySprite繼承所有DHTMLSprite的能力,并添加自己額外的能力。在JavaScript中這種繼承和增強(qiáng)很簡單:
為了提高速度,我們用局部變量保存設(shè)置參數(shù)。這里的params對象也包含DHTMLSprite的參數(shù),但這些和bouncySprite無關(guān)。表2-2顯示了傳入的參數(shù)。
animIndex保存了當(dāng)前動畫圖像索引:
我們在that中創(chuàng)建和引用一個DHTMLSprite。params對象包含了其設(shè)置參數(shù)。
接著給that引用的DHTMLSprite實(shí)例加一個moveAndDraw方法,實(shí)際上就是創(chuàng)建一個bouncySprite實(shí)例:
通過增加xDir和yDir變量來移動sprite的x和y位置:
下面的代碼根據(jù)xDir方向?qū)nimIndex變量進(jìn)行增或減,接著用取余操作(%)將其維持在?4到+4之間。如果animIndex是負(fù)的,糾正到對應(yīng)的正索引。
接著檢查bouncySprite是否超過了maxX和maxY定義的范圍。如果超過,對移動的方向取負(fù),使bouncySprite彈回。
更新bouncySprite動畫索引,并將其畫到新位置:
返回在that中引用的bouncySprite實(shí)例,供應(yīng)用程序使用:
定義了bouncySprite對象后,我們可以初始一些對象,并在setInterval()或settTimeout()控制下調(diào)用它們的 moveAndDraw()方法。更好的方法是創(chuàng)建一個對象可以初始化和處理任意數(shù)量的bouncySprite。這個對象可以叫做bouncyBoss。bouncyBoss可以傳入兩個參數(shù),如表2-3所示。
創(chuàng)建指定數(shù)目的bouncySprite,并放入bouncys數(shù)組中。每個bouncySprite給一個隨機(jī)起始位置和移動方向(xDir和yDir),并根據(jù)$drawTarget的寬和高計算最大范圍。
現(xiàn)在我們定義moveAll方法,它調(diào)用了bouncys數(shù)組中每個bouncySprite的moveAndDraw方法。每次移動,它創(chuàng)建一個setTimeOut來調(diào)用自己,實(shí)現(xiàn)連續(xù)的循環(huán)。
下面是使用新bouncyBoss對象的頁面布局:
一個bouncyBoss創(chuàng)建了50個bouncySprite對象,并連續(xù)調(diào)用它們的moveAndDraw方法。圖2-3顯示了輸出結(jié)果。
總結(jié)
以上是生活随笔為你收集整理的《JavaScript高效图形编程(修订版)》——第2章 DHTML基础 2.1创建DHTML sprite的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《Adobe Photoshop大师班:
- 下一篇: 《AngularJS深度剖析与最佳实践》