关键帧动画与性能调优
CSS关键帧动画(@keyframes)的性能取决于属性选择和关键帧设计,合理优化可实现流畅60fps。
关键帧动画基础
基本语法
CSS
@keyframes animation-name {
from {
/* 起始状态 */
}
to {
/* 结束状态 */
}
}
/* 或多关键帧 */
@keyframes animation-name {
0% { /* 状态1 */ }
25% { /* 状态2 */ }
50% { /* 状态3 */ }
75% { /* 状态4 */ }
100% { /* 状态5 */ }
}
应用动画
CSS
.element {
animation: animation-name 1s ease-in-out;
animation: animation-name 1s ease-in-out infinite;
animation: animation-name 1s ease-in-out 0.5s 3 forwards;
}
/* 分解属性 */
.element {
animation-name: slide;
animation-duration: 1s;
animation-timing-function: ease-in-out;
animation-delay: 0.5s;
animation-iteration-count: 3;
animation-direction: normal;
animation-fill-mode: forwards;
animation-play-state: running;
}
性能属性选择
高性能属性
CSS
@keyframes performant {
from {
transform: translateX(0) scale(1);
opacity: 1;
}
to {
transform: translateX(100px) scale(1.2);
opacity: 0.5;
}
}
| 属性 | 渲染路径 | 性能等级 |
|---|---|---|
| transform | Composite | 高 |
| opacity | Composite | 高 |
| filter | Paint + Composite | 中 |
避免低性能属性
CSS
/* 错误:触发回流 */
@keyframes bad {
from {
width: 100px;
height: 100px;
left: 0;
margin: 0;
}
to {
width: 200px;
height: 200px;
left: 100px;
margin: 50px;
}
}
/* 正确:使用transform替代 */
@keyframes good {
from {
transform: translateX(0) scale(1);
}
to {
transform: translateX(100px) scale(2);
}
}
关键帧设计优化
减少关键帧数量
CSS
/* 过多关键帧:计算开销大 */
@keyframes complex {
0% { transform: translateX(0); }
10% { transform: translateX(10px); }
20% { transform: translateX(20px); }
30% { transform: translateX(30px); }
/* ... */
100% { transform: translateX(100px); }
}
/* 简化关键帧:让timing-function处理中间 */
@keyframes simple {
from { transform: translateX(0); }
to { transform: translateX(100px); }
}
.element {
animation: simple 1s ease-out; /* ease-out自然过渡 */
}
避免同时动画多属性
CSS
/* 复杂:多属性同时动画 */
@keyframes multi-property {
from {
transform: translateX(0) scale(1) rotate(0);
opacity: 1;
filter: blur(0);
}
to {
transform: translateX(100px) scale(1.5) rotate(45deg);
opacity: 0.5;
filter: blur(5px);
}
}
/* 简化:分离动画 */
@keyframes slide {
from { transform: translateX(0); }
to { transform: translateX(100px); }
}
@keyframes fade {
from { opacity: 1; }
to { opacity: 0.5; }
}
.element {
animation: slide 1s ease, fade 1s ease;
}
使用steps()精确控制
CSS
/* 帧动画:精确步进 */
@keyframes sprite {
from { background-position: 0 0; }
to { background-position: -400px 0; }
}
.sprite-animation {
animation: sprite 1s steps(8) infinite;
/* 8帧,每帧停留固定时间 */
}
合成层管理
触发硬件加速
CSS
.element {
animation: slide 1s;
/* animation中的transform自动触发硬件加速 */
}
/* 或主动声明 */
.element {
will-change: transform;
animation: slide 1s;
}
动画结束后清理
CSS
.element {
will-change: transform;
animation: slide 1s forwards;
}
/* JS监听结束 */
element.addEventListener('animationend', () => {
element.style.willChange = 'auto';
});
避免层叠问题
CSS
/* 遆免隐式合成 */
.animated-container {
animation: pulse 1s infinite;
}
.animated-container .child {
/* 不设置z-index,避免隐式提升 */
}
缓动函数优化
预设缓动函数
CSS
.element {
animation-timing-function: linear; /* 线性 */
animation-timing-function: ease; /* 默认缓动 */
animation-timing-function: ease-in; /* 慢开始 */
animation-timing-function: ease-out; /* 慢结束 */
animation-timing-function: ease-in-out; /* 慢开始慢结束 */
}
自定义贝塞尔曲线
CSS
.element {
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
/* Material Design标准缓动 */
}
/* 常用缓动值 */
.snappy {
animation-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1);
}
.bounce {
animation-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
每关键帧设置不同缓动
CSS
@keyframes complex-ease {
0% {
transform: translateX(0);
animation-timing-function: ease-out;
}
50% {
transform: translateX(50px);
animation-timing-function: ease-in;
}
100% {
transform: translateX(100px);
}
}
动画控制技巧
暂停和恢复
CSS
.paused {
animation-play-state: paused;
}
.running {
animation-play-state: running;
}
JavaScript
// JS控制
element.style.animationPlayState = 'paused';
element.style.animationPlayState = 'running';
反向播放
CSS
.element {
animation: slide 1s forwards;
}
.element.reverse {
animation-direction: reverse;
animation-fill-mode: backwards;
}
循环模式
CSS
.normal {
animation-direction: normal; /* 正向 */
}
.reverse {
animation-direction: reverse; /* 反向 */
}
.alternate {
animation-direction: alternate; /* 正向→反向交替 */
}
.alternate-reverse {
animation-direction: alternate-reverse; /* 反向→正向交替 */
}
性能调优实战
弹窗动画优化
CSS
/* 低性能:直接动画所有属性 */
.modal {
animation: modal-in 0.3s ease-out forwards;
}
@keyframes modal-in {
from {
transform: scale(0.8);
opacity: 0;
width: 80%;
height: 60%;
}
to {
transform: scale(1);
opacity: 1;
width: 80%;
height: 60%;
}
}
/* 高性能:只用transform和opacity */
.modal {
animation: modal-optimize 0.3s ease-out forwards;
width: 80%;
height: 60%; /* 静态尺寸 */
}
@keyframes modal-optimize {
from {
transform: scale(0.8);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
滑动列表优化
CSS
/* 避免动画每个列表项 */
.list-item {
opacity: 0;
animation: fade-in 0.3s forwards;
}
/* 优化:限制动画数量 */
.list-item:nth-child(-n+10) {
animation: fade-in 0.3s forwards;
}
.list-item:nth-child(n+11) {
opacity: 1; /* 超出范围直接显示 */
}
无限动画优化
CSS
/* 无限旋转 */
.spinner {
animation: spin 1s linear infinite;
will-change: transform;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* 视口外暂停 */
.spinner:not(.in-view) {
animation-play-state: paused;
}
JavaScript
// Intersection Observer控制
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
entry.target.style.animationPlayState =
entry.isIntersecting ? 'running' : 'paused';
});
});
observer.observe(document.querySelector('.spinner'));
要点总结
- 只动画transform和opacity,避免几何属性
- 减少关键帧数量,让缓动函数处理过渡
- 使用will-change提前触发硬件加速
- 动画结束后移除will-change释放资源
- 视口外无限动画应暂停节省资源
- 自定义cubic-bezier实现自然缓动效果
📝 发现内容有误?点击此处直接编辑