还在使用定时器吗(CSS|还在使用定时器吗?CSS 也能实现电子时钟)
欢迎关注我的公众号:前端侦探通常要做一个时钟,肯定离不开 JS 定时器。今天换一种思路,用 CSS 来实现一个时钟,如下:
文章图片
你也可以访问这个CSS time (codepen.io)查看实际效果
当然借用了一点点 JS 用于初始化时间,整个时钟的运行都是由 CSS 完成的,有很多你可能不知道的小技巧,一起看看吧
一、数字的变换 先看看数字是如何变换的。
在以前,如果要实现数字的递增变化,可能需要提前准备好这些数字,例如像这样
1
2
...
59
然后通过改变位移来实现。
但是,现在有更简洁的方式可以实现了,那就是 CSS @property,不了解这个的可以参考这篇文章:CSS @property,让不可能变可能。这是干什么的呢?简单来讲,可以自定义属性,在这个例子中,可以让数字像颜色一样进行过渡和动画,可能不太懂,直接看例子吧
假设 HTML 是这样的
我们让这个自定义变量在页面中展示出来,单纯的
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: '';
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 如下
::
【还在使用定时器吗(CSS|还在使用定时器吗?CSS 也能实现电子时钟)】给时、分、秒附上初始值
@property --h {
syntax: '';
inherits: false;
initial-value: 0;
}
@property --m {
syntax: '';
inherits: false;
initial-value: 0;
}
@property --s {
syntax: '';
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 补零,之前在这篇文章: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
- 分隔符的闪烁动画
欢迎关注我的公众号:前端侦探
推荐阅读
- 在Java中使用FileStreams复制文件
- C#变量使用详细指南
- CSS边框样式使用详细介绍
- android获得屏幕高度和宽度(display中getSize(Point)方法使用)
- Android Service demo例子使用详解
- Python OpenCV – cv2.polylines()方法使用示例
- swift集合的使用和操作全解 – Swift最新教程
- swift数组的使用和操作全解 – Swift最新教程
- 前端基础和框架使用|Vue的异步渲染axios
- 如何使用JavaScript将JSON字符串转换为JSON对象数组()