打造高大上的Canvas粒子動畫

原文鏈接:https://isux.tencent.com/canvas-particle-animation.html

首先來看下我們準備要做的粒子動畫效果是怎麼樣的~

是這樣:

01_1

或者是這樣:

02_1

甚至是這樣:

03_1

很酷炫!

那如何去實現類似上面的粒子動畫甚至根據自己的喜好去做更多其他軌跡的動畫呢~請看下面詳細的講解。

技術選擇

因爲粒子數量很多,而且涉及到圖像像素處理,所以這裏使用Canvas是不二選擇。

 

注意,以下演示的代碼只是關鍵代碼,重點在於解決思路。

一、繪製粒子輪廓圖

首先要在canvas畫布上繪製一個由粒子組成的輪廓圖,記錄下每一個粒子的座標,這樣纔能有後續的動畫。

1. 創建一個<canvas>元素,並獲取Canvas畫布渲染上下文

QQ截圖20160815165511

< canvas>是一個雙標籤元素,通過width和height的值來設置畫布的大小。至於ctx(畫布渲染上下文),可以理解爲畫布上的畫筆,我們可以通過畫筆在畫布上隨心所欲的繪製圖案。如果瀏覽器不支持canvas會直接顯示<canvas>標籤中間自己設定的文字。當然<canvas>標籤中間也可以是一張當不支持canvas時需要替換顯示的圖片。

 

2. 使用canvas的圖像操作API繪製圖像

繪製圖像的關鍵API及參數說明:

QQ截圖20160815170315

引用MDN上的一張圖會比較清晰的看出每個參數的作用:

Canvas_drawimage

drawImage就是把一個image對象或者canvas上(甚至是video對象上的的每一幀)指定位置和尺寸的圖像繪製到當前的畫布上。而在我們的需求中,是要把整個圖像繪製到畫布中。

QQ截圖20160815172209

對應瀏覽器看到的效果

QQ截圖20160815171603

 

3. 獲取圖像的像素信息,並根據像素信息重新繪製出粒子效果輪廓圖

canvas有一個叫getImageData的接口,通過該接口可以獲取到畫布上指定位置的全部像素的數據:

QQ截圖20160815172701

把獲取的imageData輸出到控制檯可以看到,imageData包含三個屬性:

QQ截圖20160815173205

其中,width、height是讀取圖像像素信息完整區域的寬度和高度,data是一個Uint8ClampedArray類型的一維數組,包含了整個圖片區域裏每個像素點的RGBA的整型數據。這裏必須要理解這個數組所保存像素信息的排序規則,請看下圖描述的data數組:

1467789256_70_w1128_h288

每一個色值佔據data數組索引的一個位置,一個像素有個4個值(R、G、B、A)佔據數組的4個索引位置。根據數列規則可以知道,要獲取第n個位置(n從1開始)的R、G、B像素信息就是:Rn = (n-1)*4 ,Gn = (n-1)*4+1 ,Bn = (n-1)*4+2  ,so easy~  當然,實際上圖像是一個包括image.height行,image.width列像素的矩形而不是單純的一行到結束的,這個n值在矩形中要計算下:

QQ截圖20160816123536

由於一個像素是帶有4個索引值(rgba)的,所以拿到圖像中第i行第j列的R、G、B、A像素信息就是 Rij = [(j-1)*imageData.width + (i-1)]*4 ,Gij = [(j-1)*imageData.width + (i-1)]*4 + 1,Bij = [(j-1)*imageData.width + (i-1)]*4 + 2,Aij = [(j-1)*imageData.width + (i-1)]*4 + 3 。每個像素值都可以拿到了!

接下來就要把圖像的粒子化輪廓圖畫出來了。那麼,怎麼做這個輪廓圖呢,我們先讀取每個像素的信息(用到上面的計算公式),如果這個像素的色值符合要求,就保存起來,用於繪製在畫布上。另外,既然是做成粒子的效果,我們只需要把像素粒子保存一部分,展示在畫布上。

具體做法是,設定每一行和每一列要顯示的粒子數,分別是cols和rows,一個粒子代表一個單元格,那麼每個單元格的的寬高就是imageWidth/cols和imageHeight/rows,然後循環的判斷每個單元格的第一個像素是否滿足像素值的條件,如果滿足了,就把這個單元格的座標保存到數組裏,用作後續繪製圖案用。

關鍵參考代碼:

QQ截圖20160816122517

用完整代碼做出的demo及效果

QQ截圖20160815180446

至此,粒子輪廓圖已經制作完成。

 

二、製作粒子動畫

製作粒子動畫分兩種:

一種是粒子漂浮類,這種比較簡單,只需要隨機的改變每個粒子的位置值,然後一直執行setInterval或者requestAnimationFrame重繪畫布即可,具體的效果因人喜好而去設定,就不具體講解了,做了個簡單的粒子漂浮的例子

另一種是粒子的軌跡動畫,這個相對複雜一些。這裏要介紹的是每個粒子按照指定的軌跡在指定的時間內做位移,最終匯聚成指定圖案的動畫效果(也就是文章一開始的動效),要做成這類動畫效果需要解決兩個問題:一個是動畫軌跡,另外一個是每個粒子執行動畫的時機。

粒子動畫軌跡

動畫位移的軌跡,最常見的就是單位時間內改變固定的位移值,從而達到動畫效果。但要做到炫酷的效果依賴這種單調固定的位移肯定是不行的。所以位移可以依賴緩動函數去做到單位時間內改變不一樣的位移值,從而達到特別的效果。

製作緩動效果有兩種方法:

一種是自己設定好控制點,然後通過貝塞爾曲線公式來計算每個單位時間的座標值。

引用了wikipedia裏面的圖:

Bézier_2_bigBézier_3_big

上面兩個圖都是在繪製一條特定曲線,可以看出二次曲線需要一個特定控制點P1,三次曲線需要兩個特定控制點P1和P2來確定一條曲線,高階曲線甚至需要更多的控制點來確定曲線軌跡。

求曲線的公式是根據德卡斯特里奧算法計算得來的,直接上公式。

二次曲線對應的公式:

1467797904_100_w688_h73

三次曲線對應的公式:

1467797947_2_w1012_h86

從公式可以看出,只要確定控制點座標、起始座標和終點座標後,就可以確定了一條曲線,然後就可以根據曲線公式求出每個時刻t對應的位置值B(t)。

當然使用這種方法需要自己去制定控制點座標,計算也比較複雜,實現起來很繁瑣。沒事,我們還有別的辦法確定曲線。

 

另外一種方法就是使用已有的緩動函數,不需要自己制定控制點,這裏推薦出名的Tween算法的緩動函數,用其中一個緩動函數來介紹下參數值,其他緩動函數所傳的參數值是一樣的:

QQ截圖20160815183507

是不是覺得很熟悉?對沒錯,jquery用的動畫擴展插件easing.js就是Tween算法的緩動函數。有了這現成的緩動函數,就可以制定粒子的起始點、終點(終點就是圖案本身的座標位置)以及動畫執行持續時間來做我們要的效果。

關鍵參考代碼:

QQ截圖20160815183728

根據參考代碼做出一個效果

04

嗯,動畫效果是有了,但總感覺不太對勁。。。唔,仔細觀察一下,是圖案動畫執行太過整體了,沒有明顯的顆粒動畫效果,這就引出粒子動畫的另一個關鍵點,粒子執行動畫的時機。

 

粒子執行動畫的時機

要讓粒子效果比較明顯,那就不能讓動畫效果執行太過整體了,需要讓圖案上每個粒子有不同的時間間隔啓動,根據一定的規律交錯的執行動畫。這裏的粒子啓動間隔有兩種,一種是每一行粒子執行時間間隔,要讓每一行的粒子啓動時間有規律錯開;另外一種是每一行粒子之間啓動時間隨機的錯開,這樣執行的粒子動畫纔會有一種層次感和每個粒子有獨立動畫的顆粒感。看下加了粒子啓動時間間隔之後的效果對比

5

比上面不加粒子啓動時間間隔的效果好多了。

 

嗯,介紹差不多就是這樣了,如果上面介紹的方法還是解決不了問題的話,還有辦法。。。我把粒子動畫效果和Tween的緩動函數一起封裝了一下。直接配置一下就可以用了。 用法就是創建一個帶有id的canvas,設定好寬度和高度,引入particle.min.js,然後配置一下參數即可, demo

QQ截圖20160816104119

只有canvasId、imgUrl、cols、rows是必填的,其他參數都是根據需要自己選填。  ( ͡° ͜ʖ ͡°)✧

 

本文部分圖片引用來自


發佈了23 篇原創文章 · 獲贊 8 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章