This article gives you an idea of how to use the most commonly used Canvas APIS by drawing a clock. Even if you have never used it before, you can get a general idea of what Canvas can do and quickly remember how to use it when you come across it in your work.

First, we initialize a canvas element in HTML:

<canvas id="myCanvas" width="200" height="200" style="border: 1px solid black"></canvas>
Copy the code

Draw the dial

Our dial is made up of two circles, one large and one small. The core API for drawing the dial is context.arc.

The general usage is as follows:

ctx.arc(100.100.99.0.2 * Math.PI, true);
Copy the code

It means to draw a circle with (100,100) as the center and a radius of 99. The starting point of the coordinate system is the upper left vertex of the canvas on our page.

The first two parameters are the x and y coordinates of the center of the circle, the third parameter is the radius, the fourth parameter is the starting radian of the circle, the fifth parameter is the ending radian of the circle, and the last parameter is whether to draw the circle clockwise or counterclockwise (if true, clockwise).

The final effect is as follows:

To make it more realistic, let’s draw two circles as our dial:

ctx.beginPath();
ctx.arc(100.100.99.0.2 * Math.PI, true);

ctx.moveTo(194.100); // To avoid drawing extra line segments
ctx.arc(100.100.94.0.2 * Math.PI, true);
ctx.stroke();
Copy the code

You may have noticed that we added ctx.moveto (194, 100). Why? If I didn’t add it, I would draw the extra line segment,

So, we’re going to add that extra line of code, so let’s move the brush onto the inner circle.

Draw table heart

With the foundation of the previous step, this step is very simple, we just need to draw a small solid circle at the center. Notice that? In the previous step we used ctx.stroke to draw the circle, but we didn’t fill it with color. This is where we need to fill in the color, and we need to use ctx.fill in the last step.

ctx.beginPath(); // This also prevents drawing extra line segments ctx.arc(100, 100, 4, 0, 2 * math.pi, true); ctx.fill();Copy the code

The effect is shown below:

Without setting anything, the default color for the fill is black. You can specify its color using ctx.fillstyle before calling ctx.fill:

ctx.fillStyle = "#ddd"
Copy the code

Draw the hour hand

In this section, we will learn how to draw line segments. The apis used are context.lineTo and context.moveto. It’s pretty simple.

Initially, the origin of our entire canvas was in the upper left corner, but for convenience, we now change the origin to the center of the circle:

ctx.translate(100.100);
Copy the code

Next, we can draw a relatively simple line:

// Draw the minute hand ctx.moveto (0, 0); ctx.lineTo(0, -80); // Draw clockwise ctx.moveto (0, 0); ctx.lineTo(-45, 0); ctx.stroke();Copy the code

The overall effect is as follows:

A little shape ~

Let the watch move

Many students do not know that canvas can also have dynamic effects. In this section, I will show you how to realize the simplest dynamic effects of canvas.

For the sake of simplicity, let’s just move the second hand, because once you know how to move a hand, everything else is a piece of cake.

We divide the dial into four quadrants:

The division of the quadrants is the same as what we saw in junior high school, except that the Y-axis is down in the positive direction.

First we need two utility functions: getTimeQuadrant and computePoint.

GetTimeQuadrant is used to find what quadrant the current time is in:

function getTimeQuadrant(time) {
    if (time >= 60) {
        throw new Error('`time` is out of boundary');
    }
    return parseInt(time / 15) + 1;
}
Copy the code

We can use new Date().getseconds () to get the current number of seconds, and our computePoint function can calculate what the point position is based on the current number of seconds:

function computePoint(r, time) {
    let radian = time / 60 * 2 * Math.PI;

    let computePointObj = {
        1: () = > {
            return {
                x: r * Math.sin(radian),
                y: -r * Math.cos(radian)
            }
        },
        2: () = > {
            return {
                x: r * Math.sin(Math.PI - radian),
                y: r * Math.cos(Math.PI - radian)
            }
        },
        3: () = > {
            return {
                x: -r * Math.cos(1.5 * Math.PI - radian),
                y: r * Math.sin(1.5 * Math.PI - radian)
            }
        },
        4: () = > {
            return {
                x: -r * Math.sin(2 * Math.PI - radian),
                y: -r * Math.cos(2 * Math.PI - radian)
            }
        }
    }

    return computePointObj[getTimeQuadrant(time)]()
}
Copy the code

Set a timer and run a line every second:

setInterval(() = > {
    ctx.beginPath()
    ctx.moveTo(0.0);
    let { x, y } = computePoint(60.new Date().getSeconds());
    ctx.lineTo(x, y)
    ctx.closePath();

    ctx.stroke();
}, 1000)
Copy the code

But that doesn’t work, and we get the following results:

Although still pretty good, but this is not the result we want, what is the specific reason for this? Instead of erasing the previous result before the next line, we add, and the last code is:

setInterval(() = > {
    // Clear operation, the core API is clearRect
    ctx.arc(0.0.4.0.2 * Math.PI, true);
    ctx.clearRect(-60, -60.120.120);
    ctx.arc(0.0.4.0.2 * Math.PI, true);
    ctx.fill();

    / / second hand
    ctx.beginPath()
    ctx.moveTo(0.0);
    let { x, y } = computePoint(60.new Date().getSeconds());
    ctx.lineTo(x, y)
    ctx.closePath();

    ctx.stroke();
}, 1000)
Copy the code

This gives us a motion effect that moves every second, and we can add the minute hand, the second hand, according to this principle.

Our example is very simple, but basically covers all the commonly used Canvas APIS, including circle drawing, line segment drawing and dynamic effect. The rest of the students need you to explore, I hope you can harvest, thank you!

All codes:

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

ctx.beginPath();
ctx.arc(100.100.99.0.2 * Math.PI, true);

ctx.moveTo(194.100);
ctx.arc(100.100.94.0.2 * Math.PI, true);
ctx.stroke();

ctx.beginPath(); // This also prevents drawing extra line segments
ctx.arc(100.100.4.0.2 * Math.PI, true);
ctx.fill();

ctx.translate(100.100);

setInterval(() = > {
    ctx.arc(0.0.4.0.2 * Math.PI, true);
    ctx.clearRect(-60, -60.120.120);
    ctx.arc(0.0.4.0.2 * Math.PI, true);
    ctx.fill();

    / / second hand
    ctx.beginPath()
    ctx.moveTo(0.0);
    let { x, y } = computePoint(60.new Date().getSeconds());
    ctx.lineTo(x, y)
    ctx.closePath();

    ctx.stroke();
}, 1000)

function getTimeQuadrant(time) {
    if (time >= 60) {
        throw new Error('`time` is out of boundary');
    }
    return parseInt(time / 15) + 1;
}

function computePoint(r, time) {
    let radian = time / 60 * 2 * Math.PI;

    let computePointObj = {
        1: () = > {
            return {
                x: r * Math.sin(radian),
                y: -r * Math.cos(radian)
            }
        },
        2: () = > {
            return {
                x: r * Math.sin(Math.PI - radian),
                y: r * Math.cos(Math.PI - radian)
            }
        },
        3: () = > {
            return {
                x: -r * Math.cos(1.5 * Math.PI - radian),
                y: r * Math.sin(1.5 * Math.PI - radian)
            }
        },
        4: () = > {
            return {
                x: -r * Math.sin(2 * Math.PI - radian),
                y: -r * Math.cos(2 * Math.PI - radian)
            }
        }
    }

    return computePointObj[getTimeQuadrant(time)]()
}

Copy the code