最终效果

16c2f46cfa03bec4_tplv-t2oaga2asx-jj-mark_3024_0_0_0_q75.gif

一、定义初始变量

let radius = 140 //外环半径
let thickness = 20 //圆环厚度
let innerRadius = radius - thickness //内环半径
let startAngle = -90 //开始角度
let endAngle = 180 //结束角度
let x = 0 //圆心x坐标
let y = 0 //圆心y坐标
let canvas = document.getElementById('tutorial');
canvas.width = 300;
canvas.height = 300;

let ctx = canvas.getContext('2d');
ctx.translate(canvas.width / 2, canvas.height / 2);//将绘图原点移到画布中央
ctx.rotate(angle2Radian(225)) //将画布旋转225度
ctx.fillStyle = "#f2d7d7"; //初始填充颜色

二、工具方法

 //计算圆环上点的坐标
function calcRingPoint(x, y, radius, angle) {
    let res = {}
    res.x = x + radius * Math.cos(angle * Math.PI / 180)
    res.y = y + radius * Math.sin(angle * Math.PI / 180)
    return res
}

//弧度转角度
function radian2Angle(radian) {
    return 180 * radian / Math.PI
}

//角度转弧度
function angle2Radian(angle) {
    return angle * Math.PI / 180
}

三、渲染方法

//渲染函数
function renderRing(startAngle, endAngle) {
     ctx.beginPath();

    //绘制外环
    ctx.arc(x, y, radius, angle2Radian(startAngle), angle2Radian(endAngle))

    //计算外环与内环第一个连接处的中心坐标
    let oneCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, endAngle)

    //绘制外环与内环第一个连接处的圆环
    ctx.arc(oneCtrlPoint.x, oneCtrlPoint.y, thickness / 2, angle2Radian(-90), angle2Radian(270))

    //绘制内环
    ctx.arc(x, y, innerRadius, angle2Radian(endAngle), angle2Radian(startAngle), true)

    //计算外环与内环第二个连接处的中心坐标
    let twoCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, startAngle)

    //绘制外环与内环第二个连接处的圆环
    ctx.arc(twoCtrlPoint.x, twoCtrlPoint.y, thickness / 2, angle2Radian(-90), angle2Radian(270))

    ctx.fill()
    // ctx.stroke()
}

具体思路:

为了方便,代码中使用弧度的地方一律由角度转为弧度

1.绘制外环:

image.png

这一步最简单,直接按照官方的使用方法使用即可

2.绘制外环与内环连接处的第一个圆环

image.png

首先算出,外环结束处与内环开始处中间点的坐标

image.png

计算圆环上点的坐标公式为:

x = x + radius Math.cos(angle Math.PI / 180)

y = y + radius Math.sin(angle Math.PI / 180)

代入以上公式可算出圆环上任意一点的坐标,然后再以此为圆心,圆环厚度/2为半径 画圆环

3.绘制内环

image.png

这一步只需将半径缩短,并将绘制外环的开始角度与结束角度调换即可

4.绘制内环与外环连接处的第二圆环

image.png

和第二步同理,先计算出外环开始处与内环结束处中间点的坐标,然后再以此为圆心,圆环厚度/2为半径 画圆环

5.完成填充
到这一步,圆环就完成了

四、动态进度条

 //进度条动画
ctx.fillStyle = "#e87c7c";
let tempAngle = startAngle
let twoEndAngle = 0
let step = (twoEndAngle - startAngle) / 100
let numberSpan = document.querySelector('.number')
let count = 0
let inter = setInterval(() => {
    if (tempAngle > twoEndAngle) {
        clearInterval(inter)
    } else {
        count++
        numberSpan.innerText = count
        tempAngle += step
    }
    renderRing(startAngle, tempAngle)
}, 16.7)

动态计算结束角度,然后设定一个计数器,重复执行渲染方法。

五、完整代码

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>canvas</title>
    <style>
        .ring {
            width: 300px;
            height: 300px;
            display: flex;
            align-items: center;
            justify-content: center;
            flex-direction: column;
            position: relative;
        }

        .fraction {
            position: absolute;
            font-size: 30px;
            font-weight: bold;
            color: red;
        }

        .small {
            font-size: 12px;
            font-weight: lighter;
        }

        .title {
            font-size: 20px;
            color: red;
            bottom: 40px;
            position: absolute;
        }
    </style>
</head>

<body>
    <div class="ring">
        <canvas id="tutorial"></canvas>
        <span class="fraction"><span class="number">0</span> <span class="small">分</span> </span>
        <span class="title">服务分</span>
    </div>

    <script>
        let radius = 140 //外环半径
        let thickness = 20 //圆环厚度
        let innerRadius = radius - thickness //内环半径
        let startAngle = -90 //开始角度
        let endAngle = 180 //结束角度
        let x = 0 //圆心x坐标
        let y = 0 //圆心y坐标
        let canvas = document.getElementById('tutorial');
        canvas.width = 300;
        canvas.height = 300;

        let ctx = canvas.getContext('2d');
        ctx.translate(canvas.width / 2, canvas.height / 2);//将绘图原点移到画布中央
        ctx.rotate(angle2Radian(225)) //将画布旋转225度
        ctx.fillStyle = "#f2d7d7"; //初始填充颜色

        renderRing(startAngle, endAngle)

        //进度条动画
        ctx.fillStyle = "#e87c7c";
        let tempAngle = startAngle
        let twoEndAngle = 0
        let step = (twoEndAngle - startAngle) / 100
        let numberSpan = document.querySelector('.number')
        let count = 0
        let inter = setInterval(() => {
            if (tempAngle > twoEndAngle) {
                clearInterval(inter)
            } else {
                count++
                numberSpan.innerText = count
                tempAngle += step
            }
            renderRing(startAngle, tempAngle)
        }, 16.7)


        //渲染函数
        function renderRing(startAngle, endAngle) {
            ctx.beginPath();

            //绘制外环
            ctx.arc(x, y, radius, angle2Radian(startAngle), angle2Radian(endAngle))

            //计算外环与内环第一个连接处的中心坐标
            let oneCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, endAngle)

            //绘制外环与内环第一个连接处的圆环
            ctx.arc(oneCtrlPoint.x, oneCtrlPoint.y, thickness / 2, angle2Radian(-90), angle2Radian(270))

            // //绘制内环
            ctx.arc(x, y, innerRadius, angle2Radian(endAngle), angle2Radian(startAngle), true)

            //计算外环与内环第二个连接处的中心坐标
            let twoCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, startAngle)

            //绘制外环与内环第二个连接处的圆环
            ctx.arc(twoCtrlPoint.x, twoCtrlPoint.y, thickness / 2, angle2Radian(-90), angle2Radian(270))

            ctx.fill()
            // ctx.stroke()
        }

        //计算圆环上点的坐标
        function calcRingPoint(x, y, radius, angle) {
            let res = {}
            res.x = x + radius * Math.cos(angle * Math.PI / 180)
            res.y = y + radius * Math.sin(angle * Math.PI / 180)
            return res
        }

        //弧度转角度
        function radian2Angle(radian) {
            return 180 * radian / Math.PI
        }

        //角度转弧度
        function angle2Radian(angle) {
            return angle * Math.PI / 180
        }
    </script>
</body>

</html>

zyronon
9 声望3 粉丝

我爱前端