網頁上各種酷炫的粒子動畫看的人眼花繚亂,實際上原理卻非常簡單。
獲取畫素資訊
首先我們需要畫張圖到Canvas上,這裡因為我懶的扣圖(實際上是不會),就找了張jpeg的白底圖片。實際上用png的透明圖會好很多。
一定要等圖片載入完再畫,這很重要,可以用判斷圖片的complete
屬性和設定onload
方法來等圖片載入完才初始化。
另外繪製圖片是有跨域限制的,本地看的話,要麼你的圖片伺服器支援跨域,要麼你就跑個本地伺服器吧。
var img = new Image();
img.src = "images.jpeg";
if(img.complete){
init()
} else {
img.onload = function(){
init()
}
}複製程式碼
圖片獲取完之後我們就可以開始獲得圖片的畫素資訊了,介面很簡單:
context.getImageData(x,y,width,height)
獲得畫布上指定矩形的畫素資料。
ctx.drawImage(img, sx, sy);
var imgData = ctx.getImageData(sx, sy, imgW, imgH);複製程式碼
拿到畫素資料,不要太簡單好嗎?! 因為這裡我是把圖片畫在畫布中間的,所以起始點不是(0,0),獲得的矩形寬高則為圖片的寬高。
列印出來看看資料是什麼樣子的。
首先返回的是個ImageData
物件,屬性有data
和width,height,這還不是重點,data的型別是個Uint8ClampedArray
(此處一臉蒙逼), 看起來是個二維陣列,陣列裡面一堆的數字是什麼鬼(二臉蒙逼)。
還好有文件:
Uint8ClampedArray
物件是一個8位無符號整數的型別化陣列,裡面的值都是0-255。- 這堆數字代表的是rgba的值,但是每個數字只代表一個,比如說第一個數字0,代表第一個畫素的R值,第二個數字0代表第一個畫素的G值,第三第四個0分別代表第一個畫素的B值和Alpha值,最後出來第一個畫素就是個全透明的顏色,第五個數字就代表第二個畫素的R值了。
實際上,影像是二維的,是由height決定行數,width決定列畫素的行列式。
做粒子動畫首先需要把圖片粒子化,把位置給找準,這樣出來的東西才不會是亂七八糟的,所以我們拿到每個點的位置資訊儲存下來就可以了。
for(var x=0; x<imgData.width; x+=6) {
for(var y=0; y<imgData.height; y+=6) {
var i = (y*imgData.width + x) * 4;
if(imgData.data[i+3] > 128 && imgData.data[i] < 100){
var dot = new Dot(x, y, 2);
dotList.push(dot);
}
}
}複製程式碼
兩重迴圈就不說了,這裡有幾個地方簡單解釋一下:
- 迴圈條件中有個
x+=6
,y+=6
這裡為什麼不是1呢?因為是做粒子化,每個點之間需要有一些空隙才看的出來,所以不用每個點都拿,隔一段距離拿一個點就可以了,這裡的6有點類似於取樣的概念。 - 內側迴圈有個if判斷語句,
[i+3]
指的是畫素中Alpha的值,大於128是用於過濾掉一些透明畫素的。後面那個imgData.data[i] < 100
因為我用的是個白底黑字的jpeg檔案,所以還需要把不像黑色的畫素過濾掉,因為大都只有黑白兩色,所以就簡單判斷了R值小於100,所以就像之前說的,還是用png圖片比較好。 - Dot物件是用來儲存每取樣的個畫素點資訊的,這裡我只儲存了x,y資訊,第三個引數是半徑的值,這裡就直接寫死了2,實際上還可以考慮儲存畫素點的rgba資訊以及半徑做個隨機數。
function Dot(centerX, centerY, radius) {
this.x = centerX;
this.y = centerY;
this.radius = radius;
}複製程式碼
最後一步就是把儲存下來的資訊畫到Canvas上。
function draw(){
var imgW = img.width,
imgH = img.height,
sx = winWidth/2-imgW/2,
sy = winHeight/2-imgH/2;
ctx.clearRect(0, 0, winWidth, winHeight);
ctx.fillStyle = "#000";
for(var i=0; i<dotList.length; i+=1){
curDot = dotList[i];
ctx.save();
ctx.beginPath();
ctx.arc(sx+curDot.x, sy+curDot.y, curDot.radius, 0, 2*Math.PI);
ctx.fill();
ctx.restore();
}
}複製程式碼
最後出來的效果:
粒子化基本就寫到這裡,下篇講講用粒子化的東西做動畫吧,最近事情比較多,懶癌又犯了。
原始碼地址: github.com/bob-chen/ca…
Part 2 地址:gold.xitu.io/post/57dd27…
Part 3 地址: gold.xitu.io/post/57e7a7…
參考
www.w3school.com.cn/tags/canvas…