时钟动画Canvas代码怎么实现?

时钟动画是一种常见的动画效果,它能够以生动和有趣的方式展示时间的流逝。这种动画可以应用于各种场景,如教育、商业广告、游戏等。

要使时钟的秒针每秒移动一次,需要借助动画技术。虽然setInterval()、setTimeout()和requestAnimationFrame()都能实现这个效果,但因为时钟画面每秒只需重绘一次,所以三者中的任何一个都可以满足需求。

下面是一个HTML和JavaScript的示例,演示了使用Canvas如何绘制时钟动画:

示例效果与源代码:

运行效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<!DOCTYPE html>
<html>

<head>
<title>动画(时钟)</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="图形系统开发实战:基础篇 示例">
<meta name="author" content="hjq">
<meta name="keywords" content="canvas,ladder,javascript">
<script src="/examples/canvas-qa/canvas_1b/js/helper.js"></script>
</head>

<body style="overflow: hidden; margin:10px;">
<div style="display: inline-block;">
<canvas id="canvas" width="500" height="450" style="border:solid 1px #CCCCCC;"></canvas>
</div>
</body>
<script>
// 从页面中获取画板对象
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');

// 时钟大小
let x = 250, y = 225, radius = 200;
// 最后一次绘制时钟的秒数
let lastSecond = 0;

// 时钟动起来
requestAnimationFrame(drawClock);

/**
* 绘制时钟函数
*/
function drawClock() {
let second = Math.round(Date.now() / 1000);
// 当前秒数大于上一次绘制时钟的秒数时,重绘时钟
if (second > lastSecond) {
lastSecond = second;

// 清除已有内容
ctx.clearRect(0, 0, canvas.width, canvas.height);

// 绘制背景网格
drawGrid('lightgray', 10, 10, ctx);

// 绘制时钟外观
drawFacade();

// 绘制时钟刻度
let min = 60;
for (let i = 0; i < min; i++) {
drawScale(x, y, radius, i);
}

// 绘制时针、分针、秒针
drawPointer("h");
drawPointer("m");
drawPointer("s");
}

// 重绘
requestAnimationFrame(drawClock);
}

/**
* 绘制时钟外观
*/
function drawFacade() {
// 外圈渐变样式
const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius + 25);
gradient.addColorStop(0, "rgb(255,255,255)");
gradient.addColorStop(0.817, "rgb(255,255,255)");
gradient.addColorStop(0.837, "rgb(237,237,237)");
gradient.addColorStop(0.874, "rgb(77,77,77)");
gradient.addColorStop(0.904, "rgb(236,236,236)");
gradient.addColorStop(0.969, "rgba(77,77,77,168)");
gradient.addColorStop(1, "rgba(77,77,77,0)");

// 绘制外圈
ctx.save();
ctx.beginPath();
ctx.arc(x, y, radius + 10, 0, 2 * Math.PI);
ctx.fillStyle = gradient;
ctx.fill();

// 绘制中心
ctx.beginPath();
ctx.fillStyle = "gray";
ctx.arc(x, y, 10, 0, 2 * Math.PI)
ctx.fill();
ctx.restore();
}

/**
* 绘制时针、分针、秒针函数
*/
function drawPointer(type, value) {
let deg, beginX, endX;
let now = new Date();
ctx.save();

// 计算时针角度和长度,并设置样式
if (type == "s") { // 秒
deg = now.getSeconds() * 6 - 90;
beginX = -30;
endX = radius - 50; // 长
ctx.lineWidth = 4; // 细
ctx.strokeStyle = "gold";
} else if (type == "m") { // 分
deg = now.getMinutes() * 6 + (now.getSeconds() / 60 * 6) - 90;
beginX = -20;
endX = radius - 70;
ctx.lineWidth = 6;
ctx.strokeStyle = "#2400CC";
} else { // 时
deg = now.getHours() * 30 + (now.getMinutes() / 60 * 30) - 90;
beginX = -15;
endX = radius - 90;
ctx.lineWidth = 8;
ctx.strokeStyle = "#0018CC";
}

// 绘制
ctx.translate(x, y);
ctx.rotate(toRadians(deg));
ctx.beginPath();
ctx.moveTo(beginX, 0);
ctx.lineTo(endX, 0);
ctx.stroke();
ctx.restore();
}

/**
* 绘制时钟刻度函数
*/
function drawScale(x, y, radius, min) {
let deg = min * 6; // 每分钟为6°
let len = 20;
ctx.save();
// 按时钟中心旋转
ctx.translate(x, y);
ctx.rotate(toRadians(deg));

ctx.beginPath();
// 5的整数分钟的刻度要长一些粗一些
if (min % 5 == 0) {
ctx.lineWidth = 4;
ctx.moveTo(radius - 30, 0);
} else {
ctx.lineWidth = 2;
ctx.moveTo(radius - 20, 0);
}
ctx.lineTo(radius - 8, 0);
ctx.stroke();
ctx.restore();

// 每5分钟显示数字值
if (min % 5 == 0) {
ctx.save();
// 按按时钟中心旋转
ctx.translate(x, y);
ctx.rotate(toRadians(deg));
// 平移至文字所在位置,并反向旋转
ctx.translate(radius - 45, 0);
ctx.rotate(toRadians(-deg));
ctx.fillStyle = "red";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "bold 25px 黑体";
//
ctx.fillText((min > 45 ? (min - 45) : (min + 15)) / 5, 0, 0);
ctx.restore()
}
}

</script>

</html>

这里,我们选择使用requestAnimationFrame()来实现动画效果。这个方法会在屏幕刷新时执行,通常屏幕每秒刷新60次。由于时钟每秒只需重绘一次,所以我们增加了一个变量lastSecond来记录上次绘制时钟时的秒数。只有当当前秒数大于上次绘制的秒数时,我们才重绘一次时钟。这样既实现了时钟的走动效果,又减轻了计算机的负担。

这就是关于如何使用requestAnimationFrame()实现时钟动画的基本原理和步骤。希望对你有所帮助!