还在使用定时器吗(CSS|还在使用定时器吗?CSS 也能实现电子时钟)

欢迎关注我的公众号:前端侦探
通常要做一个时钟,肯定离不开 JS 定时器。今天换一种思路,用 CSS 来实现一个时钟,如下:
还在使用定时器吗(CSS|还在使用定时器吗?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); }

还在使用定时器吗(CSS|还在使用定时器吗?CSS 也能实现电子时钟)
文章图片

然后,可以通过:hover改变这个数字
span:hover::after{ --num: 59 }

还在使用定时器吗(CSS|还在使用定时器吗?CSS 也能实现电子时钟)
文章图片

很生硬的从 0 变成 59 了,非常符合常规。如果利用 CSS property,情况就不一样了,需要改造的地方很少,先定义一下--h,然后给这个变量一个过渡时间,如下
@property --h { syntax: ''; inherits: false; initial-value: 0; } span::after{ transition: 1s --num; }

神奇的一幕发生了
还在使用定时器吗(CSS|还在使用定时器吗?CSS 也能实现电子时钟)
文章图片

看着好像不可思议?可以这么理解,通过@property定义后,这个变量本身可以单独设置过渡了,而不再取决于一些仅支持过渡的属性(colorwidth等)。甚至还能加上动画,需要用到steps方法,设置动画周期为无限,如下
@keyframes num { to { --num: 10 } } span{ animation: num 1s infinite steps(10); }

时钟的基本运行原理就是这样了,一个无限循环的 CSS 动画!
还在使用定时器吗(CSS|还在使用定时器吗?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|还在使用定时器吗?CSS 也能实现电子时钟)
文章图片

这里的时、分、秒并没有联动关系,所以各自都需要单独的动画。下面就需要思考一下,如果用 CSS 动画来实现,每个的动画起始点和时长是多少呢?
没错,就是你想的,时针是0-23,时长24h,分针是0-59,时长60min,秒针是0-59,时长60s,但是 CSS 中的时间单位只支持秒(s)或者毫秒(ms),所以这里需要转换一下,时长分别是60s*60*2460s*6060s,具体实现如下:
@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),如下
还在使用定时器吗(CSS|还在使用定时器吗?CSS 也能实现电子时钟)
文章图片

三、时、分、秒自动补零 上面的布局有个问题,1 位数和 2 位数宽度变化导致时钟整体都在“晃动”,所以需要在1位数时补上一个“0”。关于 CSS 补零,之前在这篇文章:CSS 也能自动补全字符串?中提到了 3 种方案,由于这里用了计数器,所以直接选择更改计数器样式的方法,通过decimal-leading-zero来实现,具体做法如下
.hour::after{ /**/ content: counter(hour, decimal-leading-zero); /*添加计数器样式*/ }

这样就和谐多了
还在使用定时器吗(CSS|还在使用定时器吗?CSS 也能实现电子时钟)
文章图片

四、时间初始化 刚才都从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) ); }

还在使用定时器吗(CSS|还在使用定时器吗?CSS 也能实现电子时钟)
文章图片

是不是有点奇怪?分钟在秒钟走到 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)

这样就正常了
还在使用定时器吗(CSS|还在使用定时器吗?CSS 也能实现电子时钟)
文章图片

五、闪烁的分隔符 为了时钟看起来更加“动感”,可以给分隔符加上闪烁动画,代码如下
@keyframes shark { 0%, 100%{ opacity: 1; } 50%{ opacity: 0; } } .split{ animation: shark 1s step-end infinite; }

现在看下最终的效果
还在使用定时器吗(CSS|还在使用定时器吗?CSS 也能实现电子时钟)
文章图片

完整代码可以访问 CSS time (codepen.io)
六、总结一下 想不到实现一个时钟效果,用到了那么多 CSS 知识和技巧,简单总结一下吧
  1. CSS 实现本质是无限循环的 CSS 动画
  2. 灵活运用 CSS calc 计算
  3. CSS 计数器可以将 CSS 变量通过 content 显示在页面
  4. 数字的变化现在可以通过 CSS @property 配合动画实现
  5. 时分秒的区别在于各自的动画时长、动画起始点不同
  6. CSS 自动补零可以参考之前的文章,这里采用 decimal-leading-zero 实现
  7. 时间初始化其实就是指定动画 delay 值
  8. 指定初始值时还需要考虑到各自的偏移量,例如 19:30:30,此时的时针数字其实是 30.5
  9. 分隔符的闪烁动画
其实整个实现过程就是一个不断思考、学习的过程,比如为了实现数字的变化,就必须去学习 @property 相关,为了实现补零,就需要去了解更深层次的计数器相关,还有用到的各种动画。最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发???
欢迎关注我的公众号:前端侦探

    推荐阅读