Make a fancy clock

Laws are created and discovered, and memory is the derivation and extraction of laws

Recently, I was learning front-end visualization, and by the way, I did some demos that I found fun. When I was learning canvas, I remembered that the first homework assigned to us by the computer graphics teacher in college was to draw a clock. At that time, I used C# to draw. Now I find that it is more convenient to draw using the imperative method of canvas. I will record and share it here. one time.

  • Consider what a clock is made of:
  1. dial
  2. timescale
  3. hour hand
  4. minute hand
  5. second hand
  • Then we design a class that provides the following methods:
  1. Create a watch face
  2. draw scale
  3. draw the hour hand
  4. draw the minute hand
  5. Draw the second hand
  6. Draw in seconds
  • The classes are as follows:
// clock.ts 此文件需要 tsc编译
class Clock {
  // 表盘canvas
  canvas = null;
  // 绘制上下文
  context = null;
  // 时间刻度文本
  timeText = [ "XII","I", "II", "III","IV", "V", "VI","VII", "VIII","IX","X", "XI" ];
  // 时间刻度角度
  timeTheta = Array.from({ length: 12 }, (_, i) => (i / 12) * 2 * Math.PI);
  // 表盘宽度
  width = 100;
  // 表盘高度
  height = 100;
  // 构造函数
  constructor(el) {
    this.initClockBoard(el);
    this.width = this.canvas.width;
    this.height = this.canvas.height;
    this.start();
  }
  // 初始化表盘
  initClockBoard(el) {
  }
  // 初始化时间刻度
  drawTimeText() {
  }
  // 绘制时间针
  drawTimeCircle(r = 100, color = "blue", theta = -0.5 * Math.PI) {
  }
  // 绘制秒针
  drawSecondCircle(size = 200, color = 'blue') {
  }
  // 绘制分针
  drawMinuteCircle(size = 180,color = 'yellow') {
  }
  // 绘制时针
  drawHourCircle(size = 150,color = 'red') {
  }
  // 定时器绘制时间走动
  drawTimer() {
  }
  // 启动器
  start() {
  }
}
复制代码

In this way, we can create a clock by passing in the canvas element directly during initialization:

const clock = new Clock('canvas')
复制代码

The corresponding HTML is relatively simple:


<!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>candbox</title>
    <style>
      canvas {
        width: 256px;
        height: 256px;
        border: 1px solid #333;
      }
    </style>
    <script src="clock.js"></script>
  </head>
  <body>
    <canvas width="512" height="512"></canvas>
    <script src="index.js"></script>
  </body>
</html>
复制代码

Now let's take a look at how each method is simply implemented

Initialize the watch face

The dial only needs to load the canvas:

// 初始化表盘
  initClockBoard(el) {
    this.canvas = document.querySelector(el);
    this.context = this.canvas.getContext("2d");
  }
复制代码

Of course, a more rigorous judgment can be made on the input parameters here, and the more acquired canvases can be standardized, which is omitted here.

Initialize the timescale

The time scale bisects a disc from 1 to 12. We divide the angle 2π of the entire circle into 12 equal parts, and then draw them at 12 positions at the same distance from the dot:


// 初始化时间刻度
  drawTimeText() {
    this.context.save();
    this.context.clearRect(0, 0, this.width, this.height);
    this.context.translate(this.width / 2, this.height / 2);
    this.timeText.forEach((text, i) => {
      this.context.save();
      this.context.rotate(this.timeTheta[i]);
      this.context.font = "20px Arial";
      this.context.fillStyle = "black";
      this.context.fillText(text, 0, -220);
      this.context.restore();
    });
    this.context.restore();
  }
复制代码
draw the time hand

Here we simply use the fan shape as the needle, and it is relatively simple to draw the fan shape:

  // 绘制时间针
  drawTimeCircle(r = 100, color = "blue", theta = -0.5 * Math.PI) {
    this.context.save();
    this.context.beginPath();
    this.context.arc(this.width / 2, this.height / 2, r, -0.5 * Math.PI, theta);
    this.context.lineTo(this.width / 2, this.height / 2);
    this.context.fillStyle = color;
    this.context.fill();
    this.context.restore();
  }
复制代码
Draw second hand minute hand hour hand

We only need to draw the hour hand through the previous method of drawing the time hand. We need to provide the length, color and angle of each hour hand. The angle is converted according to the time ratio, and we need to pay attention to two points:

  1. When drawing the hour hand, because it is a 24-hour system, but the dial is 12 hours, when the time is greater than 12, it needs to be converted accordingly
  2. When the time is rounded up, the dial and scale need to be redrawn

details as follows:

// 绘制秒针
  drawSecondCircle(size = 200, color = 'blue') {
    const second = new Date().getSeconds();
    const secondTheta = -0.5 * Math.PI + (second / 60) * 2 * Math.PI;
    if (second === 0) {
      this.context.clearRect(0, 0, this.width, this.height);
      this.drawTimeText();
    }
    this.drawTimeCircle(size, color, secondTheta);
  }
  // 绘制分针
  drawMinuteCircle(size = 180,color = 'yellow') {
    const minute = new Date().getMinutes();
    const minuteTheta = -0.5 * Math.PI + (minute / 60) * 2 * Math.PI;
    if (minute === 0) {
      this.context.clearRect(0, 0, this.width, this.height);
      this.drawTimeText();
    }
    this.drawTimeCircle(size, color, minuteTheta);
  }
  // 绘制时针
  drawHourCircle(size = 150,color = 'red') {
    const hour = new Date().getHours();
    const hourTheta =
      -0.5 * Math.PI +
      (hour / 12 > 1 ? hour / 12 - 1 : hour / 12) * 2 * Math.PI;
    if (hour === 0) {
      this.context.clearRect(0, 0, this.width, this.height);
      this.drawTimeText();
    }
    this.drawTimeCircle(size, color, hourTheta);
  }
复制代码
draw time walk

To move the time, we use the requestAnimationFrame frame function to draw. Each frame can redraw the time scale and hour hand. It should be noted that the callback function of requestAnimationFrame needs to use an arrow function, otherwise the internal method of the class cannot be read:

  // 定时器绘制时间走动
  drawTimer() {
    this.drawTimeText();
    this.drawSecondCircle();
    this.drawMinuteCircle();
    this.drawHourCircle();
    requestAnimationFrame(() => {
      this.drawTimer();
    });
  }
复制代码
start function

Use the frame function to call the time moving function

  // 启动器
  start() {
    requestAnimationFrame(() => {
      this.drawTimer();
    });
  }
复制代码

Finally look at the effect:

6BDB3B16-7D73-4679-823D-3C7C7BE819E2.png

with code

Guess you like

Origin juejin.im/post/7086775119443394591