🍀 简窝 Blog
📃 文章详情

(js+canvas)动态粒子时钟效果的实现

如上图,也是博客首页右侧模块中的一个模块。这是一个由js+canvas的动态时钟效果,是不是很炫酷呢,实现原理其实很简单。

点阵数字

首先,定义一个三维数组digit,它是0到9以及冒号的二维点阵,每个数字都是4*7大小的二维数组,通过遍历二维数组,当值为1时绘制圆形。

如图:

  

所以0-9以及冒号如下所定义:

    var digit =
        [
            [
                [1, 1, 1, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 1, 1, 1]
            ],//0
            [
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1]
            ],//1
            [
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [1, 1, 1, 1],
                [1, 0, 0, 0],
                [1, 0, 0, 0],
                [1, 1, 1, 1]
            ],//2
            [
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [1, 1, 1, 1]
            ],//3
            [
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1]
            ],//4
            [
                [1, 1, 1, 1],
                [1, 0, 0, 0],
                [1, 0, 0, 0],
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [1, 1, 1, 1]
            ],//5
            [
                [1, 1, 1, 1],
                [1, 0, 0, 0],
                [1, 0, 0, 0],
                [1, 1, 1, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 1, 1, 1]
            ],//6
            [
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1]
            ],//7
            [
                [1, 1, 1, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 1, 1, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 1, 1, 1]
            ],//8
            [
                [1, 1, 1, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [1, 1, 1, 1]
            ],//9
            [
                [0],
                [0],
                [1],
                [0],
                [1],
                [0],
                [0]
            ],//:
            [
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0]
            ],//null

它们的下标值就是数字,10就是冒号“:”,11就是null;

数字渲染:

通过for二次循环,将数字值为1的时候,进行canvas绘制。注意,要把握好点阵粒子的位置,

    function showDigitCtx(num, posx = 0, poxy = 0, bc = '#ddd', color = 'green') {
            for (var i = 0; i < 7; i++) {
                for (var j = 0; j < (num===10?1:4); j++) {
                            ctx.beginPath();
                            ctx.arc(posx+j*9,poxy+i*9,R,0,Math.PI*2,true);
                            ctx.fillStyle=digit[num][i][j] === 1?color:bc;
                            ctx.closePath();
                            ctx.fill();


                }
            }


    }

参数分为别数字,点阵数字起始x坐标和y坐标。背景色和数字颜色。如果符号为冒号,点阵数组变1列减省空间。

时间数据:

通过new Date()获取当前时间的时分秒,以及冒号,存储在newdate数组里:

function showTime() {
        var newdate = [];
        var d = new Date();
        var h = d.getHours();
        var m = d.getMinutes();
        var s = d.getSeconds(); 
        newdate.push(Math.floor(h / 10), h % 10, 10, Math.floor(m / 10), m % 10, 10, Math.floor(s / 10), s % 10);
        showDigitGroup(newdate, 0, 0); 
    }

 展示时钟:

时间获取到了,下面就是展示时钟了,先看代码:

    function showDigitGroup(d, x, y) {
        var xx = x;
        var changedate = [];
        //console.log(date.length)
        for (var i = 0; i < d.length; i++) {
            //console.log(xx);
            var dd = d[i];
            if (dd === 0 && i === 0) {
                dd = 11;
            }
                showDigitCtx(dd, xx, y);



            if (d[i] === 10) {
                xx += 15;
            } else {
                xx += 45;
            }

        }
        // console.log(d +'--'+date)
        for (var k = 0; k < d.length; k++) {
            if (d[k] != date[k] && d[k] != undefined && date[k] != undefined) {
                changedate.push(k + '#' + date[k]);

            }
        }
        changedates(changedate);
        date = [];
        date = d.concat();

    }

传入的参数有d时间,数字矩阵起始的相对坐标。

遍历newdate数组,如果小时的个位数是0的话,那就不显示,用digit[11]的null显示,为了将数字时钟清晰的显示,数字之间要控制好间距也就是显示的位置了,每个数字宽40px,冒号宽10px,数字间间距5px。根据冒号判断,如果有冒号,后一位就+15px,否则+45px。

将当前时间与上一次时间(date数组)比较,将不同的时间数据存入changedates中,以便后面的例子特效。

最后通过定时器间隔来刷新每一秒的时间。

粒子特效:

粒子弹跳效果就是一个随机的抛物线,利用canvas实现小球作随机抛物线运动。将小球拆分为x,y轴,通过随机数组让小球作随机运动,y轴上,当小球落地时,弹跳起来,控制好减速系数,如图:

小球开始向上或者向下都是随机的,向左向右也是随机的,但是因为左边空间大,所以控制小球向左几率大一些。

时间改变:

如何触发粒子特效呢,当时钟的数字刷新一次,就会把当前数字上的粒子小球作分散随机运动。比如:秒数的个位是每一秒都刷新的,秒数的十位是十秒刷新一次等等,因此,每一秒的秒的个位都会触发粒子特效。再例如23:59:59时候,下一秒就是0:00:00了,每一位的数字都改变了,所以都触发了。那么这些数据怎么来呢?我们知道,刚刚有个changedates数组,里面就存放了当前时间相对于上一秒的变化,以及变化数字的位置。通过“#”隔开。

 for (var k = 0; k < d.length; k++) {
            if (d[k] != date[k] && d[k] != undefined && date[k] != undefined) {
                changedate.push(k + '#' + date[k]);

            }
        }

date就是存放上一秒的数据。然后将这些数据(位置+数字)取用for出来,为小球特效做准备。

 function changedates(changedate) {
        var t = []
        for (var i = 0; i < changedate.length; i++) {
            // console.log(changedate[i]);
            t = changedate[i].split('#');
            // console.log(t)
            doballs(t[0], t[1], 0, 0);
        }
    }

    function doballs(index, num, x, y) {
        var xx = x, yy = y;
        switch (Number(index)) {
            case 0:
                xx = x;
                break;
            case 1:
                xx = x + 45;
                break;
            case 2:
                xx = x + 90;
                break;
            case 3:
                xx = x + 105;
                break;
            case 4:
                xx = x + 150;
                break;
            case 5:
                xx = x + 195;
                break;
            case 6:
                xx = x + 210;
                break;
            case 7:
                xx = x + 255;
                break;
        }

        var ramNumY=[1,2,3,4];
        var ramNumX=[1,2,3,4];
        var t=[-1,1];
        for (var i=2;i<12;i++){
            ramNumX.push(i*-1);
        }
        var tt=t[Math.floor(Math.random()*t.length)];
        for (var i = 0; i < 7; i++) {
            for (var j = 0; j < 4; j++) {
                if (digit[num][i][j]===1){
                    var ball = {
                        x: xx+j*9,
                        y: yy+i*9,
                        stepX: 0.8*(ramNumX[Math.floor(Math.random()*ramNumX.length)]),
                        stepY: -4*(ramNumY[Math.floor(Math.random()*ramNumY.length)]),
                        k: 0.6,//摩擦系数
                        lastY:tt===1?Math.random()*(H-30)+30:Math.random()*(yy+i*9-R)+R,//小球向上边界
                        t:tt//小球方向
                    }

                    balls.push(ball);
                }
            }
        }

        // console.log(balls )

    }

doballs()方法是小球弹跳的初始数据,x和y:小球粒子坐标,stepx和stepy:定时器更新后的运动步长,k:摩擦系数或者说减速系数,lasty:小球向上的边界,每一次弹跳控制好随机的上边界,以至于每一次都不一样,tt:小球的方向,向上还是向下,也是随机控制的。

最后就是每一秒的更新,小球的变化方法:

    function updateballs() {

        for (var i = 0; i < balls.length; i++) {
            // if (balls[i].y === H - R) {
            //     balls[i].y = balls[i].lastY ;
            //     balls[i].x= balls[i].x+balls[i].stepX*10;
            //
            //
            // }
            balls[i].x = balls[i].x + balls[i].stepX ;
            balls[i].y = balls[i].y + balls[i].stepY ;
            if (balls[i].t===1){
                balls[i].stepY = balls[i].stepY * balls[i].k +2;
            }else if(balls[i].t===-1){
                balls[i].stepY = balls[i].stepY * balls[i].k -2;
            }

            if (balls[i].y > H - R) {
                balls[i].y =H-R;
                balls[i].stepY = -balls[i].stepY* balls[i].k;
                balls[i].t=-1;

            }else  if (balls[i].y < R) {
                balls[i].y =R;
                balls[i].stepY =   -balls[i].stepY* balls[i].k;
                console.log(balls[i].stepY)
                balls[i].t=1;

            }
                if (Math.abs(balls[i].x )> W - R ||Math.abs(balls[i].x )<R*1.5) {
                    balls.splice(i,1);
                    i--;
                }


        }

    }

 上面的两个if组控制小球的y轴方向,下面的两个if控制边界,小球触碰上边界或小边界反弹,最后一个控制小球的消失,当小球出了左右边界时,数组就删除出去小球的数据,不至于死循环。

 

最后完整代码:

<canvas id="canvas"   width="310" height="150" ></canvas>
<script>
    var digit =
        [
            [
                [1, 1, 1, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 1, 1, 1]
            ],//0
            [
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1]
            ],//1
            [
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [1, 1, 1, 1],
                [1, 0, 0, 0],
                [1, 0, 0, 0],
                [1, 1, 1, 1]
            ],//2
            [
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [1, 1, 1, 1]
            ],//3
            [
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1]
            ],//4
            [
                [1, 1, 1, 1],
                [1, 0, 0, 0],
                [1, 0, 0, 0],
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [1, 1, 1, 1]
            ],//5
            [
                [1, 1, 1, 1],
                [1, 0, 0, 0],
                [1, 0, 0, 0],
                [1, 1, 1, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 1, 1, 1]
            ],//6
            [
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1]
            ],//7
            [
                [1, 1, 1, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 1, 1, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 1, 1, 1]
            ],//8
            [
                [1, 1, 1, 1],
                [1, 0, 0, 1],
                [1, 0, 0, 1],
                [1, 1, 1, 1],
                [0, 0, 0, 1],
                [0, 0, 0, 1],
                [1, 1, 1, 1]
            ],//9
            [
                [0],
                [0],
                [1],
                [0],
                [1],
                [0],
                [0]
            ],//:
            [
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0]
            ],//null

        ];
    var date = [];
    var balls = [];
    var canvas = document.getElementById('canvas');
    $(function () {

        var d = new Date();
        var h = d.getHours();
        var m = d.getMinutes();
        var s = d.getSeconds();
        // date.push(Math.floor(h / 10), h % 10,10, Math.floor(m / 10), m % 10,10, Math.floor(s / 10), s % 10);


    });

    if (canvas.getContext) {
        var ctx = canvas.getContext('2d');
        ctx.translate(5,15)
        var W = 300;
        var H = 130;
        var R = 3.5;
        showTime();
        clearInterval(inter);
        var inter = setInterval(function () {
            render();
             updateballs();
            showTime();
        }, 16);
     }

    function render() {

        ctx.clearRect(0,0,W,H)//清除原始图形
        for (var i = 0; i < balls.length; i++) {
            ctx.beginPath();
            ctx.arc(balls[i].x, balls[i].y, R, 0, Math.PI * 2);
            ctx.fillStyle = 'red';
            ctx.closePath();
            ctx.fill();
        }
    }


    function showTime() {
        var newdate = [];
        var d = new Date();
        var h = d.getHours();
        var m = d.getMinutes();
        var s = d.getSeconds();
        // console.log(Math.floor(h/10)+" "+m%10+" "+s)
        newdate.push(Math.floor(h / 10), h % 10, 10, Math.floor(m / 10), m % 10, 10, Math.floor(s / 10), s % 10);
        showDigitGroup(newdate, 0, 0);
        //showDigitGroup(1,2,3,4,5,6)131654
    }

    function showDigitGroup(d, x, y) {
        var xx = x;
        var changedate = [];
        //console.log(date.length)
        for (var i = 0; i < d.length; i++) {
            //console.log(xx);
            var dd = d[i];
            if (dd === 0 && i === 0) {
                dd = 11;
            }
                showDigitCtx(dd, xx, y);



            if (d[i] === 10) {
                xx += 15;
            } else {
                xx += 45;
            }

        }
        // console.log(d +'--'+date)
        for (var k = 0; k < d.length; k++) {
            if (d[k] != date[k] && d[k] != undefined && date[k] != undefined) {
                changedate.push(k + '#' + date[k]);

            }
        }
        changedates(changedate);
        date = [];
        date = d.concat();

    }

    function changedates(changedate) {
        var t = []
        for (var i = 0; i < changedate.length; i++) {
            // console.log(changedate[i]);
            t = changedate[i].split('#');
            // console.log(t)
            doballs(t[0], t[1], 0, 0);
        }
    }

    function doballs(index, num, x, y) {
        var xx = x, yy = y;
        switch (Number(index)) {
            case 0:
                xx = x;
                break;
            case 1:
                xx = x + 45;
                break;
            case 2:
                xx = x + 90;
                break;
            case 3:
                xx = x + 105;
                break;
            case 4:
                xx = x + 150;
                break;
            case 5:
                xx = x + 195;
                break;
            case 6:
                xx = x + 210;
                break;
            case 7:
                xx = x + 255;
                break;
        }

        var ramNumY=[1,2,3,4];
        var ramNumX=[1,2,3,4];
        var t=[-1,1];
        for (var i=2;i<12;i++){
            ramNumX.push(i*-1);
        }
        var tt=t[Math.floor(Math.random()*t.length)];
        for (var i = 0; i < 7; i++) {
            for (var j = 0; j < 4; j++) {
                if (digit[num][i][j]===1){
                    var ball = {
                        x: xx+j*9,
                        y: yy+i*9,
                        stepX: 0.8*(ramNumX[Math.floor(Math.random()*ramNumX.length)]),
                        stepY: -4*(ramNumY[Math.floor(Math.random()*ramNumY.length)]),
                        k: 0.6,//摩擦系数
                        lastY:tt===1?Math.random()*(H-30)+30:Math.random()*(yy+i*9-R)+R,//小球向上边界
                        t:tt//小球方向
                    }

                    balls.push(ball);
                }
            }
        }

        // console.log(balls )

    }

    function updateballs() {

        for (var i = 0; i < balls.length; i++) {
            // if (balls[i].y === H - R) {
            //     balls[i].y = balls[i].lastY ;
            //     balls[i].x= balls[i].x+balls[i].stepX*10;
            //
            //
            // }
            balls[i].x = balls[i].x + balls[i].stepX ;
            balls[i].y = balls[i].y + balls[i].stepY ;
            if (balls[i].t===1){
                balls[i].stepY = balls[i].stepY * balls[i].k +2;
            }else if(balls[i].t===-1){
                balls[i].stepY = balls[i].stepY * balls[i].k -2;
            }

            if (balls[i].y > H - R) {
                balls[i].y =H-R;
                balls[i].stepY = -balls[i].stepY* balls[i].k;
                balls[i].t=-1;

            }else  if (balls[i].y < R) {
                balls[i].y =R;
                balls[i].stepY =   -balls[i].stepY* balls[i].k;
                console.log(balls[i].stepY)
                balls[i].t=1;

            }
                if (Math.abs(balls[i].x )> W - R ||Math.abs(balls[i].x )<R*1.5) {
                    balls.splice(i,1);
                    i--;
                }


        }

    }

    function showDigitCtx(num, posx = 0, poxy = 0, bc = '#ddd', color = 'green') {
            for (var i = 0; i < 7; i++) {
                for (var j = 0; j < (num===10?1:4); j++) {
                            ctx.beginPath();
                            ctx.arc(posx+j*9,poxy+i*9,R,0,Math.PI*2,true);
                            ctx.fillStyle=digit[num][i][j] === 1?color:bc;
                            ctx.closePath();
                            ctx.fill();


                }
            }


    }






</script>
 

📑 目录