Canvas 倒计时

背景

由于最近要做一个小程序倒计时功能,搜集了许多资料发现都是顺时针,要么就是差那么一点意思; 于是自己写了一个web版的,后续翻译成小程序版。

思路

1、三个canvas层,分为: bottom(底色圆圈)、center、top(由于canvas中圆形,没有类似clearReact的方法;重新画时界面会闪,这里用两个canvas来规避这个问题)

2、center、top为每次倒计时的canvas,top比center小一帧

3、每次重绘时,先把center 重置一次,再把top重置

代码如下:

<!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>Document</title>
    <style>
        .countdown-container {
            display: flex;
            justify-content: center;
            align-items: center;
            position: relative;
            width: 165px;
            height: 165px;
            margin: 0 auto;
        }

        .countdonw-time-container {
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }

        .countdown-time {
            font-size: 56px;
        }

        .countdown-tips {
            font-size: 12px;
        }

        .sign-btn {
            width: 120px;
            height: 32px;
            background: #4F8BFF;
            border-radius: 4px;
            color: white;
            display: flex;
            flex-direction: column;
            justify-content: center;
            margin: 20px auto;
            text-align: center;
        }

        #myCanvas1,
        #myCanvas2,
        #myCanvas3 {
            position: absolute;
            top: 0;
            left: 0;
        }

        #myCanvas1 {
            z-index: 0;
        }

        #myCanvas2 {
            z-index: 1;
        }

        #myCanvas3 {
            z-index: 2;
        }

        .hide {
            display: none;
            width: 0;
        }
    </style>
</head>

<body>
    <div class="countdown-container">
        <canvas id="myCanvas1" width="168" height="168"></canvas>
        <canvas id="myCanvas2" width="168" height="168"></canvas>
        <canvas id="myCanvas3" width="168" height="168"></canvas>
        <div class="countdonw-time-container">
            <div class="countdown-time">1</div>
            <div class="countdown-tips">倒计时( S )</div>
        </div>
    </div>

    <div class="sign-btn"></div>
</body>

</html>

<script>

    const basePx = 168
    const toZoomPx = (pxValue) => {
        const width = document.documentElement.clientWidth;
        return pxValue * (width / 750)
    }

    const pxWidth = toZoomPx(basePx)

    const time = 30 // 可以随意设置
    let count = time // 倒计时剩余时间
    const percent = 2 / time // 每一秒需要的度数
    let timer

    const countTimeNode = document.querySelector('.countdown-time')
    const cvsNode1 = document.querySelector('#myCanvas1')
    const ctx1 = cvsNode1.getContext('2d')

    const cvsNode2 = document.querySelector('#myCanvas2')
    const ctx2 = cvsNode2.getContext('2d')

    const cvsNode3 = document.querySelector('#myCanvas3')
    const ctx3 = cvsNode3.getContext('2d')
    const btn = document.querySelector('.sign-btn')

    countTimeNode.innerHTML = time


    const drawBottom = () => {
        ctx1.beginPath()
        ctx1.lineWidth = 3
        ctx1.strokeStyle = '#d9d9d9'
        ctx1.arc((basePx - 2) / 2, (basePx - 6) / 2, (basePx - 10) / 2, 0, 2 * Math.PI, false)
        ctx1.stroke()
    }

    const getPercent = (count) => {
        const deg = (time - count) * percent
        console.log('count , deg:', count, deg)
        return deg <= 0 ? -0.5 * Math.PI : (1.5 - deg) * Math.PI
    }

    const drawCenter = () => {
        ctx2.beginPath()
        ctx2.lineWidth = 5
        ctx2.strokeStyle = '#4F8BFF'
        ctx2.lineCap = 'round'
        ctx2.arc((basePx - 2) / 2, (basePx - 6) / 2, (basePx - 10) / 2, 1.5 * Math.PI, getPercent(count), false)
        ctx2.stroke()
    }

    const drawTop = () => {
        ctx3.beginPath()
        ctx3.lineWidth = 5
        ctx3.strokeStyle = '#4F8BFF'
        ctx3.lineCap = 'round'
        ctx3.arc((basePx - 2) / 2, (basePx - 6) / 2, (basePx - 10) / 2, 1.5 * Math.PI, getPercent(count), false)
        ctx3.stroke()
    }

    const initCanvasPx = () => {
        cvsNode1.setAttribute('width', pxWidth)
        cvsNode1.setAttribute('height', pxWidth)

        cvsNode2.setAttribute('width', pxWidth)
        cvsNode2.setAttribute('height', pxWidth)

        cvsNode3.setAttribute('width', pxWidth)
        cvsNode3.setAttribute('height', pxWidth)
    }

    const startCountDown = () => {
        if (count <= 0) {
            clearInterval(timer)
            return
        }
        count--
        countTimeNode.innerHTML = count
        // 隐藏中间层,重置中间层 
        cvsNode2.setAttribute('width', 0)
        cvsNode2.setAttribute('width', basePx)
        setTimeout(() => {
            drawCenter()

            cvsNode3.setAttribute('width', 0)
            cvsNode3.setAttribute('width', basePx)

            setTimeout(() => {
                cvsNode3.className = ' '
                drawTop()
            }, 100)
        }, 200)
    }

    const initCanvas = () => {

        // 画底圆
        drawBottom()

        // 画倒计时圆
        drawCenter()

        // 画倒计时圆
        drawTop()
    }

    const initCircle = () => {
        // initCanvasPx()
        initCanvas()
        timer = setInterval(startCountDown, 1000)
    }

    const init = () => {
        initCircle()
        console.log('on init...', countTimeNode.innerHTML)
    }

    btn.addEventListener('click', () => {
        if (timer) {
            clearInterval(timer)
            timer = null
        } else {
            timer = setInterval(startCountDown, 1000)
        }
    })
    init()
</script>

遗留问题:

1、暂未考虑移动端分辨率

2、canvas 绘制的圆形会有锯齿

效果:

原文地址:https://www.cnblogs.com/307914070/p/15012122.html