发明专利公开 -- CSS动画精准实现时钟

上一篇【setTimeout不准时,CSS精准实现计时器功能】的博文,最后提到了通过 CSS 动画实现计时器的方式。

本文详情描述如何通过 CSS 完整实现时钟效果,这也是团队 21 年专利的一项内容(专利公布号:CN114003087A)。

该发明专利主要解决大屏下通过 javascript 实现的时钟不准确的问题。通过 CSS 动画进行计时,避免同步阻塞卡顿的问题。

在这里插入图片描述

前置知识

伪元素

伪元素允许你对被选择元素的特定部分修改样式。

::after 用来创建一个伪元素,作为已选中元素的最后一个子元素。通常会配合content属性来为该元素添加装饰内容。这个虚拟元素默认是行内元素。

伪元素:before:after添加的内容默认是inline元素

  • 伪元素不属于文档,所以js无法操作它
  • 伪元素属于主元素的一部分,因此点击伪元素触发的是主元素的click事件
  • 块级元素才能有:before, :after,其实是不妥的,大部分行级元素也可以设置伪元素,但是像img可替换元素,因为其外观和尺寸有外部资源决定,那么如果外部资源正确加载,就会替换掉其内部内容,这时伪元素也会被替换掉,但是当外部资源加载失败时,设置的伪元素是可以起作用的。

示例

a::after {
    
    
	content: "→";
}

数据属性 data-*

HTML5 有扩展性的设计,它初衷是数据应与特定的元素相关联,但不需要任何定义。data-* 允许我们在标准内于 HTML 元素中存储额外的信息,而不需要使用类似于 classList 。

attr()

CSS 表达式 attr() 用来获取选择到的元素的某一 HTML 属性值,并用于其样式。它也可以用于伪元素,属性值采用伪元素所依附的元素。

attr() 理论上能用于所有的 CSS 属性但目前支持的仅有伪元素的 content 属性,其他的属性和高级特性目前是实验性的

示例

同 数据属性 结合,可以很好的实现相应的效果展示。

hover到 <a>标签,展示对应 data-hover内容

<style>
  a:hover::after {
      
      
    content: attr(data-hover);
  }
</style>
<body>
	<a href="javascript:void(0);" data-hover="hover展示内容">hover</a>
</body>

css animation

animation 属性是 animation-name,animation-duration, animation-timing-function,animation-delay,animation-iteration-count,animation-direction,animation-fill-mode 和 animation-play-state 属性的一个简写属性形式。

属性 说明 示例
animation-name 指定应用的一系列动画 animation1,animation2
animation-duration 指定一个动画周期的时长,单位 s 或者 ms 60s
animation-timing-function 在每一动画周期中执行的节奏 easelinearsteps(60)
animation-delay 定义动画于何时开始,单位 s 或者 ms 100ms
animation-iteration-count 定义动画在结束前运行的次数 infinite(无限次)、3
animation-direction 指示动画是否反向播放 normalalternatereverse
animation-fill-mode 设置 CSS 动画在执行之前和之后如何将样式应用于其目标 forwardsbackwards
animation-play-state 定义一个动画是否运行或者暂停 runningpaused

steps(number_of_steps, direction):定义了一个阶梯函数,将输出值的域等距地划分。

示例

动画周期的时长 10s ,等距划分为10步,每秒执行一次 timer,无限次循环执行。

animation: timer 10s infinite steps(10) forwards;

translate

transform: translate(x, y) / translate: x y; 平移变换。

示例

a:hover {
    
    
  translate: 200px 50px;
  /*等价于*/
  transform: translate(200px, 50px);
}

实现思路

在这里插入图片描述

步骤一:定义DOM

将时、分、秒进行DOM定义,针对时、分、秒将相关数据通过数据属性 data-* 进行绑定

<!-- 时 -->
<div class="card">
  <div class="card-hours"> <!-- 见步骤四其作用 -->
    <div class="hours" data-hours="18 19 20 21 22 23 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17"></div>
  </div>
</div>
<div class="card">:</div>
<!-- 分 -->
<div class="card">
  <div class="card-minutes">  <!-- 见步骤四其作用 -->
    <div class="minutes"
         data-minutes="58 59 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57">
    </div>
  </div>
</div>
<div class="card">:</div>
<!-- 秒 -->
<div class="card">
  <div class="seconds"
       data-seconds="50 51 52 53 54 55 56 57 58 59 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49">
  </div>
</div>

关于data-hoursdata-minutesdata-seconds 数据通过当前时间进行初始化(动态形式)。上述示例,初始时间为:18:58:50

上述,针对分钟、小时,多一层 DOM,<div class="card-hours"><div class="card-minutes"> 后续解释作用(见步骤四)。

步骤二:展示区域基础定义

这里针对:时、分、秒 统一设置。

在这里插入图片描述

设置一个固定大小的 font-size ,确保每行只能展示一个数字;同时,指定 line-height,确保可视区内只有展示一个数字。(为后续动画做准备)

body {
    
    
  font-size: 48px;
}
.card {
    
    
  display: inline-block;
  height: 68px;
  overflow: hidden;
}
.hours, .minutes, .seconds {
    
    
  width: 68px;
  line-height: 68px;
}
  • font-sizeline-height 属性,具有继承性,贯穿整个网页
  • font-sizeline-height 属性,和字体宽度没有必然的换算关系;如果希望字体是等宽的,需要使用等宽字体(如:Consolas、Monaco、monospace)

步骤三:实现「秒」

每一个周期60s,等距划分为60份(六十进制),每份1s,然后通过 translate 来展示下一个数值。

在这里插入图片描述

.seconds:after {
    
    
  display: block;
  content: attr(data-seconds);
  animation: counter 60s steps(60) infinite forwards;
}
@keyframes counter {
    
    
  from {
    
    
    translate: 0 0;
  }
  to {
    
    
    translate: 0 -100%;
  }
}

步骤四:实现「分、时」

对于分钟、小时,滚动展示的逻辑同秒一致,只是切割的份数及单位时间需要根据实际的来。

  • 分钟:等距 60 份(六十进制),一个周期 60 ∗ 60 = 3600 60 * 60 = 3600 6060=3600
  • 小时:等距 24 份(二十四进制),一个周期 $ 24 * 60 * 60 = 86400$
.minutes:after {
    
    
  display: block;
  content: attr(data-minutes);
  animation: counter 3600s steps(60) infinite forwards;
  animation-delay: 10s;	/* 延后执行 */
}

.hours:after {
    
    
  display: block;
  content: attr(data-hours);
  animation: counter 86400s infinite steps(24) forwards;
  animation-delay: 70s; /* 延后执行 */
}

分钟、小时与秒不同的是,第一次进制大概率不是整 60 或 24。

以上述实例解释:当前秒为 50,再过 10s(计算方式:$ 60 - 50 ),分钟就应该变成 59 ;同样的,再过 70 s (计算方式: ),分钟就应该变成 59;同样的,再过 70s(计算方式: ),分钟就应该变成59;同样的,再过70s(计算方式: 3600 - 58*60 - 50 $),小时就应该变成 19。

所以,上述 animation-delay 就是让固定周期的动画,稍后开始,等待的时间就是依据初始时间计算而来。

animation-delay 指定从应用动画到元素开始执行动画之前等待的时间量

解释清楚了延迟的作用,但问题是:首次的动画如何执行?(上述 animation 是从等待后的完整周期开始的)

步骤一,中提到的 <div class="card-hours"><div class="card-minutes"> 就是为此。

.card-minutes {
    
    
  height: 136px;
  overflow: hidden;
   /* 60-pastSeconds */
  animation: delay-counter 10s steps(1) 1 forwards;
}

.card-hours {
    
    
  height: 136px;
  overflow: hidden;
  /* 3600-pastMinutes*60-pastSeconds */
  animation: delay-counter 70s steps(1) 1 forwards;
}
@keyframes delay-counter {
    
    
  from {
    
    
    translate: 0 0;
  }
  to {
    
    
    translate: 0 -50%;
  }
}

上述动画只执行一次(初始化)

总结

CSS 动画运行效果良好,甚至在低性能的系统上。渲染引擎会使用跳帧或者其他技术以保证动画表现尽可能的流畅。

同时动画在各个执行阶段,也提供了相应的事件,这里暂不展开,有诉求的可以查看相关 MDN AnimationEvent

猜你喜欢

转载自blog.csdn.net/ligang2585116/article/details/130640692