canvas实现时钟效果

写的时候遇到的下列几个问题:
1、api了解不深刻,如requestAnimationFrame

const fn =()=>{
//requestAnimationFrame(fn())//错误的写法
requestAnimationFrame(fn)//正确的写法
}

2、关于下面的clear函数(清除动画)与update(更新时间数据)函数,这两个函数都有用到requestAnimationFrame,因此我起初是打算将是合并为一个的,将clear放到update后面进行执行:

const clear =()=>{
//...
}
const update =()=>{
//...
clear()
//...
requestAnimationFrame(update)
}
update()

下面是我个人对这样写的看法:
(1)、虽然看起来是没有问题的,但是由于Demo中update函数中的获取画布像素颗粒循环次数较多,需要一定的时间,会延迟clear的执行。
(2)、获取你会想到将clear放到update最上面优先执行,这样的话就犯了最初级的错误,页面不会显示如何东西,在update中我们需要先获取画布颗粒,如果清除了,还怎么获得?

完整代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>时钟</title>
    <style>
        * {
            box-sizing: border-box;

        }

        .canvas {
            border: 1px solid;
            zoom: 0.5;
        }
    </style>
</head>

<body>
    <canvas class="canvas" width="800" height="600"></canvas>
</body>
<script>
    const random = (m, n) => {
        return Math.random() * (n - m) + m
    }

    //拿到canvas标签
    const canvas = document.querySelector(".canvas")
    let ctx = canvas.getContext("2d", {
        willReadFrequently: true
    });
    const cWidth = canvas.width
    const cHeight = canvas.height
    console.log(cWidth, cHeight);

    //draw绘制圆的函数,传递x,y,size {x轴位置 , y轴位置 , size 圆半径}
    const draw = (x, y, size) => {
        ctx.beginPath();
        //填充颜色
        ctx.fillStyle = "#333"
        //描边颜色
        ctx.strokeStyle = "#333"
        ctx.arc(x, y, size, 0 * Math.PI, 2 * Math.PI);
        ctx.closePath();
        //描边
        ctx.stroke();
        //填充
        ctx.fill();
    }

    //构造圆圈对象
    class circle {

        constructor() {


            const r = Math.min(cWidth, cHeight) / 2
            const wid = cWidth / 2
            const hei = cHeight / 2
            const rad = random(0, 360) * Math.PI / 180
            const x = Math.cos(rad) * r + wid
            const y = Math.sin(rad) * r + hei
            const size = random(1, 4)

            this.x = x
            this.y = y
            this.wid = wid
            this.hei = hei
            this.size = size
            this.r = r


        }
        drawArc() {
            draw(this.x, this.y, this.size)
        }


        move(duringX, duringY) {

            //总运动时间
            const speed = 500
            const x = this.x
            const y = this.y
            //每毫秒运动的距离
            const xLen = duringX - x
            const yLen = duringY - y


            //?这里之所以不用 当前位置 + 速度 = 下一次的位置
            //  而是用原来的位置+ 时间*速度 =当前的位置
            //是因为requestAnimationFrame并非是按照时间进行调用的,而是根据刷新率
            const xSpeed = xLen / speed
            const ySpeed = yLen / speed

            const time = (new Date()) - 0

            //_move,更改当前圆对象的 x坐标与y坐标
            const _move = () => {
                const now_time = (new Date()) - time
                this.x = x + now_time * xSpeed
                this.y = y + now_time * ySpeed

                if (now_time < speed) {
                    requestAnimationFrame(_move)
                } else {
                    this.x = duringX
                    this.y = duringY
                }
            }

            _move()
        }
    }

    //text 时间文本
    let text = null

    //返回当前日期  yyyy - MM - dd
    function getText() {
        return new Date().toTimeString().substring(0, 8)
    }
    const list = []

    function unpadte() {
        const now_text = getText()
        if (text == now_text) {
            requestAnimationFrame(unpadte)
            return
        }
        text = now_text
        ctx.fillStyle = "#000"
        ctx.textBaseline = "middle"
        ctx.font = '100px serif'
        const wid = ctx.measureText(text).width
        ctx.fillText(text, (cWidth - wid) / 2, cHeight / 2)
        const points = getPoints()
        if (points.length < list.length) {
            list.splice(points.length)
        }
        // clear()
        for (let i = 0; i < points.length; i++) {

            let p = list[i]
            if (!p) {
                p = new circle()
                list.push(p)
            }
            const [x, y] = points[i]
            p.move(x, y)
        }
        requestAnimationFrame(unpadte)
    }

    //clear,定时清除画布内容,实习动画效果的次要关键内容
    const clear = () => {
        ctx.clearRect(0, 0, cWidth, cHeight)
        list.forEach(item => {
            item.drawArc()
        })
        requestAnimationFrame(clear)
    }
    clear()

    //获取画布所有像素坐标
    const getPoints = () => {
        const points = []
        const {
            width,
            height,
            data
        } = ctx.getImageData(0, 0, canvas.clientWidth, canvas.clientHeight)
        //gap表示间隔跨度
        const gap = 3;
        for (let i = 0; i < width; i += gap) {
            for (let j = 0; j < height; j += gap) {
                const index = (i + j * width) * 4
                const r = data[index]
                const g = data[index + 1]
                const b = data[index + 2]
                const a = data[index + 3]
                if (r == 0 && g == 0 && b == 0 && a == 255) {
                    points.push([i, j])
                }
            }
        }
        return points
    }
    unpadte()


</script>

</html>

代码是根据网络上的文章实现的(渡一),按照自己的思路又变化了一些

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值