翻页时钟不能用 innerhtml 直接更新,因其会销毁 dom 导致动画中断、重排卡顿、数字错位;必须用 css 分层位移 + js 精确控制变化位、animationend 事件与 animation-fill-mode: forwards,并动态计算数字高度校准时间。

翻页时钟不是靠 setInterval 每秒重写整个字符串实现的,它依赖 CSS 分层位移 + 精确 JS 控制时机。直接套用普通实时时钟逻辑会导致数字错位、动画撕裂或卡顿。
为什么不能直接用 innerHTML = new Date().toTimeString() 更新翻页时钟
翻页动画本质是两块数字面板(当前值 / 下一值)在遮罩内上下滑动,DOM 结构必须保持稳定。每次用 innerHTML 替换,会销毁并重建所有 .digit 元素,导致:
- 动画 class 被清空,
animationend事件丢失,后续翻页无法触发 - 硬件加速失效(
transform和will-change重置) - 浏览器强制重排(reflow),6 位数字全部重绘,性能骤降
- 数字高度错位:若
.number-panel的background-position-y没同步更新,新数字会显示成乱码
transform: translateY() 的位移值必须严格匹配单个数字高度
每位数字容器(如 .digit.hour-tens)内有两个 .number-panel,每个面板纵向排列 0–9 十个数字。假设字体行高为 48px,则面板总高应为 480px,且每个数字占 48px 高度。
翻页时关键位移只有两个:
- 旧数字上翻:从
transform: translateY(0)→translateY(-48px) - 新数字下落:从
transform: translateY(48px)→translateY(0)
任何偏差(比如写成 -50px 或漏掉单位)都会导致数字切口不齐、露底或悬空。务必用 JS 动态读取 getComputedStyle(el).lineHeight 计算,别硬编码。
JS 触发动画必须按「位」逐个处理,且监听 animationend
不能一次性给所有 .digit 加 flipping class。正确流程是:
- 对比上一秒的
getSeconds()、getMinutes()、getHours() - 只对变化的位(比如秒个位从
2→3)执行翻页逻辑 - 先给该位加
flip-upclass,等其animationend事件触发后,再更新下一位的background-position-y值,并加flip-downclass - 用
element.addEventListener('animationend', handler, { once: true })防止重复绑定
漏掉 { once: true } 或没做「变化位」过滤,会导致动画堆叠、数字跳变甚至卡死。
animation-fill-mode: forwards 是翻页收尾的关键
翻页动画结束后,元素必须停留在最终位置,否则会弹回原位。仅靠 @keyframes 定义终点不够,必须显式声明:
.digit.flipping .number-panel.current {
animation: flip-up 0.4s ease-out;
animation-fill-mode: forwards;
}
同理,新数字下落也需 animation-fill-mode: forwards。若多个动画(如 flip-up 和 flip-down)同时作用于同一元素,CSS 会冲突——必须确保它们分属不同子元素(如一个作用于 .current,另一个作用于 .next),且 class 切换有明确先后。
最易被忽略的是时间校准逻辑:单纯用 setInterval(() => {}, 1000) 在页面后台或高负载时会丢帧。真实项目中应每秒用 new Date().getTime() 校准,再计算毫秒差,只在真正跨秒时才触发动画。否则,你看到的“翻页”可能只是视觉假象,底层时间早已滞后两秒。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











