Canvas基礎-粒子動畫Part1

Bob-Chen發表於2016-09-05

網頁上各種酷炫的粒子動畫看的人眼花繚亂,實際上原理卻非常簡單。

獲取畫素資訊

首先我們需要畫張圖到Canvas上,這裡因為我懶的扣圖(實際上是不會),就找了張jpeg的白底圖片。實際上用png的透明圖會好很多。

https://user-gold-cdn.xitu.io/2016/11/29/294750e6a92a0a929f7ac79a367fb2dd.jpg

一定要等圖片載入完再畫,這很重要,可以用判斷圖片的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),獲得的矩形寬高則為圖片的寬高。

列印出來看看資料是什麼樣子的。

https://user-gold-cdn.xitu.io/2016/11/29/f6edc1d8156f386772e2ec78d9acfad8.jpg

首先返回的是個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();
        }
    }複製程式碼

最後出來的效果:

https://user-gold-cdn.xitu.io/2016/11/29/d6ef6b2f1ac3aa3a6d05b1e91716fd88.jpg

粒子化基本就寫到這裡,下篇講講用粒子化的東西做動畫吧,最近事情比較多,懶癌又犯了。

原始碼地址: 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…

msdn.microsoft.com/zh-cn/libra…

www.cnblogs.com/axes/p/3500…

相關文章