Canvas基礎-粒子動畫Part2

Bob-Chen發表於2016-09-17

緊接上一篇文章 Canvas基礎-粒子動畫Part1 其實這篇早在一個星期之前就應該發了,無奈事情太多,而且我又跑去寫微信公眾號了。

粒子動起來

有了上一篇的基礎,我們已經可以獲得粒子,並將輪廓顯示在Canvas上,如果看了之前我寫的一些關於 Canvas動畫啊,畫圖啊什麼文章的話,其實應該已經很清楚如何去讓這些粒子動起來。

這裡我們重新定義一個draw2()方法,init()等還是和Part1一樣,對圖片進行取樣,獲取粒子的位置,儲存在Dot物件裡面,這裡就省略了。

要讓粒子動起來無非是不斷的計算粒子的位置,如果是線性增加的話,會比較生硬,這裡使用了Tween的緩動函式,可以看一下jquery.easing.js裡面的緩動函式,直接拿來用就可以了,效果很多,我這裡只選擇了一個easeInOut的效果。

    // t 當前時間
    // b 初始值
    // c 總位移
    // d 總時間
    function easeInOutCubic(t, b, c, d) {
        if ((t/=d/2) < 1) return c/2*t*t*t + b;
        return c/2*((t-=2)*t*t + 2) + b;
    }複製程式碼

有了緩動函式,我們還需要在每個Dot物件中新增一些資訊,

    function Dot(centerX, centerY, radius) {
        this.x = centerX;
        this.y = centerY;
        this.radius = radius;
        this.frameNum = 0;
        this.frameCount =  Math.ceil(3000 / 16.66);
        this.sx = 400;
        this.sy = 400;
    }複製程式碼

x,y,radius都和Part1一樣,分別表示,圓心座標和半徑,新增的幾個含義如下:

  • frameNum, 表示為這個粒子當前在第幾幀;
  • frameCount, 表示一共有多少幀,一般來說我們不會直接知道做完這個動畫一共有多少幀,所以這裡我們是算出來的,parseInt(3000 / 16.66) 中3000表示3000毫秒,也就是整個動畫耗時3秒,而16.66是因為按60FPS來算,瀏覽器對每一幀畫面的渲染工作需要1秒 / 60 = 16.66毫秒,算出來之後再做個向上取整,就算出總幀數。
  • sx, 起始點x值,這裡為了方便就寫死了,也可以用隨機數;
  • sy, 起始點y值。

然後我們來寫draw2方法:

    var rafId = null,
        finishCount = 0;
    function draw2() {
        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";

        var len = dotList.length,
            curDot = null,
            frameNum = 0,
            frameCount = 0,
            curX, curY;
        finishCount = 0;
        for(var i=0; i < len; i+=1) {
            // 當前粒子
            curDot = dotList[i];

            // 獲取當前的time和持續時間和延時
            frameNum = curDot.frameNum;
            frameCount = curDot.frameCount;

            ctx.save();
            ctx.beginPath();

            if(frameNum < frameCount) {
                curX = easeInOutCubic(frameNum, curDot.sx, curDot.x-curDot.sx, curDot.frameCount);
                curY = easeInOutCubic(frameNum, curDot.sy, curDot.y-curDot.sy, curDot.frameCount);

                ctx.arc(curX, curY, curDot.radius, 0, 2*Math.PI);
                curDot.frameNum += 1;
            } else {
                ctx.arc(curDot.x, curDot.y, curDot.radius, 0, 2*Math.PI);
                finishCount += 1;
            }
            ctx.fill();
            ctx.restore();

            if (finishCount >= len) {
                cancelAnimationFrame(rafId);
                return;
            }
        }

        rafId = requestAnimationFrame(draw2);
    }複製程式碼

程式碼雖然有點長,但是還是比較好理解的。

  • 動畫進行中的時候frameNum < frameCount,通過前面的緩動函式計算出當前應該到達的x,y值,然後畫到Canvas上並將這個點的幀數加一。
  • 最後一個幀的時候,也就是else條件,就不要畫計算出來的值了,畫實際應該在的位置。
  • 一定要注意ctx.beginPath()ctx.fill(),不然你的畫布上啥子都沒有。
  • 定義了一個finishCount,用來在每次畫粒子的時候統計有多少個是已經跑到相應位置了,所以每次迴圈開始前都要將其置為0,當跑到位的粒子數量和總粒子數量相等的時候,就呼叫cancelAnimationFrame並退出,停掉相應的繪製,不要浪費資源。
  • 還有就是判斷是否停掉要放在ctx.fill()之後做,不然有會出現少了一個粒子的情況。

這樣出來的效果:

https://user-gold-cdn.xitu.io/2016/11/29/5128d9b5b92fc7aa189a99090704635d.gif

是不是感覺被騙了,粒子整體移動,一開始一團團的,最後才有點粒子化,粒子感不明顯,說好的酷炫狂拽屌炸天呢?

別急,知道我的尿性,不一開始把所有東西都說出來,而要把整個探索過程講清楚。這裡我們不要將全部的粒子一次都放出去,我們慢慢放。

首先在Dot物件裡面新增兩個屬性delaydelayCount

    function Dot(centerX, centerY, radius) {
        .
        .
        .
        this.frameCount =  Math.ceil(3000 / 16.66);
        .
        .
        this.delay = this.frameCount*Math.random();
        this.delayCount = 0;
    }複製程式碼
  • delay,這裡表示這個粒子要等待多少幀才開始動,這裡簡單用總幀數和一個隨機數相乘。
  • delayCount,表示當前粒子以及等待了多少幀。

改完Dot物件之後,接下來的事情就好辦的,在迴圈開始之前現判斷一下是否達到等待幀數即可。

        for(var i=0; i < len; i+=1) {
            .
            .
            .
            if(curDot.delayCount < curDot.delay){
                curDot.delayCount += 1;
                continue;
            }

            ctx.save();
            ctx.beginPath();

            if(frameNum < frameCount) {
            .
            .
            .複製程式碼

最後出來的效果:

https://user-gold-cdn.xitu.io/2016/11/29/780832f1dc60eeaa279e6a9e2751327e.gif

粒子化動畫的大致原理就是這樣的啦,隨著我們給Dot物件新增更多的屬性,粒子動畫的想象空間還是比較大的,比如加些顏色,加些運動軌跡,通過顏色和透明度做3D效果等等,下篇講講這個程式碼的優化重構吧。

原始碼地址: github.com/bob-chen/ca…

Part 1 地址: gold.xitu.io/post/57cda0…

Part 3 地址: gold.xitu.io/post/57e7a7…

碎碎念

最近總想記錄一些所思所想,寫寫科技與人文,寫寫生活狀態,寫寫讀書感悟,昨天終於動筆,在微信公眾號上寫,主要是扯淡和感悟,歡迎關注,交流。

微信公眾號:程式設計師的詩和遠方

公眾號ID : MonkeyCoder-Life

https://user-gold-cdn.xitu.io/2016/11/29/92d9e667c10e7d56e05b76c62ddb110d

參考

gsgd.co.uk/sandbox/jqu…

www.zybuluo.com/dengzhirong…

developers.google.com/web/fundame…

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

相關文章