前言
最近在电影院看了一部AI主题的科幻惊悚片《Mercy》(极限审判),Chris Pratt 饰演的主角需要在 90 分钟内向 AI 法官证明自己的清白。
这部电影有个非常巧妙的设计——全片都有一个倒计时器在屏幕上显示,让观众和主角一起感受时间的紧迫。这个倒计时组件的设计相当精美,于是我决定用纯 CSS 来复刻一个类似的效果。
最终效果
先来看看最终实现的效果,采用了苹果液态玻璃(Liquid Glass)风格:
核心原理
这个倒计时效果的核心原理其实非常简单:垂直堆叠数字 + overflow:hidden + CSS动画位移。
1. 数字堆叠结构
每一位数字实际上是多个数字垂直堆叠在一个容器中,容器设置 overflow: hidden,只显示其中一个数字:
<div class="time-part seconds ones">
<div class="digit-wrapper">
<!-- 9,8,7,6,5,4,3,2,1,0,9 - 末尾重复9实现无缝循环 -->
<span class="digit">9</span>
<span class="digit">8</span>
<span class="digit">7</span>
<span class="digit">6</span>
<span class="digit">5</span>
<span class="digit">4</span>
<span class="digit">3</span>
<span class="digit">2</span>
<span class="digit">1</span>
<span class="digit">0</span>
<span class="digit">9</span>
<!-- 重复的9,用于无缝循环 -->
</div>
</div>
.time-part {
height: var(--digit-height);
overflow: hidden; /* 只显示一个数字 */
}
.digit {
display: inline-block;
height: var(--digit-height);
line-height: 1;
}
2. 动画位移实现倒计时
通过 CSS @keyframes 动画,让 digit-wrapper 向上位移,从而显示不同的数字:
@keyframes animate-10 {
0% {
transform: translateY(0);
} /* 显示 9 */
10% {
transform: translateY(calc(var(--digit-height) * -1));
} /* 显示 8 */
20% {
transform: translateY(calc(var(--digit-height) * -2));
} /* 显示 7 */
30% {
transform: translateY(calc(var(--digit-height) * -3));
} /* 显示 6 */
40% {
transform: translateY(calc(var(--digit-height) * -4));
} /* 显示 5 */
50% {
transform: translateY(calc(var(--digit-height) * -5));
} /* 显示 4 */
60% {
transform: translateY(calc(var(--digit-height) * -6));
} /* 显示 3 */
70% {
transform: translateY(calc(var(--digit-height) * -7));
} /* 显示 2 */
80% {
transform: translateY(calc(var(--digit-height) * -8));
} /* 显示 1 */
90% {
transform: translateY(calc(var(--digit-height) * -9));
} /* 显示 0 */
100% {
transform: translateY(calc(var(--digit-height) * -10));
} /* 显示重复的 9 */
}
3. 无缝循环的秘密 ✨
这是整个效果最精妙的地方!
注意数字列表的末尾有一个重复的 9。当动画运行到 100% 时,显示的是这个重复的 9。当动画循环回到 0% 时,translateY 重置为 0,但此时显示的仍然是 9(列表开头的那个)。
视觉上数字没有任何变化,实现了完美的无缝循环!
截了一个长图方便理解原理
4. 震动效果
为了让倒计时更有紧迫感,我给秒钟的个位数添加了一个微妙的震动效果——数字切换时会先向上过冲一点,然后回弹到正确位置:
@keyframes animate-10-vibrate {
/* 显示 8 */
17% {
transform: translateY(calc(var(--digit-height) * -1));
} /* 保持在 8 */
18.5% {
transform: translateY(calc(var(--digit-height) * -2.15));
} /* 过冲!超过目标位置 */
20% {
transform: translateY(calc(var(--digit-height) * -2));
} /* 回弹到 7 */
/* ... 其他数字同理 */
}
效果如下:
这个 -2.15 中的 .15 就是过冲量,可以根据需要调整震动的强度。
5. 不同位数的动画配置
倒计时器有分钟、秒钟、百分秒三个部分,每个部分的十位和个位动画周期不同:
/* 秒钟个位:0-9,每10秒循环一次 */
.time-part.seconds.ones .digit-wrapper {
animation-name: animate-10-vibrate;
animation-duration: 10s;
animation-iteration-count: 360; /* 1小时内循环360次 */
}
/* 秒钟十位:0-5,每60秒循环一次 */
.time-part.seconds.tens .digit-wrapper {
animation-name: animate-6;
animation-duration: 60s;
animation-iteration-count: 60;
}
/* 百分秒:更新更快,不需要震动效果 */
.time-part.hundredths .digit-wrapper {
animation-timing-function: cubic-bezier(1, 0, 1, 0); /* 瞬间切换 */
}
液态玻璃风格
为了让界面更加现代化,我采用了苹果最新的液态玻璃(Liquid Glass)设计风格:
.wrapper {
/* 毛玻璃效果 */
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
/* 玻璃边框和阴影 */
border-radius: 24px;
border: 1px solid rgba(255, 255, 255, 0.18);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
配合深色渐变背景和浮动的光晕效果,营造出科幻电影的氛围感。
总结
这个纯 CSS 倒计时效果的核心技巧:
- 数字堆叠 + overflow:hidden — 基础结构
- translateY 动画 — 实现数字滚动
- 重复末尾数字 — 实现无缝循环
- 过冲回弹 — 添加震动效果
- 不同周期配置 — 分钟/秒钟/百分秒协调运转
整个效果不依赖任何 JavaScript,完全由 CSS 驱动,性能优秀,兼容性好。
希望这篇文章对你有帮助,如果觉得不错欢迎点赞收藏 👍
完整代码可以在码上掘金中查看和运行