还在使用定时器吗?有点离谱的 CSS 电子时钟

Posted 前端开发博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了还在使用定时器吗?有点离谱的 CSS 电子时钟相关的知识,希望对你有一定的参考价值。

关注公众号 前端开发博客,领27本电子书

回复加群,自助秒进前端群

通常要做一个时钟,肯定离不开 JS 定时器。今天换一种思路,用 CSS 来实现一个时钟,如下:

Kapture 2022-03-04 at 16.28.36

你也可以访问这个CSS time (codepen.io)[1]查看实际效果

当然借用了一点点 JS 用于初始化时间,整个时钟的运行都是由 CSS 完成的,有很多你可能不知道的小技巧,一起看看吧

一、数字的变换

先看看数字是如何变换的。

在以前,如果要实现数字的递增变化,可能需要提前准备好这些数字,例如像这样

<span>
 <i>1</i>
  <i>2</i>
  ...
  <i>59</i>
</span>

然后通过改变位移来实现。

但是,现在有更简洁的方式可以实现了,那就是 CSS @property[2],不了解这个的可以参考这篇文章:CSS @property,让不可能变可能[3]。这是干什么的呢?简单来讲,可以自定义属性,在这个例子中,可以让数字像颜色一样进行过渡和动画,可能不太懂,直接看例子吧

假设 html 是这样的

<span style="--num: 0"></span>

我们让这个自定义变量在页面中展示出来,单纯的 content无法直接显示自定义变量,需要借助定时器,有兴趣的可以参考这篇文章:小tips: 如何借助content属性显示CSS var变量值[4]

span::after
  counter-reset: num var(--num);
  content: counter(num);
image-20220304165629730

然后,可以通过:hover改变这个数字

span:hover::after
  --num: 59
Kapture 2022-03-04 at 17.09.17

很生硬的从 0 变成 59 了,非常符合常规。如果利用 CSS property,情况就不一样了,需要改造的地方很少,先定义一下--h,然后给这个变量一个过渡时间,如下

@property --h  
  syntax: '<integer>';
  inherits: false;
  initial-value: 0;

span::after
  transition: 1s --num;

神奇的一幕发生了

Kapture 2022-03-04 at 17.14.07

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

@keyframes num 
  to 
    --num: 10
  

span
  animation: num 1s infinite steps(10);

时钟的基本运行原理就是这样了,一个无限循环的 CSS 动画!

Kapture 2022-03-04 at 17.26.23

二、时、分、秒

下面来看具体时、分、秒的实现,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);
image-20220304183029959

这里的时、分、秒并没有联动关系,所以各自都需要单独的动画。下面就需要思考一下🤔,如果用 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),如下

Kapture 2022-03-04 at 18.57.48

三、时、分、秒自动补零

上面的布局有个问题,1 位数和 2 位数宽度变化导致时钟整体都在“晃动”,所以需要在1位数时补上一个“0”。关于 CSS 补零,之前在文章 CSS 补全字符串?中提到了 3 种方案,由于这里用了计数器,所以直接选择更改计数器样式的方法,通过decimal-leading-zero来实现,具体做法如下

.hour::after
  /**/
  content: counter(hour, decimal-leading-zero);/*添加计数器样式*/

这样就和谐多了

Kapture 2022-03-04 at 19.04.13

四、时间初始化

刚才都从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) );
Kapture 2022-03-04 at 19.36.13

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

这样就正常了

Kapture 2022-03-04 at 19.45.04

五、闪烁的分隔符

为了时钟看起来更加“动感”,可以给分隔符加上闪烁动画,代码如下

@keyframes shark 
  0%, 100%
    opacity: 1;
  
  50%
    opacity: 0;
  

.split
  animation: shark 1s step-end infinite;

现在看下最终的效果

Kapture 2022-03-04 at 19.49.03

完整代码可以访问 CSS time (codepen.io)[5]

六、总结一下

想不到实现一个时钟效果,用到了那么多 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 相关,为了实现补零,就需要去了解更深层次的计数器相关,还有用到的各种动画。最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发❤❤❤

参考资料

[1]

CSS time (codepen.io): https://codepen.io/xboxyan/pen/OJOdvyy

[2]

CSS @property: https://developer.mozilla.org/zh-CN/docs/Web/CSS/@property

[3]

CSS @property,让不可能变可能: https://juejin.cn/post/6951201528543707150

[4]

小tips: 如何借助content属性显示CSS var变量值: https://www.zhangxinxu.com/wordpress/2019/05/content-css-var/

[5]

CSS time (codepen.io): https://codepen.io/xboxyan/pen/OJOdvyy

- EOF -

推荐阅读  点击标题可跳转

让页面无懈可击!关于 CSS,你该做好这 20 件事

CSS 绘制一个时钟

如何用一行 CSS 实现 10 种现代布局

最后

我是小前端,欢迎大家围观我的朋友圈,搞搞技术,吹吹牛逼。我的微信:kujian89,秒添加,回复加群,可以进入 500人前端群。

关注公众号:前端开发博客

  1. 回复「小抄」,领取Vue、javascript 和 WebComponent 小抄 PDF

  2. 回复「Vue脑图」获取 Vue 相关脑图

  3. 回复「思维图」获取 JavaScript 相关思维图

  4. 回复「简历」获取简历制作建议

  5. 回复「简历模板」获取精选的简历模板

  6. 回复「加群」进入500人前端精英群

  7. 回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。

  8. 回复「知识点」下载高清JavaScript知识点图谱

 👍🏻 点赞 + 在看 支持小编

以上是关于还在使用定时器吗?有点离谱的 CSS 电子时钟的主要内容,如果未能解决你的问题,请参考以下文章

程序员教你如何用canvas自适应圆形时钟绘制

纯css3制作小时钟带摇摆

基于单片机的电子时钟(有报时和定时)

还在为满意的渐变色发愁吗?10+个网站帮你轻松实现

还在为满意的渐变色发愁吗?10+个网站帮你轻松实现

还在为满意的渐变色发愁吗?10+个网站帮你轻松实现