上周為AlloyTeam/JX做了個簡單的官網http://alloyteam.github.com/JX/,當時文檔,demo以及其他的附屬工具都還沒完善,地址就流了出去...
獨立的粒子特效demo 可以看這里 http://hongru.github.com/proj/laro/examples/jxhome/
JX 作為webqq的底層,框架本身怎么樣,我這里暫時不作評論,很多同學對JX官網home頁的opening動畫的實現很感興趣,我這里就簡單說一下實現的思路。應該沒有大家想象中麻煩。
【關於canvas的使用】
home頁的粒子效果 其實是受啟發於 Google 2011年的I/O 大會的opening。只是實現思路可能稍有不同。我知道I/O大會上他們的opening用的是Three.js 來做3D渲染,但是具體的細節我也沒有深究。我這里只是說一下我自己的實現思路。
目前這個例子效果是基於canvas的,其實用dom也是可以實現的。只是擔心大量的dom操作和appendChild,removeChild之類的,對性能和內存的影響蠻大,就沒去做兼容。
實現思路就是:基於canvas的 3D 粒子 旋轉算法 + 依賴imageData 生成 模型數組。
【3D粒子旋轉】
這個部分說麻煩也麻煩,說簡單也很簡單。其實就是兩三個數學公式的事兒。我之前有好幾篇隨筆都是講它的,所以我這里也就不重復贅述了。具體可以參考:
【怎么讓粒子組成指定形狀?】
最開始想到的思路,是自己拼數組出來,類似現在很多html5小游戲的地圖數據 一樣,通過數組中特定的值來表示某一個 具體的位置,比如這個demo(http://hongru.github.com/test/google-clock.html)里面的 由粒子組成的數字,其實最開始就是一組特定的數組,如下:
var NUM = [
'#### ########## ##################### ',
'# # # # ## ## # ## ## # ',
'# # # # ## ## # ## ## # # ',
'# # ##################### ######### ',
'# # ## # # ## # ## # # # ',
'# # ## # # ## # ## # # ',
'#### ######### ######### ######### '
]
由左向右分別是0-9以及冒號。 通過這種數組, # 就代表一個粒子。 這種人工生成 模型 數組的方式還可以處理一些簡單的模型,例如這種 數字,或者少數字母等等。
但是如果想要讓粒子組成一些稍微復雜一些的圖案,人工排列的方式就變得不太靠譜了。
那有什么辦法可以讓它自動生成這種類似的模型數組呢?
canvas的imageData就有用了。我們知道canvas支持像素級別的操作,可以把一個canvas畫布里面每一個像素點都拿出來獨立處理。
假如 ,我們把一個canvas畫布上不透明或者有顏色的所有像素點都找出來,把每一個像素點都當成一個粒子的話,那么豈不是我們在canvas上寫的字,draw的image等等,都可以由很多粒子組成?
比如這個demo(http://hongru.github.com/test/my-text-particle.html):
大家注意這一段代碼:
getImageData: function () {
var imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
for (var x = 0; x < imageData.width; x ++) {
for (var y = 0; y < imageData.height; y ++) {
//var i = 4*(x * imageData.height + y);
var i = 4*(y * imageData.width + x);
if (imageData.data[i + 3] > 128) {
this.place ++;
(this.place%4 == 0) && this.particles.push(new Particle(x, y, this.canvas));
}
}
}
},
我們遍歷canvas的指定區域像素點,把透明度大於 0.5 的點都找出來,當成一個particle來處理。 當然,為了減少並行的粒子數造成的性能問題,我這里 把符合 條件的粒子 又除以了4.
看到這里,我想應該好多同學都明白了怎么讓粒子組成想要的圖形或者字符了吧。沒錯,就是借助圖片,利用imageData取出 想要的點。 然后 記錄下它的位置和顏色,利用這些信息生成自定義的粒子。
當然,這里還有個小技巧,就是關於圖片大小的控制,因為imageData是像素級別的,所以太大圖片,必然導致過多的粒子,我們做這種效果並不需要多精確的形狀,而且太多的粒子在后續渲染上 是個 極大的消耗,所以 通常 控制在 20*20 大小,算比較合適,也就是保持在 400 個粒子左右變化, 在圖形組成和 變換性能上 取一個權衡點。
【最后,關於進程的控制】
這也是一個關鍵點,通常,一個稍有經驗的開發者,有了上面說的幾點知識后,應該就能大致寫出類似的效果了,但是如果涉及到像這種連續幾個不同的動畫 來 轉換的時候, 進程的控制 會讓人覺得頭疼, 因為從一個動畫 進入到下一個動畫的時候,為了降低性能消耗 ,需要把上一次 使用的粒子 請出循環數組, 而是用下一個 動畫需要的 模型數組來變化。另外 我們還需要在 動畫 連續幀變化 的loop 中控制每個動畫過程的時間, 以便到了某個節點 可以進入到下一個 動畫進程。
為了很好的控制進程,簡化編碼思路,我這里引入了一個 【有限狀態機 FSM】的概念。引入這個概念之后,基本上相似的動畫,每個動畫進程可以獨立成 一個 status來處理,互相基本沒有耦合,只用關心自己即可。而且代碼都具有相似的結構
進入狀態-update更新-重繪draw-狀態轉換條件transition- 離開狀態leave

這很好的幫助了我們簡化思路。便利的控制整個進程。
關於有限狀態機 更多的信息和 源碼以及使用方法, 可以參照源碼:
https://github.com/hongru/Laro/blob/master/src/game/fsm.js
https://github.com/hongru/Laro/blob/master/src/game/state.js
【后記】
思路大致就是這樣,當然,細節方面還有很多需要注意的地方,如果感興趣的同學不妨一點點嘗試,從3D 旋轉算法開始, 完成一個類似的特效。 對編碼思路 以及 簡易的圖形學算法 應該會有些幫助。
大家晚安 : )
