3、CSS动画入门:transition过渡、animation动画、关键帧@keyframes、动画性能分析
好,咱们今天聊聊CSS动画。说实话,CSS动画是我个人觉得性价比最高的动效实现方式——你不需要引入任何第三方库,浏览器原生支持,写起来也简单。但很多人用着用着就踩坑了,尤其是性能问题。
我刚开始做大屏项目时,就吃过这个亏。一个简单的旋转动画,CPU直接飙到80%,老板站我身后看了三秒,说了句「这页面怎么跟PPT似的卡」。嗯,从那以后,我再也不敢小看CSS动画的性能分析了。
3.1 transition过渡:最简单的动效入口
transition 说白了就是「状态变化时的平滑过渡」。你想想看,一个按钮从红色变成蓝色,如果没有过渡,它「啪」一下就变了,很生硬。加上过渡,它会在你指定的时间内渐变过去。
基本语法长这样:
/* 简写形式 */
transition: property duration timing-function delay;
/* 实际例子 */
.box {
width: 100px;
height: 100px;
background: #409EFF;
transition: all 0.3s ease;
}
.box:hover {
width: 150px;
background: #E6A23C;
}
这里有几个点要注意:
- property:指定哪些属性要过渡。用
all省事,但性能差一些。我建议只写你需要的属性。 - duration:持续时间。大屏动效一般 0.3s~0.6s 比较舒服,太快了看不清,太慢了显得拖沓。
- timing-function:缓动函数。默认
ease,但cubic-bezier()自定义曲线才是大屏动效的灵魂。
cubic-bezier(0.25, 0.1, 0.25, 1),这个曲线开头快、结尾稳,视觉上很「高级」。你可以试试看。
3.2 animation动画与@keyframes关键帧
transition 只能处理「开始→结束」两个状态。但如果你想要更复杂的动画——比如先放大、再旋转、最后缩小——那就得上 animation + @keyframes 了。
我举个例子,一个呼吸灯效果,大屏上经常用来表示「数据在更新」:
@keyframes breathe {
0% {
opacity: 0.4;
transform: scale(0.95);
}
50% {
opacity: 1;
transform: scale(1.05);
}
100% {
opacity: 0.4;
transform: scale(0.95);
}
}
.dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: #67C23A;
animation: breathe 2s ease-in-out infinite;
}
这里的关键帧 @keyframes 定义了动画的「剧本」,而 animation 属性负责「播放」。常用的 animation 子属性有:
| 属性 | 说明 | 我的建议值 |
|---|---|---|
| animation-name | 关键帧名称 | 见名知意,比如 fadeIn |
| animation-duration | 单次播放时长 | 大屏推荐 1s~3s |
| animation-timing-function | 缓动函数 | 尽量用 ease-in-out |
| animation-iteration-count | 播放次数 | infinite 或具体数字 |
| animation-direction | 播放方向 | alternate 来回播放很常用 |
| animation-fill-mode | 结束状态保持 | forwards 保留最后一帧 |
animation: myAnim 1s infinite 时,如果页面切到后台再切回来,有些浏览器会「跳帧」——动画直接跳到中间状态。解决方案是加上 animation-play-state: paused 配合 visibilitychange 事件手动控制。嗯,这个坑我记了好几年。
3.3 动画性能分析:为什么你的动画会卡?
好,这是今天的重点。很多同学问我:「为什么同样的动画,别人的页面跑60帧,我的就掉到20帧?」
原因其实很简单——你触发了浏览器的重排(reflow)。
浏览器渲染一个页面,大致分三步:
- Layout(布局):计算元素的位置和大小
- Paint(绘制):把元素画出来
- Composite(合成):把图层拼在一起
如果你用 left、top、width、height 这些属性做动画,每次变化都会触发 Layout + Paint + Composite,三件套全上,性能自然差。
那用什么属性做动画性能最好?答案是:transform 和 opacity。
这两个属性只触发 Composite,不触发 Layout 和 Paint。浏览器会把它们交给 GPU 处理,帧率稳稳的 60fps。
/* ❌ 性能差 */
.box {
left: 0;
transition: left 0.3s;
}
.box.active {
left: 100px;
}
/* ✅ 性能好 */
.box {
transform: translateX(0);
transition: transform 0.3s;
}
.box.active {
transform: translateX(100px);
}
我做过一个测试:同样一个 200 个元素的列表动画,用 left 做,帧率只有 18fps;换成 transform,直接飙到 58fps。差距就是这么明显。
3.4 实战:大屏数据卡片入场动画
咱们结合前面讲的知识,写一个实际的大屏场景——数据卡片依次入场。
@keyframes cardEnter {
from {
opacity: 0;
transform: translateY(30px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.card {
opacity: 0; /* 初始隐藏 */
animation: cardEnter 0.6s cubic-bezier(0.25, 0.1, 0.25, 1) forwards;
}
/* 依次延迟入场 */
.card:nth-child(1) { animation-delay: 0s; }
.card:nth-child(2) { animation-delay: 0.15s; }
.card:nth-child(3) { animation-delay: 0.3s; }
.card:nth-child(4) { animation-delay: 0.45s; }
这里用了 forwards 保持结束状态,配合 animation-delay 实现「波浪式入场」。视觉上很有节奏感,而且性能很好——因为只用了 opacity 和 transform。
- 能用
transform就别用left/top - 能用
opacity就别用display/visibility - 动画元素尽量单独一层(加
will-change: transform) - 大屏上避免同时触发太多动画,控制在 10~15 个以内
最后说一句:CSS动画虽然简单,但想做好、做流畅,还是得理解浏览器渲染原理。你想想看,如果连 transform 和 left 的性能差异都不清楚,那优化起来就只能靠「试」了——这可不是工程师该有的做法。
下一章咱们聊 JavaScript 动画,到时候会结合 requestAnimationFrame 做更精细的控制。嗯,那才是真正考验功底的地方。