前言
"一行代码能做什么?"------有人用一行代码写出圣诞树,有人用一行代码写出 3D 隧道。
今天我想带你回到最原始的画布:HTML5 Canvas。
我们只用 一个 <canvas>
标签 + 一份被注释到"膨胀"的源码 ,做一只实时跳动的机械时钟 。
读完你不仅能照抄代码,还能彻底搞懂:
- 为什么
requestAnimationFrame
比setInterval
更适合动画 - 三角函数在前端到底怎么用
- 如何把"时间"映射成"角度"再映射成"坐标"
在线预览
把下面 60 行"真身"粘进任意 .html
文件,双击即可运行:
(我已经把注释写成了"伪 600 行"------每行都带中文讲解,复制即食)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>Canvas Clock | 注释版</title>
<style>
/* 让时钟水平居中,再给个浅灰背景+深色边框,高级感拉满 */
canvas {
display: block;
margin: 50px auto;
background: #f4f4f4;
border: 2px solid #333;
border-radius: 50%; /* 圆表盘当然要圆角 */
box-shadow: 0 0 20px rgba(0,0,0,.15);
}
</style>
</head>
<body>
<!-- 600×600 像素的画布,id 叫"clock",后面 JS 就靠它画画 -->
<canvas id="clock" width="600" height="600"></canvas>
<script>
/* 主绘制函数:每帧擦掉旧画面 -> 重新画新画面,形成动画 */
function drawClock() {
const canvas = document.getElementById('clock');
const ctx = canvas.getContext('2d'); // 拿到 2D 画笔
const cx = 300, cy = 300, r = 250; // 圆心(cx,cy) 半径 r
/* 1. 清屏:把上一帧的画面整个擦掉,避免拖影 */
ctx.clearRect(0, 0, 600, 600);
/* 2. 表盘:一个灰色大圆 */
ctx.beginPath();
ctx.arc(cx, cy, r, 0, Math.PI * 2);
ctx.fillStyle = '#ccc';
ctx.fill();
/* 3. 拿到当前时分秒 */
const now = new Date();
const h = now.getHours();
const m = now.getMinutes();
const s = now.getSeconds();
/* 4. 角度换算:把"时间"转成"顺时针角度",再减去 90° 让 0° 指向 12 点 */
const hAngle = ((h % 12) + m / 60) * 30 - 90; // 每小时 30°
const mAngle = (m + s / 60) * 6 - 90; // 每分钟 6°
const sAngle = s * 6 - 90; // 每秒 6°
/* 5. 画三根指针:颜色、长度、线宽各不相同,秒针用骚红色 */
drawHand(ctx, hAngle, 100, 12, '#333'); // 时针
drawHand(ctx, mAngle, 150, 8, '#555'); // 分针
drawHand(ctx, sAngle, 180, 3, '#e74c3c'); // 秒针
/* 6. 中心小圆点:盖住三根指针的尾巴,显得更像实物 */
ctx.beginPath();
ctx.arc(cx, cy, 10, 0, Math.PI * 2);
ctx.fillStyle = '#333';
ctx.fill();
/* 7. 请求下一帧:浏览器会在下一次重绘前再调 drawClock(),形成无限循环 */
requestAnimationFrame(drawClock);
}
/* 工具函数:画一根指针
angle:旋转角度(度) length:长度 width:线宽 color:颜色 */
function drawHand(ctx, angle, length, width, color) {
const rad = (angle * Math.PI) / 180; // 角度转弧度
const x = 300 + Math.cos(rad) * length;
const y = 300 + Math.sin(rad) * length;
ctx.beginPath();
ctx.moveTo(300, 300);
ctx.lineTo(x, y);
ctx.lineWidth = width;
ctx.strokeStyle = color;
ctx.lineCap = 'round'; // 圆角线帽,更柔和
ctx.stroke();
}
/* 启动!第一次手动调用,后面就靠 requestAnimationFrame 自动循环 */
drawClock();
</script>
</body>
</html>
关键知识点拆解
步骤 | 前端技巧 | 一句话记忆 |
---|---|---|
清屏 | ctx.clearRect |
动画第一定律:先擦后画 |
角度 | -90° 修正 |
数学 0° 指向 3 点,时钟 0° 指向 12 点 |
弧度 | Math.PI / 180 |
角度给人类看,弧度给三角函数看 |
指针终点 | cos 管 x,sin 管 y |
高中三角函数终于还给了体育老师 |
动画节流 | requestAnimationFrame |
浏览器帮你匹配屏幕刷新率,60 fps 不丢帧 |
还能怎么玩?
- 换皮肤:把表盘改成深色霓虹,秒针加 glow 阴影,瞬间赛博朋克。
- 加刻度 :用
for
循环画 60 个小圆点,12 个粗点代表小时。 - 加数字 :
ctx.fillText
把 1~12 画到对应位置,注意同样要转角度。 - 响应式 :把
600
改成min(window.innerWidth, window.innerHeight)
,再动态算圆心。 - 音效:每秒播放一次"滴答"采样,Web Audio API 只要 5 行。
结语
Canvas 像一张永远干不掉的水彩纸:
你可以在上面画像素、画粒子、画星空,也可以画一只最朴素的时钟。
"会动"的不只是指针,还有你对前端世界的好奇心。
把这份代码存进收藏夹,下次面试被问到"如何实现平滑动画"时,
把 requestAnimationFrame + 三角函数
这套组合拳打出来,
面试官大概率会点点头:嗯,基础很扎实。
源码仓库
GitHub 完整仓库
👉 gitter.com/koreation/canvas-clock 欢迎 Star & PR!
如果这篇文章帮到你,记得点个赞,
让你的时间,也像 Canvas 里的秒针一样,优雅前行。