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

Canvas基础-粒子动画Part2

紧接上一篇文章 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()之后做,不然有会出现少了一个粒子的情况。

这样出来的效果:

是不是感觉被骗了,粒子整体移动,一开始一团团的,最后才有点粒子化,粒子感不明显,说好的酷炫狂拽屌炸天呢?

别急,知道我的尿性,不一开始把所有东西都说出来,而要把整个探索过程讲清楚。这里我们不要将全部的粒子一次都放出去,我们慢慢放。

首先在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) {
            .
            .
            .

最后出来的效果:

粒子化动画的大致原理就是这样的啦,随着我们给Dot对象添加更多的属性,粒子动画的想象空间还是比较大的,比如加些颜色,加些运动轨迹,通过颜色和透明度做3D效果等等,下篇讲讲这个代码的优化重构吧。

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

参考

http://gsgd.co.uk/sandbox/jquery/easing/

https://www.zybuluo.com/dengzhirong/note/183215

https://developers.google.com/web/fundamentals/performance/rendering/?hl=zh-cn

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

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

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

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

    在之前的文章 Canvas基础-粒子动画Part2 和 Canvas基础-粒子动画Part3 中分别讲了用图片和文字做粒子动画,今天我们来把代码简单整理一下,...

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

    在上一篇文章 Canvas基础-粒子动画Part2 中讲了让图片做粒子动画。上篇写完不久,想起既然是用Canvas,写上去的东西都可以做粒子动画,那么就补充讲...

    Bob.Chen
  • 使用 Microsoft.UI.Xaml 解决 UWP 控件和对老版本 Windows 10 的兼容性问题

    发布于 2018-07-21 13:51 更新于 2018-07...

    walterlv
  • 使用http维持socket长连接

    项目中有遇到问题如下: 1、旧版的cs服务,因为每个用户和唯一的长连接是在登录后绑定的,并且所有的消息报文均是基于该长连接去发送接收的,所以要求node服务要维...

    用户1141560
  • Python3+Django2配置后台管理

    使用 Django 我们只需要做一些配置,就可以实现简单的后台管理系统,下面我们以新闻系统为例子来搭建后台。

    小柒2012
  • 使用lamp搭建个人博客

    购买一台网络云服务器,可以考虑阿里云、腾讯云、百度云等,这里以阿里云服务器举例。 共享型价格在半年170元左右。不定期会有活动,首台半价等。 系统配置Ubu...

    lollipop72
  • Python3+Django2配置后台管理

    使用 Django 我们只需要做一些配置,就可以实现简单的后台管理系统,下面我们以新闻系统为例子来搭建后台。

    小柒2012
  • 不一样的 "反弹Shell" 系统剖析

    前两篇文章讲解了cmd和powershell混淆的各种姿势,原理和防御方式,希望能对大家有启发。

    七夜安全博客
  • 技术 | 中山大学取得溶瘤病毒M1研究新进展,为治愈肿瘤带来曙光

    镁客网

扫码关注云+社区

领取腾讯云代金券