专栏首页程序员的诗和远方Canvas基础-粒子动画Part3

Canvas基础-粒子动画Part3

在上一篇文章 Canvas基础-粒子动画Part2 中讲了让图片做粒子动画。上篇写完不久,想起既然是用Canvas,写上去的东西都可以做粒子动画,那么就补充讲下文字做粒子动画,至于为什么拖了这么久,还不是因为去写公众号和研究微信小程序了,给公众号搞了一个2048形式的小游戏,叫『码工修炼之路』,在公众号 『程序员的诗和远方』回复关键字 2048 就可以玩了。

文字做粒子动画

上一篇中我们是把图片给画到 Canvas 中,要写文字进去就简单多,直接有方便的接口就可以做,我们来试试,

先在页面上添加一个输入框,用来输入文字。

    <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() 方法。

    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();
    }

这里大概解释一下:

  • 首先获取了文本框的输入值,如果没有输入东西,我们就按输入值是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() 函数,或者给我留言。

最后的 draw2() 函数,以及用到的几个函数,和 Canvas基础-粒子动画Part2 中基本一致,这里就不多做解释了,有不明白的欢迎留言。

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

出来的效果:

会发现文字被截掉了一部分,尿性又来了,这是因为 Canvas 画出来的文本在垂直方向并不是按顶部对齐的,类似 css 中的 vertical-align 属性,Canvas 可可以通过 contextextBaseline 属性来设置文字的对其方式,加入代码 ctx.textBaseline="top"; 即可。

最后出来的效果:

如果理解了前面对图片做粒子动画的话,理解这个应该不难。另外,本篇贴了很多代码,因为之前有读者留言说希望能贴完整代码出来,有助于理解。考虑到移动端看代码,上下左右翻来翻去体验实在不好,还是贴尽量完整的主要代码好了,源码都有放在 github 上,可以对照着来看,更重要的是自己亲手实践实践。关于贴代码的建议,欢迎留言讨论哈。

源码地址: https://github.com/bob-chen/canvas-demo/blob/master/basic/particle-part3.html

参考

http://www.w3school.com.cn/tags/canvas_measuretext.asp

http://www.w3school.com.cn/tags/canvas_filltext.asp

http://www.cnblogs.com/vajoy/p/3992852.html

http://www.cnblogs.com/axes/p/3500655.html

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Canvas画图-一个比想象中更骚气的圆(渐变圆环)

    之前介绍了Canvas画图基础,这篇介绍一下画一个带渐变效果的圆。 一个渐变的圆环 渐变色应用广泛,和圆环结合做进度条非常酷,今天我们就来画一个这样的圆环...

    Bob.Chen
  • Canvas基础-粒子动画Part2

    紧接上一篇文章 Canvas基础-粒子动画Part1 其实这篇早在一个星期之前就应该发了,无奈事情太多,而且我又跑去写微信公众号了。 粒子动起来 有了上一...

    Bob.Chen
  • Canvas基础-粒子动画Part1

    网页上各种酷炫的粒子动画看的人眼花缭乱,实际上原理却非常简单。 获取像素信息 首先我们需要画张图到Canvas上,这里因为我懒的扣图(实际上是不会),就找...

    Bob.Chen
  • 干货!2018年你值得一看的网页设计作品集赏析

    网页设计作品集对网页设计师而言,既是网页门面,也是个人专业素养的体现。那么在作品集设计上万不能掉以轻心。无论是制作一份简约大方还是极具表现力的精良作品集,设计师...

    奔跑的小鹿
  • 详解Vue Native

    译者按: 一家叫GeekyAnts的印度公司开发了Vue Native,基于React Native实现。

    Fundebug
  • lucene原理及源码解析--核心类

    静儿
  • Canvas 进阶(四)实现一个“刮刮乐”游戏

    我们创建一个 ScrapAward 类,通过传入 option 和调用其 restart() 方法实现重新开始。

    小皮咖
  • 详解Vue Native

    译者按: 一家叫 GeekyAnts 的印度公司开发了 Vue Native,基于 React Native 实现。

    Fundebug
  • 开发团队的效率

    我之前写过一篇叫《加班与效率》的文章,从概念上说了一些我对“效率”的认识,但是那篇文章趋于概念化,对于一些没有经历过这样的环境的同学来说,可能会觉得太抽象了。很...

    laixiangran
  • 基于vue.js开发的WordPress主题

    https://gitee.com/xuanmos/xm-vue-wordpress-theme.git

    程序源代码

扫码关注云+社区

领取腾讯云代金券