canvas 实现多彩的圆环数字时钟
前言
最近在学习 canvas
的道路上一去不复返,刚好又在小破站学到了一个时钟效果,虽然之前也开发过相关的内容,但是这次的内容会比之前的更加有趣,依旧是使用 canvas
来实现,不同的是这次实现的方法跟以往有很大的区别。老规矩,先来看一下最终的实现效果,如图所示:
效果很简单,圆环时钟的时分秒都展示不同的颜色,并且中间时间的颜色还会根据当前时间的变化而变化,下面就跟着我一起来看一下如何实现这个多彩的圆环时钟吧!
圆环
在上一篇文章中,我们通过 canvas
的 ctx.arc()
方法绘制了多个炫彩的动态圆环,而这一节要实现这个圆环数字时钟,依旧需要使用到 ctx.arc()
方法。
首先我们还是先来编写相关的 html
和 css
,因为这一节的内容跟前面的文字有些不一样,因此我们这里还是给出完整的 html
和 css
代码,如下:
<canvas id="canvas"></canvas>
<div id="clock"></div>
可以看到 html
中除了有一个 canvas
标签外,还多了一个 id
为 clock
的 div
标签,它主要是为了实现中间数字时间而准备的。接下来我们再一起来看 css
相关的代码,如下:
*{margin: 0; padding: 0;}
body {
background: #333;
width: 100%;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
#clock {
position: absolute;
font-size: 50px;
font-weight: bold;
font-family: Arial, Helvetica, sans-serif;
}
css
相关的内容也很简单,主要是将 body
设置为 flex
布局,实现上下左右居中对齐,让页面中的所有元素都居中显示。接下来就该来实现 js
相关的内容了,这次我们依旧跟前面的文章一样,采用 TS + ES6
的写法来开发。
还是跟前面的文章中描述的一样,我们首先定义一个 Clock
类,通过面向对象的方法来实现这个圆环时钟的效果,初始化的代码如下:
class Clock {
canvas: HTMLCanvasElement;
ctx: CanvasRenderingContext2D;
clock: HTMLElement;
constructor() {
this.canvas = document.getElementById('canvas') as HTMLCanvasElement;
this.clock = document.getElementById('clock') as HTMLElement;
this.ctx = this.canvas.getContext('2d');
this.canvas.width = 400;
this.canvas.height = 400;
}
}
在 Clock
类的 constructor
函数中,我们定义了 canvas
的宽和高都是 400,这是因为我们要实现的圆环时钟不需要很大,当然你也可以自己随意的设置 canvas
的大小,这根据个人的喜好来就可以了。有了初始的内容,接下来我们就先将时钟的外部圆环画出来,那该如何实现呢?
在我们实现的所有 canvas
效果中,最常定义的两个函数分别是 draw
和 update
,这两个函数一般用于绘制和更新,在这个效果中同样也不例外。我们先来定义一个 draw
函数,然后在这个函数中将圆环时钟的三个圈先画出来,具体代码如下:
class Clock {
...other code
draw() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
const date = new Date();
// 根据当前时间获取时分秒对应的角度
// 每秒角度变化为6度,每毫秒角度变化为6/1000度
const secdeg = date.getSeconds() * 6 + date.getMilliseconds() * 6 / 1000;
const mindeg = date.getMinutes() * 6 + secdeg / 60;
// 小时转换为12小时制
const hourdeg = date.getHours() % 12 * 30 + mindeg / 60;
const lines = [hourdeg, mindeg, secdeg];
this.ctx.lineWidth = 10;
this.ctx.lineCap = 'round';
for (let i = 0; i < 3; i++) {
// 渲染表盘(底色)
this.ctx.beginPath();
this.ctx.arc(200, 200, 140 + i * 20, 0, Math.PI * 2);
this.ctx.strokeStyle = `hsl(210deg, 30%, 10%)`;
this.ctx.stroke();
// 渲染表带(走的时间)
this.ctx.beginPath();
this.ctx.arc(200, 200, 140 + i * 20, -Math.PI / 2, Math.PI / 180 * lines[i] - Math.PI / 2, false);
this.ctx.strokeStyle = `hsl(${i * 120}deg, 45%, 60%)`;
this.ctx.stroke();
}
}
animate() {
requestAnimationFrame(() => this.animate());
this.draw();
}
}
在 draw
方法中,我们需要借助 Date
这个对象来获取当前是时间,并根据当前的时分秒来生成对应的角度,最后通过循环动态的绘制出时分秒的三个圆环,通过上述的代码,最终实现的效果如下所示:
可以看到我们已经将时分秒的三个圆环绘制出来了,但是目前还看不出这是一个时钟,因此我们还需要添加最外层的刻度盘,方便我们知道当前的大概时间,修改 draw
方法即可,相关代码如下:
class Clock {
...other code
draw() {
...other code
for (let i = 0; i < 12; i++) {
const bx = 200 + 180 * Math.cos(i * 30 * Math.PI / 180);
const by = 200 + 180 * Math.sin(i * 30 * Math.PI / 180);
this.ctx.fillStyle = '#6fd08c';
this.ctx.beginPath();
this.ctx.arc(bx, by, 3, 0, Math.PI * 2);
this.ctx.fill();
}
}
}
再次循环 12次,获取到每个刻度的位置,然后依旧通过 ctx.arc()
方法来将这些圆点绘制在对应的刻度上,最终实现的效果如下所示:
通过上图可以看出,每个刻度是5秒钟,而一分钟是60秒,因此我们需要循环12次来渲染出这些刻度来。
光有刻度还不行,因为还无法准确的看出当前的时间,所以我们还需要继续来改造 draw
方法才行。还记得我们在 html
中添加的 id
为 clock
的 div
元素吗?它就是用来做电子时钟的显示用的,让我们一起来看代码,如下:
class Clock {
...other code
draw() {
...other code
const x = Math.cos((secdeg + 90) * Math.PI / 180);
const y = Math.sin((secdeg + 90) * Math.PI / 180);
this.clock.style.textShadow = `${x}px ${y}px 5px hsl(${mindeg | 0}, 50%, 70%)`;
this.clock.style.color = `hsl(${secdeg | 0}, 50%, 80%)`;
this.clock.innerText = `${('0' + date.getHours()).slice(-2)}:${('0' + date.getMinutes()).slice(-2)}:${('0' + date.getSeconds()).slice(-2)}`;
}
}
在 draw
方法的最后,我们通过获取到的 div
标签,设置它的 textShadow
属性,也就是文字的阴影,然后给文字添加对于的 color
值,最后再通过 innerText
这个 api
来给 div
标签设置对应的文字。这里依旧用的是前面的 Date
对象,通过 Date
对象的相关 api
来获取当前的时分秒,从而实现我们最终的圆环数字时钟效果。
完整的代码及效果可以在这里查看:
总结
这篇文章的实现非常的简单,但是其中包含的知识点还是非常多的,先不说 canvas
相关的操作,对于 Date
对象的使用,我们也应该格外的注意,因为在实际的开发中,我们需要经常与 Date
对象打交道,因此还是要对 Date
对象有一定的了解才行。
最后,如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,谢谢大家
往期回顾
不得不说,这个 canvas 的旋转半圆效果可能会闪瞎你的双眼🐶
转载自:https://juejin.cn/post/7156740899991453710