用 js canvas 做一个优雅的模拟时钟, canvas 教程实例
有很多次,我都想找到一个比较不错的,可以查看模拟时钟的网页。
有时候是想看下距离某个时间点还有多长时间,有时候是想看一下,两个时间点之间的间隔是多少。因为模拟时钟的排布比数字时钟要更直观。
但一直没有找到。
这些天闲的时候就想做个 canvas 模拟时钟玩,慢慢就做出效果来了。
目前的大致效果如下:
可访问的地址:
线上地址(白): http://kylebing.cn/tools/clock-a/
线上地址(黑): http://kylebing.cn/tools/clock-a?theme=black
github: https://github.com/KyleBing/animate-clock-canvas
一、需求
我对模拟时钟的需求有几个:
- 一定要优雅,不花哨
- 秒针一定要动的平滑,不是一秒一秒的动,而是要像 iOS 中的时钟那样
- 优雅,还是优雅,简洁而不简单
二、实现原理:表盘
你需要具备使用 canvas 绘制简单图形的能力,不需要复杂,会画个方块、圆、文字,就可以了。
很简单,直接看 MDN 官方文档上的例子就能看明白。
会了基础的操作之后,就说一下粗略的实现过程:
<!DOCTYPE html>
<html lang="zh-CN">
<body>
<canvas id='canvas' width="600" height="600" style="width: 600px; height: 600px; border: 1px solid black;"></canvas>
</body>
<script>
let canvasItem = document.querySelector('#canvas')
let ctx = canvasItem.getContext("2d")
ctx.fillStyle = 'magenta'
ctx.fillRect(0,0, 5, 20)
</script>
</html>
此时画面是这样的
1. 画12个小时的刻度
这里需要你会对 ctx 进行旋转,Math.PI
是半个圆的旋转角度,那么整个圆12等分后,每个刻度的旋转角度就是
let rotationAngle = Math.PI * (1/6)
这里需要注意的是,当你用 ctx.rotate() 方法旋转的时候,你旋转的是整个画布,并且它不会自动恢复到原来的样子。
也就是说,如果你 rotate() 了30度,那么画布就会在后面的绘画过程中都保持在 旋转了30度的这个状态。后面画的所有内容都是旋转 30 度的。
而这不是我们想要的,我们只需要在画这个表盘的时候旋转,在绘制完成之后恢复到原来的样子。
此时就需要 ctx 的两个方法了: save()
restore()
这两个方法的作用就是 save()
保存画布的一个状态,在后面再 restore()
恢复到保存状态时的状态。
比如我们在这个画布上旋转30度再写个文字试试:
<!DOCTYPE html>
<html lang="zh-CN">
<body>
<canvas id='canvas' width="600" height="600" style="width: 600px; height: 600px; border: 1px solid black;"></canvas>
</body>
<script>
let canvasItem = document.querySelector('#canvas')
let ctx = canvasItem.getContext("2d")
ctx.rotate(Math.PI / 6) // 旋转 30 度
ctx.fillStyle = 'magenta'
ctx.fillRect(0,0, 5, 20)
ctx.font = '50px Impact' // 字体设置
ctx.fillText('Rotate 30', 100,0) // 绘制字体
</script>
</html>
但是如果我们使用 save() 和 restore() 就能保证文字的角度正常了。
<!DOCTYPE html>
<html lang="zh-CN">
<body>
<canvas id='canvas' width="600" height="600" style="width: 600px; height: 600px; border: 1px solid black;"></canvas>
</body>
<script>
let canvasItem = document.querySelector('#canvas')
let ctx = canvasItem.getContext("2d")
ctx.save() // 保存旋转之前的画布状态
ctx.rotate(Math.PI / 6)
ctx.fillStyle = 'magenta'
ctx.fillRect(0,0, 5, 20)
ctx.restore()
ctx.font = '50px Impact'
ctx.fillText('Rotate 30', 100,50)
</script>
</html>
好,知道这些了就开始绘制表盘的 12 个刻度了。
let canvasItem = document.querySelector('#canvas')
let ctx = canvasItem.getContext("2d")
ctx.save() // 保存旋转之前的画布状态
for (let i=0;i<12;i++){
ctx.rotate(Math.PI / 6)
ctx.fillStyle = 'magenta'
ctx.fillRect(0,0, 5, 20)
}
ctx.restore()
ctx.font = '50px Impact'
ctx.fillText('Rotate 30', 100,50)
能看到上图中,这几个旋转是旋转了,但中心点不对,这里需要另外一个方法,就是 translate() ,用它将左上角的 0,0 点移动到画布的中间位置,再绘制。
let canvasItem = document.querySelector('#canvas')
let ctx = canvasItem.getContext("2d")
ctx.save() // 保存旋转之前的画布状态
ctx.translate(300, 300) // 将 0,0 移动到画布中间
for (let i=0;i<12;i++){
ctx.rotate(Math.PI / 6)
ctx.fillStyle = 'magenta'
ctx.fillRect(0,0, 5, 20)
}
ctx.restore()
ctx.font = '50px Impact'
ctx.fillText('Rotate 30', 100,50)
加个辅助线,再看,你应该参看到所有刻度线都没有位于中间。
let canvasItem = document.querySelector('#canvas')
let ctx = canvasItem.getContext("2d")
ctx.save() // 保存旋转之前的画布状态
ctx.translate(300, 300) // 将 0,0 移动到画布中间
for (let i=0;i<12;i++){
ctx.rotate(Math.PI / 6)
ctx.fillStyle = 'magenta'
ctx.fillRect(0,0, 10, 30)
}
ctx.restore()
drawRef