Today let’s use canvas to draw a round progress bar to drive the drawing effect

Knowledge points include

  • The basics of Canvas
  • Commonly used small skills slow motion formula; Xy coordinate calculation of a point on the arc; The use of requestAnimationFrame

Details in the code

<canvas id="cav" width="400" height="400"></canvas>

<script>

    class circleProgress {
        constructor(params) {
        
            /*
                需要传入的参数
                el canvas 元素
                progress  真实的进度比例 0 - 1  之间 100% 就是1;
                number 当前进度表示的数额
            */  
            const { el, progress, number } = params;
            
            // Get canvas element and its 2D context
            const cav = document.querySelector(el);
            this.ctx = cav.getContext('2d');
            
            // Get the width and height information of the Canvas element to calculate the coordinate of the circle progress
            const { width, height } = cav.getBoundingClientRect();
            this.w = width;
            this.h = height;
            this.x = width / 2;
            this.y = height / 2;
            this.r = 60;
            
            Math.pi / 180; math.pi / 180; math.pi / 180;
            this.angle = Math.PI / 180;
            
            // Start and end path degrees of circular progress
            this.beginDeg = 140;
            this.endDeg = 40;
            
            // Calculate the actual end path Angle of the progress bar
            this.progressDeg = this.beginDeg + (360 - this.beginDeg + this.endDeg) * progress;
            
            this.aniDeg = this.beginDeg;
            this.number = number;
            this.move();
        }


        /* Obtain the xy coordinates of a point on the circle: x = center x + radius * math.pi (Angle of the point * math.pi /180) y = center y + radius * math.sin (Angle of the point * math.pi /180) */ 
        getPointPos(deg) {
            const { ctx, x, y, r, angle } = this;
            return {
                xPos: x + r * Math.cos(deg * angle),
                yPos: y + r * Math.sin(deg * angle)
            }
        }

        // Draw text
        drawFont(font) {
            const { ctx, w, h } = this;
            // Get the width of the text centered with the x coordinate of the calculated text
            ctx.font = 'bold 30px arial';
            const fontW = ctx.measureText(font).width;
            ctx.fillStyle = '#00aeef';
            ctx.fillText(font, (w - fontW) / 2, h / 2 + 12)}// Draw a circle
        drawCircle(color, endDeg) {
            const { ctx, x, y, r, angle } = this;
            ctx.beginPath();
            ctx.lineCap = "round";
            ctx.lineWidth = 8;
            ctx.strokeStyle = color;
            ctx.arc(x, y, r, 140 * angle, endDeg * angle);
            ctx.stroke();
            ctx.closePath();
        }



        // Draw the dots
        drawPoint() {
            const { ctx, x, y, r, angle, aniDeg } = this;
            // Draw a circle
            ctx.beginPath();
            ctx.fillStyle = "#00aeef";
            const { xPos, yPos } = this.getPointPos(aniDeg)
            ctx.arc(xPos, yPos, 8.0.2 * Math.PI);
            ctx.fill();
            ctx.closePath();
        }

        // Animate
        move() {
            const { ctx, x, y, r, angle, w, h, progressDeg, number } = this;
            let font = 0;
            let last = false;
            const draw = () = > {
                // Canvas animation beauty frame should be cleared before drawing
                ctx.clearRect(0.0, w, h);
                // Draw the background arc
                this.drawCircle('#ccc'.this.endDeg);
                // Draw the true progress arc
                this.drawCircle('#00aeef'.this.aniDeg);
                // Draw real progress dots
                this.drawPoint();
                
                // A = A + (B - A)/speed;
                // The formula will approach B infinitely, so we need to add a critical judgment;
                this.aniDeg = this.aniDeg + (progressDeg - this.aniDeg) / 20;
                font = font + (number - font) / 20;
                this.drawFont(Math.floor(font))
                if (last) { return; }
                if ((progressDeg - this.aniDeg > 1)) {
                    // Use requestAnimationFrame for more detailed animation
                    requestAnimationFrame(draw)
                } else {
                    // Critical judge the last drawing reached the target progress;
                    last = true;
                    this.aniDeg = progressDeg; font = number; draw(); } } draw(); }}const goProgress = new circleProgress({
        el: '#cav'.progress: 0.4.number: 4000
    })
    
</script>
Copy the code

The final result