Canvas基礎-粒子動畫Part3

在上一篇文章 Canvas基礎-粒子動畫Part2 中講了讓圖片作粒子動畫。上篇寫完不久,想起既然是用Canvas,寫上去的東西均可以作粒子動畫,那麼就補充講下文字作粒子動畫,至於爲何拖了這麼久,還不是由於去寫公衆號和研究微信小程序了,給公衆號搞了一個2048形式的小遊戲,叫『碼工修煉之路』,在公衆號 『程序員的詩和遠方』回覆關鍵字 2048 就能夠玩了。javascript

文字作粒子動畫

上一篇中咱們是把圖片給畫到 Canvas 中,要寫文字進去就簡單多,直接有方便的接口就能夠作,咱們來試試,css

先在頁面上添加一個輸入框,用來輸入文字。html

<body>
        <div class="input-wrap">
            <input id="txt" type="text" name="" value="" placeholder="輸入發射文字...">
            <button id="btn" class="btn">發射</button>
        </div>
        <canvas id="canvas" width="300" height="300" ></canvas>
    </body>複製代碼

以後修改一下咱們的 init() 方法。java

function init() {
        var s = 0;
        input = document.querySelector("#txt");

        var l = input.value ? input.value : "Beta";
        input.value = "";

        // 有正在運行的動畫要取消掉
        if (rafId) cancelAnimationFrame(rafId);

        setFontSize(fontSize);
        s = Math.min(fontSize,
                  (canvas.width / ctx.measureText(l).width) * 0.8 * fontSize, 
                  (canvas.height / fontSize) * (isNumber(l) ? 1 : 0.5) * fontSize);
        setFontSize(s);

        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.fillText(l, 10, 0);

        dotList = [];
        handleCanvas();

        ctx.clearRect(0, 0, canvas.width, canvas.height);

        draw2();
    }複製代碼

這裏大概解釋一下:git

  • 首先獲取了文本框的輸入值,若是沒有輸入東西,咱們就按輸入值是Beta來作;
  • 而後,若是有正在運行的動畫,咱們須要把正在運行的給幹掉;
  • 以後這一步是設置畫到 Canvas 中的字體大小,咱們先按預設的值給 Canvas 設置了一個字體大小,就是第一句 setFontSize(fontSize)
  • 實際上,Canvas 的空間有限,若是預設的字體大小不合適,極可能文字會現實不徹底。因而這裏使用了 ctx.measureText(l) 方法來檢查字體的寬度,measureText() 方法返回一個對象,該對象包含以像素計算的指定字體寬度,由於但願留點空間,因此這裏乘了一個0.8。一樣,對高度也作了一些處理,不能搞過高。
  • 以後把咱們處理事後的 fontSzie 值給從新設置給 Canvas;
  • 使用 fillText 方法把文字寫進去,第一個參數爲文本值,第二個爲開始繪製文本的x座標,第三個爲y座標,這裏作了一點偏移。
  • 以後和上一篇的處理大體相同,這裏把處理畫布,得到可用粒子這一步給單獨作了一個函數 handleCanvas()

setFontSize(), isNumber(), handleCanvas()函數以下:程序員

var fontSize = 500,
        fontFamily = 'Helvetica Neue, Helvetica, Arial, sans-serif';

    function setFontSize(s) {
        ctx.font = s + 'px ' + fontFamily;
    }

    function isNumber(n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    }

    function handleCanvas() {
        var imgW = canvas.width,
            imgH = canvas.height;

        var imgData = ctx.getImageData(0, 0, imgW, imgH);
        // console.log(imgData);

        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);
                }
            }
        }
    }複製代碼

關於 handleCanvas() 函數有不明白的地方,能夠看以前的文章 Canvas基礎-粒子動畫Part1 裏面的 init() 函數,或者給我留言。github

最後的 draw2() 函數,以及用到的幾個函數,和 Canvas基礎-粒子動畫Part2 中基本一致,這裏就很少作解釋了,有不明白的歡迎留言。canvas

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;
        this.delay = this.frameCount*Math.random();
        this.delayCount = 0;
    }

    // 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;
    }

    var rafId = null,
        finishCount = 0;
    function draw2() {
        console.log(1);

        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;

            if(curDot.delayCount < curDot.delay){
                curDot.delayCount += 1;
                continue;
            }

            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);
    }複製代碼

出來的效果:小程序

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

會發現文字被截掉了一部分,尿性又來了,這是由於 Canvas 畫出來的文本在垂直方向並非按頂部對齊的,相似 css 中的 vertical-align 屬性,Canvas 可能夠經過 contextextBaseline 屬性來設置文字的對其方式,加入代碼 ctx.textBaseline="top"; 便可。微信小程序

最後出來的效果:

https://user-gold-cdn.xitu.io/2016/11/29/308625a56eb75e57e9f921a26bf5fff4.gif

若是理解了前面對圖片作粒子動畫的話,理解這個應該不難。另外,本篇貼了不少代碼,由於以前有讀者留言說但願能貼完整代碼出來,有助於理解。考慮到移動端看代碼,上下左右翻來翻去體驗實在很差,仍是貼儘可能完整的主要代碼好了,源碼都有放在 github 上,能夠對照着來看,更重要的是本身親手實踐實踐。關於貼代碼的建議,歡迎留言討論哈。

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

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

Part 2 地址: gold.xitu.io/post/57dd27…

碎碎念

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

微信公衆號:程序員的詩和遠方

公衆號ID : MonkeyCoder-Life

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

參考

www.w3school.com.cn/tags/canvas…

www.w3school.com.cn/tags/canvas…

www.cnblogs.com/vajoy/p/399…

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

相關文章
相關標籤/搜索