通常要做一个时钟,必定离不开 JS 定时器。明天换一种思路,用 CSS 来实现一个时钟,如下:
你也能够拜访这个 CSS time (codepen.io)查看实际效果
当然借用了一点点 JS 用于初始化工夫,整个时钟的运行都是由 CSS 实现的,有很多你可能不晓得的小技巧,一起看看吧
一、数字的变换
先看看数字是如何变换的。
在以前,如果要实现数字的递增变动,可能须要提前准备好这些数字,例如像这样
<span>
<i>1</i>
<i>2</i>
...
<i>59</i>
</span>
而后通过扭转位移来实现。
然而,当初有更简洁的形式能够实现了,那就是 CSS @property,不理解这个的能够参考这篇文章:CSS @property,让不可能变可能。这是干什么的呢?简略来讲,能够自定义属性,在这个例子中,能够让数字像色彩一样进行过渡和动画,可能不太懂,间接看例子吧
假如 HTML 是这样的
<span style="--num: 0"></span>
咱们让这个自定义变量在页面中展现进去,单纯的 content
无奈间接显示自定义变量,须要借助定时器,有趣味的能够参考这篇文章:小 tips: 如何借助 content 属性显示 CSS var 变量值
span::after{counter-reset: num var(--num);
content: counter(num);
}
而后,能够通过 :hover
扭转这个数字
span:hover::after{--num: 59}
很僵硬的从 0 变成 59 了,十分合乎惯例。如果利用 CSS property,状况就不一样了,须要革新的中央很少,先定义一下--h
,而后给这个变量一个过渡工夫,如下
@property --h {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
span::after{transition: 1s --num;}
神奇的一幕产生了
看着如同不堪设想?能够这么了解,通过 @property
定义后,这个变量自身能够独自设置过渡了,而不再取决于一些仅反对过渡的属性(color
、width
等)。甚至还能加上动画,须要用到 steps
办法,设置动画周期为有限,如下
@keyframes num {
to {--num: 10}
}
span{animation: num 1s infinite steps(10);
}
时钟的根本运行原理就是这样了,一个有限循环的 CSS 动画!
二、时、分、秒
上面来看具体时、分、秒的实现,HTML 如下
<div class="time">
<span class="hour"></span>
<a class="split">:</a>
<span class="minitus"></span>
<a class="split">:</a>
<span class="seconds"></span>
</div>
给时、分、秒附上初始值
@property --h {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
@property --m {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
@property --s {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
.hour::after{counter-reset: hour var(--h);
content: counter(hour);
}
.minitus::after{counter-reset: minitus var(--m);
content: counter(minitus);
}
.seconds::after{counter-reset: seconds var(--s);
content: counter(seconds);
}
这里的时、分、秒并没有联动关系,所以各自都须要独自的动画。上面就须要思考一下🤔,如果用 CSS 动画来实现,每个的动画起始点和时长是多少呢?
没错,就是你想的,时针是 0-23
,时长24h
,分针是0-59
,时长60min
,秒针是0-59
,时长60s
,然而 CSS 中的工夫单位只反对 秒(s)
或者 毫秒(ms)
,所以这里须要转换一下,时长别离是60s*60*24
、60s*60
、60s
,具体实现如下:
@keyframes hour {
to {--h: 24}
}
@keyframes minitus {
to {--m: 60}
}
@keyframes seconds {
to {--s: 60}
}
.hour::after{counter-reset: hour var(--h);
content: counter(hour);
animation: hour calc(60s * 60 * 24) infinite steps(24);
}
.minitus::after{counter-reset: minitus var(--m);
content: counter(minitus);
animation: minitus calc(60s * 60) infinite steps(60);
}
.seconds::after{counter-reset: seconds var(--s);
content: counter(seconds);
animation: seconds 60s infinite steps(60);
}
这里为了便于察看,将工夫调快了 10 倍(60s => 6s),如下
三、时、分、秒主动补零
下面的布局有个问题,1 位数和 2 位数宽度变动导致时钟整体都在“晃动”,所以须要在 1 位数时补上一个“0”。对于 CSS 补零,之前在这篇文章中提到了 3 种计划,因为这里用了计数器,所以间接抉择更改计数器款式的办法,通过 decimal-leading-zero
来实现,具体做法如下
.hour::after{
/**/
content: counter(hour, decimal-leading-zero);/* 增加计数器款式 */
}
这样就谐和多了
四、工夫初始化
方才都从 00:00:00
开始了,所以须要手动指定一下初始工夫。假如当初是19:26:30
,如何初始化呢?
这里须要用 animation-delay
来提前静止到将来指定地位,为了不便管制,应用三个变量 --dh
、--dm
、--ds
来示意初始工夫,留神,因为 animation-delay
也只反对 秒(s)
或者 毫秒(ms)
,所以也同样须要转换,实现如下
:root{
--dh: 19;
--dm: 26;
--ds: 30;
}
.hour::after{
/**/
animation: hour calc(60s * 60 * 24) infinite steps(24);
animation-delay: calc(-60s * 60 * var(--dh) );
}
.minitus::after{
/**/
animation: minitus calc(60s * 60) infinite steps(60);
animation-delay: calc(-60s * var(--dm) );
}
.seconds::after{
/**/
animation: seconds 60s infinite steps(60);
animation-delay: calc(-1s * var(--ds) );
}
是不是有点奇怪?分钟在秒钟走到 30 的时候才变动,晚了半分钟。起因是这样的,尽管从数字上看,分钟是 26,然而还要思考到秒钟的静止状况,比方像这种状况,分钟其实曾经走了一半,应该是 26.5(26 + 30 / 60),所以在计算时还须要加上偏移量。上面咱们通过 JS 获取实在的工夫,并修复偏移
const d = new Date()
const h = d.getHours();
const m = d.getMinutes();
const s = d.getSeconds();
document.body.style.setProperty('--ds', s)
document.body.style.setProperty('--dm', m + s/60)
document.body.style.setProperty('--dh', h + m/60 + s/3600)
这样就失常了
五、闪动的分隔符
为了时钟看起来更加“动感”,能够给分隔符加上闪动动画,代码如下
@keyframes shark {
0%, 100%{opacity: 1;}
50%{opacity: 0;}
}
.split{animation: shark 1s step-end infinite;}
当初看下最终的成果
残缺代码能够拜访 CSS time (codepen.io)
六、总结一下
想不到实现一个时钟成果,用到了那么多 CSS 常识和技巧,简略总结一下吧
- CSS 实现实质是有限循环的 CSS 动画
- 灵活运用 CSS calc 计算
- CSS 计数器能够将 CSS 变量通过 content 显示在页面
- 数字的变动当初能够通过 CSS @property 配合动画实现
- 时分秒的区别在于各自的动画时长、动画起始点不同
- CSS 主动补零能够参考之前的文章,这里采纳 decimal-leading-zero 实现
- 工夫初始化其实就是指定动画 delay 值
- 指定初始值时还须要思考到各自的偏移量,例如 19:30:30,此时的时针数字其实是 30.5
- 分隔符的闪动动画
其实整个实现过程就是一个一直思考、学习的过程,比方为了实现数字的变动,就必须去学习 @property 相干,为了实现补零,就须要去理解更深层次的计数器相干,还有用到的各种动画。最初,如果感觉还不错,对你有帮忙的话,欢送点赞、珍藏、转发❤❤❤