canvas实现绚丽的倒计时效果代码分析(原demo来源慕课网https://www.imooc.com/learn/133)

#实现效果
随着时间的改变生成小球并动态掉落
在这里插入图片描述随着时间的改变生成小球并动态掉落随着时间的改变生成小球并动态掉落

#HTML代码

<!DOCTYPE html>
<html lang="en" style="margin: 0;height: 100%;">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>绚丽的倒计时</title>
</head>
<body style="height: 100%; margin: 0;">
    <canvas id="canvas" style="height: 100%; width: 100%;"></canvas>   <!-- 注1 -->
    <script src="digit.js"></script>
    <script src="countdown.js"></script>
</body>
</html>

#JS代码

var WINDOW_WIDTH = 1024;//定义画布的宽度
var WINDOW_HEIGHT = 768;//定义画布的长度
var RADIUS = 8;//定义每个小球的半径
var MARGIN_LEFT = 30;//定义画布位置
var MARGIN_TOP = 60;//定义画布位置

const endTime = new Date();//定义截止时间,数据类型为Date  注2
endTime.setTime(endTime.getTime() + 3600*1000);//倒计时时间设定为一个小时
var curShowTimeSeconds = 0;

var balls = [];//用于装掉落小球

const colors = ["#FFCCFF", "#FF00FF", "#FF6600", "CCOOFF", "#FFCC00", "#CC0000", "#66CCFF", "#66CC33", "CCFF00"];//掉落小球的颜色库

window.onload = function () {

    var canvas = document.getElementById("canvas"); //Dom操作获取到html中的canvas元素
    var context = canvas.getContext("2d");  //设定绘图环境

    window.onresize = function () { change(); } //实现倒计时效果适应各种窗口大小   注4
    change();

    curShowTimeSeconds = getCurrentShowTimeSeconds();//获取到倒计时的秒数
    setInterval(
        function () {
            render(context); //绘制小球的函数
            update();//实现小球掉落,滚动
        }
        ,
        50
    )//每50毫秒调用一次render和update函数    注5

}//   注3


function change() {
    WINDOW_WIDTH = document.documentElement.clientWidth || document.body.clientWidth; //获取当前页面的宽度
    WINDOW_HEIGHT = document.documentElement.clientHeight || document.body.clientHeight;//获取当前页面的高度

    MARGIN_LEFT = Math.round(WINDOW_WIDTH / 10); //设定左边距为当前页面宽度的十分之一
    RADIUS = Math.round(WINDOW_WIDTH * 4 / 5 / 108) - 1; 
    //设定小球半径,左边距右边距各占十分之一,则倒计时内容占五分之四,/108见render函数中的注释,求出每个小球所在列宽度后,再减一,让小球不占满每一个列的所有宽度,相当于给各个列之间添加空隙
    MARGIN_TOP = Math.round(WINDOW_HEIGHT / 5);//设定倒计时效果距顶部为五分之一的高度
    canvas.width = WINDOW_WIDTH; //定义画布宽度
    canvas.height = WINDOW_HEIGHT;//定义画布高度
}

function getCurrentShowTimeSeconds() {
    var curTime = new Date(); //curTime为当前的时间
    var ret = endTime.getTime() - curTime.getTime();//ret中为倒计时的 毫秒 数
    ret = Math.round(ret / 1000); //转换为 秒

    return ret >= 0 ? ret : 0;//若倒计时结束则返回0
}


function render(cxt) {

    cxt.clearRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);//刷新画布,cxt为传入的绘图环境   注6

    var hours = parseInt(curShowTimeSeconds / 3600);//设定小时,curShowTimeSeconds为倒计时的秒数,除3600后取整为小时数
    var minutes = parseInt((curShowTimeSeconds - hours * 3600) / 60);//设定分钟
    var seconds = curShowTimeSeconds % 60;//设定秒

    renderDigit(MARGIN_LEFT, MARGIN_TOP, parseInt(hours / 10), cxt);//绘制小时的十位数的数字的,第一个参数为每个数字的左边距,每个数字不同;第二个参数为每个数字距顶部的距离,每个数字相同;第三个参数为要用小球绘制的数字,要绘制小时的十位数上的数字,则把小时数除10后取整数;最后一个参数为绘图环境
    renderDigit(MARGIN_LEFT + 15 * (RADIUS + 1), MARGIN_TOP, parseInt(hours % 10), cxt);//绘制小时的个位数的数字,在digit.js中设置了数组来表示每一个数组,一个数字的宽度有7列小球,一列小球的宽度为2*(半径+1),7*2*(半径+1),为了各个数字间留有空隙,变为15*(半径+1)
    renderDigit(MARGIN_LEFT + 30 * (RADIUS + 1), MARGIN_TOP, 10, cxt);//绘制冒号,左边距多加一个数字的距离,多加15*(半径+1)

    renderDigit(MARGIN_LEFT + 39 * (RADIUS + 1), MARGIN_TOP, parseInt(minutes / 10), cxt);//绘制分钟的十位数数字,digit.js中冒号有四列,左边距则要多加9*(半径+1)
    renderDigit(MARGIN_LEFT + 54 * (RADIUS + 1), MARGIN_TOP, parseInt(minutes % 10), cxt);//绘制分钟的个位数数字,左边距多加15*(半径+1)
    renderDigit(MARGIN_LEFT + 69 * (RADIUS + 1), MARGIN_TOP, 10, cxt);//绘制冒号,54+15

    renderDigit(MARGIN_LEFT + 78 * (RADIUS + 1), MARGIN_TOP, parseInt(seconds / 10), cxt);//绘制秒数的十位数数字,69+9
    renderDigit(MARGIN_LEFT + 93 * (RADIUS + 1), MARGIN_TOP, parseInt(seconds % 10), cxt);//绘制秒数的个位数数字,78+15

    //93+15 = 108 则Math.round(WINDOW_WIDTH * 4 / 5 / 108)求出每列的(半径+1),再减1后得到半径RADIUS

    for (var i = 0; i < balls.length; i++) { //balls中装着滚动掉落的小球,遍历并绘制每个小球
        cxt.fillStyle = balls[i].color;//添加绘图的颜色

        cxt.beginPath();
        cxt.arc(balls[i].x, balls[i].y, RADIUS, 0, 2 * Math.PI, true);//画圆  注7
        cxt.closePath();

        cxt.fill();//绘图填充

    }

}


function renderDigit(x, y, num, cxt) {
    cxt.fillStyle = "rgb(0,102,153)";//设置绘制倒计时数字的小球颜色

    for (var i = 0; i < digit[num].length; i++) 
        for (var j = 0; j < digit[num][i].length; j++)//双层循环,逐步绘制每一个小球

            if (digit[num][i][j] == 1) {  //digit.js设置的数组中数值为1的地方画圆,为0的地方不画
                cxt.beginPath();
                cxt.arc(x + j * 2 * (RADIUS + 1) + (RADIUS + 1), y + i * 2 * (RADIUS + 1) + (RADIUS + 1), RADIUS, 0, 2 * Math.PI);//画圆
                cxt.closePath();

                cxt.fill();//绘图填充
            }



}


function update() {
    var nextShowTimeSeconds = getCurrentShowTimeSeconds();//获取到当前时刻最新的倒计时秒数

    var nextHours = parseInt(nextShowTimeSeconds / 3600);//当前小时数
    var nextMinutes = parseInt((nextShowTimeSeconds - nextHours * 3600) / 60);//当前分钟数
    var nextSeconds = nextShowTimeSeconds % 60;//当前秒数

    var curHours = parseInt(curShowTimeSeconds / 3600);//已经绘制完成的小时数
    var curMinutes = parseInt((curShowTimeSeconds - curHours * 3600) / 60);//已经绘制完成的分钟数
    var curSeconds = curShowTimeSeconds % 60;//已经绘制完成的秒数

    if (nextSeconds != curSeconds) { //当前时间与绘制完成后的时间不同,则添加小球到balls数组中,具体实现为addBalls函数
        if (parseInt(curHours / 10) != parseInt(nextHours / 10)) {
            addBalls(MARGIN_LEFT + 0, MARGIN_TOP, parseInt(nextHours / 10));
        }
        if (parseInt(curHours % 10) != parseInt(nextHours % 10)) {
            addBalls(MARGIN_LEFT + 15 * (RADIUS + 1), MARGIN_TOP, parseInt(nextHours / 10));
        }

        if (parseInt(curMinutes / 10) != parseInt(nextMinutes / 10)) {
            addBalls(MARGIN_LEFT + 39 * (RADIUS + 1), MARGIN_TOP, parseInt(nextMinutes / 10));
        }
        if (parseInt(curMinutes % 10) != parseInt(nextMinutes % 10)) {
            addBalls(MARGIN_LEFT + 54 * (RADIUS + 1), MARGIN_TOP, parseInt(nextMinutes % 10));
        }

        if (parseInt(curSeconds / 10) != parseInt(nextSeconds / 10)) {
            addBalls(MARGIN_LEFT + 78 * (RADIUS + 1), MARGIN_TOP, parseInt(nextSeconds / 10));
        }
        if (parseInt(curSeconds % 10) != parseInt(nextSeconds % 10)) {
            addBalls(MARGIN_LEFT + 93 * (RADIUS + 1), MARGIN_TOP, parseInt(nextSeconds % 10));
        }
        //addBalls()函数第一个参数为左边距,第二个为上边距,第三个为具体要绘制的数字

        curShowTimeSeconds = nextShowTimeSeconds;//添加小球到balls完成后将当前时间的值赋予绘制时完成的时间,以便下一次的调用。
    }

    updateBalls();//设定小球下落的物理模型,并清除已经离开屏幕的小球

}


function addBalls(x, y, num) {

    for (var i = 0; i < digit[num].length; i++)
        for (var j = 0; j < digit[num][i].length; j++)   //两层循环,遍历到每一个小球
            if (digit[num][i][j] == 1) {
                var aBall = {
                    x: x + j * 2 * (RADIUS + 1) + (RADIUS + 1),
                    y: y + i * 2 * (RADIUS + 1) + (RADIUS + 1),
                    g: 1.5 + Math.random(), //每个小球的加速度略微不同
                    vx: Math.pow(-1, Math.ceil(Math.random() * 1000)) * 4, //水平方向上的速度随机为-4或4
                    vy: -5,
                    color: colors[Math.floor(Math.random() * colors.length)]  //在颜色库中随机选择一种颜色
                }

                balls.push(aBall);//将小球装入balls中
            }
}

function updateBalls() {

    for (var i = 0; i < balls.length; i++) {  //改变小球的位置和速度
        balls[i].x += balls[i].vx;
        balls[i].y += balls[i].vy;
        balls[i].vy += balls[i].g;

        if (balls[i].y >= WINDOW_HEIGHT - RADIUS) {
            balls[i].y = WINDOW_HEIGHT - RADIUS;
            balls[i].vy = -balls[i].vy * 0.75;
        }
    }

        //清除过多的小球,以防止随着时间的累积生成过多的小球,影响系统的性能
    var cnt = 0;

    for (var i = 0; i < balls.length; i++)
        if (balls[i].x + RADIUS > 0 && balls[i].x - RADIUS < WINDOW_WIDTH)
            balls[cnt++] = balls[i];

    while (balls.length > Math.min(300, cnt)) {  
        balls.pop();
    }

}

用来表示数字的数组JS代码

digit =
    [
        [
            [0,0,1,1,1,0,0],
            [0,1,1,0,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,0,1,1,0],
            [0,0,1,1,1,0,0]
        ],//0
        [
            [0,0,0,1,1,0,0],
            [0,1,1,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [1,1,1,1,1,1,1]
        ],//1
        [
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,0,1,1,0,0,0],
            [0,1,1,0,0,0,0],
            [1,1,0,0,0,0,0],
            [1,1,0,0,0,1,1],
            [1,1,1,1,1,1,1]
        ],//2
        [
            [1,1,1,1,1,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,0,1,1,1,0,0],
            [0,0,0,0,1,1,0],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//3
        [
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,1,0],
            [0,0,1,1,1,1,0],
            [0,1,1,0,1,1,0],
            [1,1,0,0,1,1,0],
            [1,1,1,1,1,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,0,1,1,0],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,1,1]
        ],//4
        [
            [1,1,1,1,1,1,1],
            [1,1,0,0,0,0,0],
            [1,1,0,0,0,0,0],
            [1,1,1,1,1,1,0],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//5
        [
            [0,0,0,0,1,1,0],
            [0,0,1,1,0,0,0],
            [0,1,1,0,0,0,0],
            [1,1,0,0,0,0,0],
            [1,1,0,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//6
        [
            [1,1,1,1,1,1,1],
            [1,1,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,0,0,1,1,0,0],
            [0,0,1,1,0,0,0],
            [0,0,1,1,0,0,0],
            [0,0,1,1,0,0,0],
            [0,0,1,1,0,0,0]
        ],//7
        [
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,1,1,0]
        ],//8
        [
            [0,1,1,1,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,1,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,0,1,1],
            [0,0,0,0,1,1,0],
            [0,0,0,1,1,0,0],
            [0,1,1,0,0,0,0]
        ],//9
        [
            [0,0,0,0],
            [0,0,0,0],
            [0,1,1,0],
            [0,1,1,0],
            [0,0,0,0],
            [0,0,0,0],
            [0,1,1,0],
            [0,1,1,0],
            [0,0,0,0],
            [0,0,0,0]
        ]//:
    ];//用1和0来表示0`9的各个数字,每一个1的位置上都将绘制一个小球

注释:
注1:https://www.w3school.com.cn/tags/tag_canvas.asp (HTML 标签)

注2:https://www.runoob.com/jsref/jsref-obj-date.html (JavaScript Date 对象)

注3:https://www.runoob.com/w3cnote/javascript-window-onload.html (JavaScript window.onload)

注4:https://www.runoob.com/jsref/event-onresize.html (onresize 事件)

注5:https://www.runoob.com/jsref/met-win-setinterval.html (Window setInterval() 方法)

注6:https://www.runoob.com/jsref/met-canvas-clearrect.html (HTML canvas clearRect() 方法)

注7:https://www.runoob.com/tags/canvas-arc.html (HTML canvas arc() 方法)

注8:https://www.runoob.com/jsref/jsref-obj-math.html (JavaScript Math 对象)

github代码仓库:https://github.com/WOWO5/js.demo01

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值