4. CSS关键帧动画(Keyframes):@keyframes规则、animation属性、多步骤动画控制
说实话,CSS动画里最让我着迷的就是关键帧动画。为什么?因为它给了你「导演」的权力——你可以精确告诉浏览器,在动画的哪个时间点,元素应该长什么样。
我记得刚入行那会儿,做一个小球弹跳效果,用transition死活调不出那种「砸到地面再弹起」的质感。后来同事甩了一句:「用@keyframes啊,中间加几个关键帧控制速度曲线。」嗯,从那以后,我就再也没用transition做过复杂动画了。
4.1 @keyframes规则:动画的剧本
@keyframes说白了就是一份「时间-状态对照表」。你定义好几个时间节点,每个节点上元素长什么样,浏览器自动帮你算中间过程。
@keyframes bounce {
0% {
transform: translateY(0);
}
50% {
transform: translateY(-100px);
}
100% {
transform: translateY(0);
}
}
这里我用了三个关键帧:起点、中间最高点、终点。你想想看,如果只用transition,你只能定义起点和终点,中间过程完全交给浏览器。但用@keyframes,你可以在50%的位置插入一个「弹起」状态,效果完全不一样。
4.2 animation属性:把剧本搬上舞台
定义好@keyframes之后,需要用animation属性把它「激活」。这个属性是个简写,背后有8个子属性,但常用的其实就那几个。
.ball {
animation: bounce 1s ease-in-out infinite;
}
这一行代码干了四件事:引用了bounce这个关键帧,时长1秒,缓动函数ease-in-out,无限循环。
我个人习惯把animation拆开来写,尤其是调试阶段——这样能快速定位问题。
.ball {
animation-name: bounce;
animation-duration: 1s;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: both;
animation-delay: 0.5s;
animation-play-state: running;
}
| 子属性 | 作用 | 常用值 |
|---|---|---|
| animation-name | 引用@keyframes名称 | bounce, fadeIn, 自定义 |
| animation-duration | 单次动画时长 | 0.3s, 1s, 2s |
| animation-timing-function | 速度曲线 | ease, linear, cubic-bezier() |
| animation-iteration-count | 播放次数 | 1, 3, infinite |
| animation-direction | 播放方向 | normal, reverse, alternate |
| animation-fill-mode | 动画前后的状态 | none, forwards, backwards, both |
| animation-delay | 延迟启动 | 0s, 0.5s, -1s(提前播放) |
| animation-play-state | 播放/暂停 | running, paused |
4.3 多步骤动画控制:不止起点和终点
真正的动画高手,不会只用0%和100%。多步骤动画才是精髓。
举个例子,做一个「呼吸灯」效果——不是简单的淡入淡出,而是模拟真实呼吸的节奏:快速吸气、缓慢呼气、停顿。
@keyframes breathe {
0% {
opacity: 0.3;
transform: scale(0.95);
}
25% {
opacity: 1;
transform: scale(1.05);
}
50% {
opacity: 0.8;
transform: scale(1.02);
}
75% {
opacity: 0.4;
transform: scale(0.97);
}
100% {
opacity: 0.3;
transform: scale(0.95);
}
}
.light {
animation: breathe 4s ease-in-out infinite;
}
你看,我用了5个关键帧。25%的位置是吸气峰值,50%开始缓慢呼气,75%接近最低点,100%回到起点。整个周期4秒,模拟一次完整的呼吸。
为什么会这样设计?因为真实世界的运动很少是线性的。你想想看,人呼吸的时候,吸气的速度比呼气快,中间还有短暂的停顿。这些细节,只有多步骤关键帧才能还原。
4.4 实战技巧:让动画更「活」
这里分享几个我常用的技巧,都是项目里摸爬滚打总结出来的。
- 错开时间:多个元素同时动画时,给每个元素不同的animation-delay,产生「波浪」效果。比如列表项依次出现,每个延迟0.1秒。
- 组合关键帧:一个元素可以同时应用多个animation,用逗号分隔。比如同时做位移和颜色变化,各自独立控制。
- 用cubic-bezier调节奏:默认的ease-in-out太「假」了。我经常用cubic-bezier(0.68, -0.55, 0.27, 1.55)做出弹性效果,或者cubic-bezier(0.25, 0.46, 0.45, 0.94)做出自然衰减。
- 暂停与恢复:用animation-play-state: paused配合:hover,让用户悬停时动画暂停,移开继续。这个交互细节很加分。
.card {
animation: float 3s ease-in-out infinite;
}
.card:hover {
animation-play-state: paused;
}
嗯,这里要注意一点:animation-play-state只控制当前动画的暂停和继续,不会重置动画。如果你想让动画重置,需要用JavaScript移除并重新添加类名。
最后说一句:CSS关键帧动画虽然强大,但也有边界。如果你需要做复杂的物理模拟、时间线编排、或者交互驱动的动画,还是得上JavaScript。但日常的UI动效——按钮反馈、加载提示、页面过渡——用@keyframes完全够用,而且性能更好。
下一章我会讲CSS过渡动画(Transition),到时候咱们再聊聊「什么时候用Transition,什么时候用Keyframes」这个经典问题。