Canvas Draws Dynamic Progress Bar Ring

Posted by antwonw on Sat, 27 Jul 2019 05:11:09 +0200

Final effect

Definition of initial variables

let radius = 140 //Outer ring radius
let thickness = 20 //Ring thickness
let innerRadius = radius - thickness //Inner Ring Radius
let startAngle = -90 //Beginning Angle
let endAngle = 180 //End Angle
let x = 0 //Centroid x coordinates
let y = 0 //Centroid y coordinates
let canvas = document.getElementById('tutorial');
canvas.width = 300;
canvas.height = 300;

let ctx = canvas.getContext('2d');
ctx.translate(canvas.width / 2, canvas.height / 2);//Move the drawing origin to the center of the canvas
ctx.rotate(angle2Radian(225)) //Rotate the canvas 225 degrees
ctx.fillStyle = "#f2d7d7 "; // Initial fill color

II. Tool Approach

 //Calculating the coordinates of points on a ring
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
}

//Radian rotation angle
function radian2Angle(radian) {
    return 180 * radian / Math.PI
}

//radians
function angle2Radian(angle) {
    return angle * Math.PI / 180
}

3. Rendering methods

//renderer
function renderRing(startAngle, endAngle) {
     ctx.beginPath();

    //Drawing Outer Ring
    ctx.arc(x, y, radius, angle2Radian(startAngle), angle2Radian(endAngle))

    //Calculate the central coordinates of the first connection between the outer ring and the inner ring
    let oneCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, endAngle)

    //Draw the ring at the first connection between the outer ring and the inner ring
    ctx.arc(oneCtrlPoint.x, oneCtrlPoint.y, thickness / 2, angle2Radian(-90), angle2Radian(270))

    //Drawing inner rings
    ctx.arc(x, y, innerRadius, angle2Radian(endAngle), angle2Radian(startAngle), true)

    //Calculate the central coordinates of the second connection between the outer ring and the inner ring
    let twoCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, startAngle)

    //Draw the ring at the second connection between the outer ring and the inner ring
    ctx.arc(twoCtrlPoint.x, twoCtrlPoint.y, thickness / 2, angle2Radian(-90), angle2Radian(270))

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

Specific ideas:

For convenience, all places where radians are used in code change from angles to radians.

1. Draw the outer ring:

This step is the simplest and can be used directly according to the official usage method.

2. Draw the first ring at the junction of the outer ring and the inner ring.

First, the coordinates of the middle point at the end of the outer ring and the beginning of the inner ring are calculated.

The coordinate formulas for calculating points on a ring are as follows:

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

By substituting the above formulas, the coordinates of any point on the ring can be calculated, and then the ring can be drawn with the center of the ring and the thickness of the ring/2 as the radius.

3. Drawing inner rings

This step only needs to shorten the radius and change the starting and ending angles of the drawing outer ring.

4. Draw the second ring at the junction of the inner ring and the outer ring.

In the same way as the second step, the coordinates of the middle point at the beginning of the outer ring and the end of the inner ring are calculated, and then the circle is drawn with the center of the circle and the thickness of the circle/2 as the radius.

5. Complete filling
At this point, the circle is finished.

IV. Dynamic Progress Bar

 //Progress Bar Animation
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)

The end angle is calculated dynamically, and then a counter is set to repeat the rendering method.

V. Complete Code

<!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">branch</span> </span>
        <span class="title">Service score</span>
    </div>

    <script>
        let radius = 140 //Outer ring radius
        let thickness = 20 //Ring thickness
        let innerRadius = radius - thickness //Inner Ring Radius
        let startAngle = -90 //Beginning Angle
        let endAngle = 180 //End Angle
        let x = 0 //Centroid x coordinates
        let y = 0 //Centroid y coordinates
        let canvas = document.getElementById('tutorial');
        canvas.width = 300;
        canvas.height = 300;

        let ctx = canvas.getContext('2d');
        ctx.translate(canvas.width / 2, canvas.height / 2);//Move the drawing origin to the center of the canvas
        ctx.rotate(angle2Radian(225)) //Rotate the canvas 225 degrees
        ctx.fillStyle = "#f2d7d7 "; // Initial fill color

        renderRing(startAngle, endAngle)

        //Progress Bar Animation
        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)


        //renderer
        function renderRing(startAngle, endAngle) {
            ctx.beginPath();

            //Drawing Outer Ring
            ctx.arc(x, y, radius, angle2Radian(startAngle), angle2Radian(endAngle))

            //Calculate the central coordinates of the first connection between the outer ring and the inner ring
            let oneCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, endAngle)

            //Draw the ring at the first connection between the outer ring and the inner ring
            ctx.arc(oneCtrlPoint.x, oneCtrlPoint.y, thickness / 2, angle2Radian(-90), angle2Radian(270))

            // // Drawing inner rings
            ctx.arc(x, y, innerRadius, angle2Radian(endAngle), angle2Radian(startAngle), true)

            //Calculate the central coordinates of the second connection between the outer ring and the inner ring
            let twoCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, startAngle)

            //Draw the ring at the second connection between the outer ring and the inner ring
            ctx.arc(twoCtrlPoint.x, twoCtrlPoint.y, thickness / 2, angle2Radian(-90), angle2Radian(270))

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

        //Calculating the coordinates of points on a ring
        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
        }

        //Radian rotation angle
        function radian2Angle(radian) {
            return 180 * radian / Math.PI
        }

        //radians
        function angle2Radian(angle) {
            return angle * Math.PI / 180
        }
    </script>
</body>

</html>

Topics: Front-end IE