2、CSS关键帧动画:深入理解@keyframes规则,掌握animation系列属性,制作循环与非线性动画。
好,咱们接着聊CSS动画。上一章我们把transition聊透了,它适合做「从A到B」的简单过渡。但如果你想让一个元素做一套完整的动作——比如先向右、再向下、再旋转、再弹回来——那transition就有点力不从心了。
这时候,就该@keyframes登场了。
2.1 @keyframes 规则:动画的剧本
@keyframes 说白了就是给动画写剧本。你告诉浏览器:「在0%的时候元素在哪儿,在50%的时候长什么样,在100%的时候变成什么。」浏览器就会自动帮你算出中间的所有过程。
基本语法长这样:
@keyframes 动画名称 {
0% {
/* 起始状态 */
transform: translateX(0);
opacity: 1;
}
50% {
/* 中间状态 */
transform: translateX(100px);
opacity: 0.5;
}
100% {
/* 结束状态 */
transform: translateX(0);
opacity: 1;
}
}
我个人习惯用百分比来定义关键帧,因为这样最灵活。不过你也可以用 from 和 to 关键字,它们分别等价于 0% 和 100%。
2.2 animation 系列属性:让动画跑起来
光有剧本不行,你还得告诉浏览器怎么演。这就是 animation 系列属性的作用。
我整理了一张表,把常用的 animation 属性列出来:
| 属性 | 作用 | 默认值 |
|---|---|---|
| animation-name | 指定要使用的 @keyframes 名称 | none |
| animation-duration | 动画完成一个周期的时间 | 0s |
| animation-timing-function | 动画的速度曲线 | ease |
| animation-delay | 动画开始前的延迟时间 | 0s |
| animation-iteration-count | 动画播放次数 | 1 |
| animation-direction | 动画播放方向 | normal |
| animation-fill-mode | 动画结束后的状态 | none |
| animation-play-state | 控制动画播放或暂停 | running |
写完整写法是这样的:
.box {
animation-name: bounce;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-delay: 0.5s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: both;
}
当然,你也可以用简写:
.box {
animation: bounce 2s ease-in-out 0.5s infinite alternate both;
}
2.3 循环动画:让动画永不停歇
做循环动画很简单,把 animation-iteration-count 设为 infinite 就行。
但这里有个坑——循环动画的衔接。如果你想让动画看起来是「无缝循环」的,那起始状态和结束状态必须一致。否则每次循环结束都会「跳」一下。
举个例子:
@keyframes loop-bad {
0% { transform: translateX(0); }
100% { transform: translateX(100px); }
}
@keyframes loop-good {
0% { transform: translateX(0); }
50% { transform: translateX(100px); }
100% { transform: translateX(0); }
}
第一个动画每次循环结束都会从 100px 跳回 0px,看起来很突兀。第二个动画在 100% 时回到了 0px,所以循环起来是平滑的。
2.4 非线性动画:让动作更自然
现实世界中的运动很少是匀速的。你扔一个球,它先加速再减速。你推一个抽屉,它先快后慢。这就是非线性动画的魅力。
在 CSS 里,我们用 animation-timing-function 来控制速度曲线。除了内置的 ease、linear、ease-in、ease-out、ease-in-out,你还可以用 cubic-bezier() 自定义曲线。
我常用的几个贝塞尔曲线:
/* 弹跳效果 */
cubic-bezier(0.68, -0.55, 0.27, 1.55)
/* 先慢后快再慢 */
cubic-bezier(0.42, 0, 0.58, 1)
/* 快速进入,缓慢结束 */
cubic-bezier(0.25, 0.1, 0.25, 1)
你想想看,为什么有些动画看起来「很舒服」,有些却「很生硬」?区别就在这。生硬的动画往往是匀速的,而舒服的动画一定是有节奏变化的。
2.5 实战:制作一个呼吸灯效果
光说不练假把式。咱们结合上面讲的知识,做一个呼吸灯效果。
@keyframes breathe {
0% {
transform: scale(1);
opacity: 0.6;
box-shadow: 0 0 10px rgba(0, 150, 255, 0.3);
}
50% {
transform: scale(1.2);
opacity: 1;
box-shadow: 0 0 30px rgba(0, 150, 255, 0.8);
}
100% {
transform: scale(1);
opacity: 0.6;
box-shadow: 0 0 10px rgba(0, 150, 255, 0.3);
}
}
.light {
width: 100px;
height: 100px;
border-radius: 50%;
background: #0096ff;
animation: breathe 3s ease-in-out infinite;
}
这个效果看起来就像灯在「呼吸」——慢慢变亮变大,再慢慢变暗变小。注意我用了 ease-in-out,这样变化过程是平滑的。如果用 linear,就会显得很机械。
2.6 小结
嗯,这一章的内容就到这。咱们回顾一下:
- @keyframes 是动画的剧本,用百分比定义关键帧
- animation 系列属性 控制动画的播放方式
- 循环动画 要注意起始和结束状态的一致性
- 非线性动画 用 timing-function 和 cubic-bezier 实现自然运动
下一章咱们聊聊 CSS 过渡与动画的性能优化。我会分享一些我在实际项目中踩过的坑,以及怎么让动画跑得更流畅。到时候见。