前言
我为什么想写个钟?其实你上网胡乱一搜,资料便铺天盖地。但是我最烦的是搜着搜着,好不容易有点眉目了,就要你充个会员,真的很影响兴致。我承认,现在是一个知识付费的的大时代,包括音乐、视频、文档等等。当免费习以为常,付费就如鲠在喉。随着时间的推移,你终将败下阵来,然后付费变得习以为常。
扯远了,我为什么要写个钟?起因:写着玩儿,就是觉得这个东西很有趣。我可以把我它变成我喜欢的样子,用代码写出来的东西和买来的东西,意义截然不同。虽不惊艳,却乐在其中。 还会想起小时候,和小伙伴们一起用圆珠笔在手上笨拙地画一个歪歪扭扭的手表......
一、钟的大体结构
左思右想,索性采用清一色的 div 搭个架子:
- 首先来六根刻度线,交叉就是 12 根,足矣
- 然后来个遮罩层,把刻度线盖住,露出边边角角,作为刻度
- 再来时钟、分钟、秒钟各一个 div
- 最后就是修修补补的圆点点,再附上时期和星期
<body>
<div class="clock">
<!-- 刻度线 -->
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
<!-- 遮罩层 -->
<div class="mask"></div>
<!-- 时钟 -->
<div class="hour"></div>
<!-- 分钟 -->
<div class="minute"></div>
<!-- 秒钟 -->
<div class="second"></div>
<!-- 螺丝 -->
<div class="dotted-black"></div>
<div class="dotted-red"></div>
<div class="dotted-white"></div>
<!-- 日期和星期 -->
<div class="curtime"></div>
</div>
</body>
二、钟的大体样式
虽“美人”在骨不在皮,重要时刻还是捯饬捯饬:
- 刻度线先水平垂直居中,然后给刻度线设置旋转度数
- 把遮罩盖上去,同样水平垂直居中,只漏出最外层的一点,作为刻度
- 时钟、分钟、秒钟注意设置旋转中心,秒钟要复杂一丢丢
- 层级问题可以最后再看,我是文字上不去,所以加了层级 z-index
<style>
* {
padding: 0;
margin: 0;
}
.clock {
position: relative;
width: 200px;
height: 200px;
border-radius: 50%;
margin: 100px auto;
background-color: rgb(240, 241, 245);
box-shadow: 0 0 10px rgb(231, 235, 244) inset;
}
.clock .line {
position: absolute;
width: 3px;
height: 195px;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
background-color: rgb(195, 204, 215);
border-radius: 5px;
}
.clock .line:nth-child(2) {
transform: rotateZ(30deg);
}
.clock .line:nth-child(3) {
transform: rotateZ(60deg);
}
.clock .line:nth-child(4) {
transform: rotateZ(90deg);
}
.clock .line:nth-child(5) {
transform: rotateZ(120deg);
}
.clock .line:nth-child(6) {
transform: rotateZ(150deg);
}
.mask {
position: absolute;
z-index: 1;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
width: 180px;
height: 180px;
border-radius: 50%;
background-color: rgb(240, 241, 245);
}
.hour {
position: absolute;
z-index: 3;
left: 0;
right: 0;
bottom: 100px;
margin: auto;
width: 5px;
height: 50px;
background-color: black;
border-radius: 5px 5px 0 0;
transform-origin: bottom center;
}
.minute {
position: absolute;
z-index: 4;
left: 0;
right: 0;
bottom: 100px;
margin: auto;
width: 3px;
height: 70px;
background-color: black;
border-radius: 3px 3px 0 0;
transform-origin: bottom center;
}
.second {
position: absolute;
z-index: 6;
left: 0;
right: 0;
bottom: 90px;
margin: auto;
width: 2px;
height: 100px;
background-color: red;
border-radius: 1px;
transform-origin: 1px 90px;
}
.dotted-black {
position: absolute;
z-index: 5;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
width: 12px;
height: 12px;
background-color: black;
border-radius: 50%;
}
.dotted-red {
position: absolute;
z-index: 7;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
width: 8px;
height: 8px;
background-color: red;
border-radius: 50%;
}
.dotted-white {
position: absolute;
z-index: 7;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
width: 4px;
height: 4px;
background-color: white;
border-radius: 50%;
}
.curtime {
position: absolute;
z-index: 2;
left: 0;
right: 0;
top: 50px;
text-align: center;
color: #fff;
}
</style>
这是它最初的模样:素静。任尔东西南北风,它自岿然不动呀。
三、让时分秒跑起来
你既静如处子,想必动如脱兔:
- 采用 animation + @keyframs 使之旋转
- 秒针转一圈 60s ,分针转一圈 360s ,时针转一圈 43200s
- infinite 无限循环,linear 匀速运动
- 如果是 steps(60) 就会在规定时间内,走60下
.hour {
/* 补间动画*/
animation: hour 43200s infinite linear;
/* 逐帧动画*/
/* animation: hour 43200s infinite steps(43200); */
}
.minute {
animation: minute 3600s infinite linear;
/* animation: minute 3600s infinite steps(3600); */
}
.second {
animation: second 60s infinite linear;
/* animation: second 60s infinite steps(60); */
}
@keyframes hour {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
@keyframes minute {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
@keyframes second {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
看,它可以旋转了!
四、时分秒匹配当前时间
即便使用 CSS 动画就可以让时钟、分钟、秒钟有节奏地跑起来,但如何匹配当前的时间呢?
- 首先一圈 360°,那么一秒钟走 6°,一分钟走 6°,一小时走 360°
- 秒钟旋转度数:s * 6
- 分钟旋转度数:( m + s / 60 ) * 6
- 时钟旋转度数:( h + m / 60 + s / 3600 ) * 30
<script>
// ◆ 获取元素
let hour = document.querySelector('.hour')
let minute = document.querySelector('.minute')
let second = document.querySelector('.second')
let curtime = document.querySelector('.curtime')
let mask = document.querySelector('.mask')
let clock = document.querySelector('.clock')
let dottedB = document.querySelector('.dotted-black')
let dottedR = document.querySelector('.dotted-red')
let dottedW = document.querySelector('.dotted-white')
// ◆ 在此调用一次,解决初次进入页面的卡顿现象
setTime()
// ◆ 我的钟表每隔一秒走一次,这里会有 1s 的卡顿
setInterval(setTime, 1000)
// ◆ 封装时分秒走动的函数
function setTime() {
// 1.获取当前日期与时间等
let date = new Date()
let Y = date.getFullYear()
let M = date.getMonth() + 1
let D = date.getDate()
let d = date.getDay()
let s = date.getSeconds()
let m = date.getMinutes()
let h = date.getHours()
// 2.获取当前时分秒旋转的角度
let sdeg = s * 6
let mdeg = (m + s / 60) * 6
let hdeg = (h + m / 60 + s / 3600) * 30
// 3.把当前时分秒旋转的的角度赋值给 transform
second.style.transform = `rotate(${sdeg}deg) `
minute.style.transform = `rotate(${mdeg}deg) `
hour.style.transform = `rotate(${hdeg}deg)`
console.log(s, m, h)
curtime.innerHTML = `${M}月${D}日 星期${dayFormat(d)}`
// 3.晚上钟的样式
if (h >= 18 || h <= 7) {
mask.style.backgroundColor = 'rgb(37, 38, 40)'
clock.style.backgroundColor = 'rgb(37, 38, 40)'
hour.style.backgroundColor = 'rgb(173, 174, 176)'
minute.style.backgroundColor = 'rgb(194, 195, 197)'
second.style.backgroundColor = 'rgb(217, 70, 24)'
dottedB.style.backgroundColor = 'rgb(173, 174, 176)'
dottedR.style.backgroundColor = 'rgb(217, 70, 24)'
dottedW.style.backgroundColor = 'rgb(0, 0, 0)'
curtime.style.color = 'black'
}
}
// ◆ 封装星期函数
function dayFormat(num) {
const arr = ['日', '一', '二', '三', '四', '五', '六']
return arr[num]
}
</script>
(前面的 CSS 动画可以干掉了。)
嗯,还是晚上看起来更高级。goodnight!
五、发现钟的一个小 bug
不不不,还没完!最后渲染页面会有一个 1s 的误差,如果你仔细去比对秒针的走势,就会发现,咱们的钟始终比实际时间慢一秒,更严重的会变成 2s,乃至更多。如果你的电脑比较好,应该稳定在 1s 的误差。在 PC 端,我们需要加 1s 解决这个误差,这是重绘导致。如果你在手机上看这个 demo 就不需要 加 1s 了,因为手机的渲染性能足够优秀!
let s = date.getSeconds() + 1
结语
说实话,这个钟“折磨”了我两天,有两点:第一,旋转度数的计算;第二,那个 1s 的误差。我原以为这是一个小工程,没想到困扰我这么久。关于旋转度数的计算,我查了很多资料,网上答案千奇百怪,其实这就是一个小学数学题吧。至于那个 1s 的误差,我一开始百思不得其解,一直以为是我的度数计算有问题,也确实是有问题,最初时钟旋转计算掉了个 s/3600,可我把它加上时还是出了问题。最后问了一位资深的前端前辈,豁然开朗......