不用定时器,CSS 也能实现电子时钟

  • 1,962 views
  • 阅读模式
通常要做一个时钟,肯定离不开 JS 定时器。今天换一种思路,用 CSS 来实现一个时钟,如下:不用定时器,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);
}

不用定时器,CSS 也能实现电子时钟

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

span:hover::after{
  --num: 59
}

不用定时器,CSS 也能实现电子时钟

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

@property --h { 
  syntax: '<integer>';
  inherits: false;
  initial-value: 0;
}
span::after{
  transition: 1s --num;
}

神奇的一幕发生了

不用定时器,CSS 也能实现电子时钟

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

@keyframes num {
  to {
    --num: 10
  }
}
span{
  animation: num 1s infinite steps(10);
}

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

不用定时器,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 也能实现电子时钟

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

三、时、分、秒自动补零

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

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

这样就和谐多了

不用定时器,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 也能实现电子时钟

是不是有点奇怪?分钟在秒钟走到 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 也能实现电子时钟

五、闪烁的分隔符

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

@keyframes shark {
  0%, 100%{
    opacity: 1;
  }
  50%{
    opacity: 0;
  }
}
.split{
  animation: shark 1s step-end infinite;
}

现在看下最终的效果

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

参考资料

  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

欢迎关注原作者公众号:前端侦探

weinxin
扫码关注微信公众号--IT老五
微信扫一扫关注公众号,获取更多实用app,订阅地址不定时更新
IT老五
  • 版权声明 本文源自 前端侦探-XboxYan 整理 发表于 2023-01-15 21:15:56
  • 转载请务必保留本文链接:https://itlao5.com/10305.html
评论  0  访客  0
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定