
7.1 CSS 动效基础
CSS 动效是前端交互反馈与视觉体验的核心实现方式,基于浏览器合成线程原生渲染,具备 GPU 加速能力,性能远优于 JavaScript 动画。CSS 动效体系分为过渡动画与关键帧动画两大核心类型,配合原生动画事件实现动画生命周期的回调控制,能够覆盖状态反馈、入场出场、循环装饰等绝大多数动效场景,是构建流畅交互体验的基础。
7.1.1 transition 过渡动画
一、核心原理
过渡动画是状态驱动的补间动画,针对元素的两个状态(默认态与 hover 态、类名切换态等),在状态变化时自动计算中间帧,实现属性的平滑过渡。
它是最简单的 CSS 动效形式,适合交互触发的状态变化反馈。
二、核心属性
transition 是简写属性,包含四个子属性,按顺序为:属性名、时长、缓动函数、延迟时间。
|--------------------------------|----------------------------------|-------------------------------------------------------------------------|
| 属性 | 说明 | 取值示例 |
| transition-property | 指定应用过渡的 CSS 属性,all 表示所有可过渡属性 | all 、opacity 、transform |
| transition-duration | 过渡持续时间,单位秒(s)或毫秒(ms) | 0.3s 、300ms |
| transition-timing-function | 过渡缓动曲线,控制动画的速度变化 | ease 、linear 、ease-in-out 、cubic-bezier(0.4, 0, 0.2, 1) |
| transition-delay | 过渡延迟触发时间,单位秒或毫秒 | 0s 、100ms |
三、缓动函数详解
缓动函数决定动画的运动感,是动效自然度的核心:
- linear:匀速线性运动,适合匀速旋转、位移动画
- ease:默认值,慢-快-慢,自然的运动感,通用场景
- ease-in:慢入快出,加速运动,适合元素出场、下落动画
- ease-out:快入慢出,减速运动,适合元素入场、停止动画
- ease-in-out:慢入慢出,两端平缓,适合悬停切换、来回过渡
- cubic-bezier(x1,y1,x2,y2):自定义贝塞尔曲线,精准控制运动曲线
四、基础示例
css
/* 按钮悬停过渡:所有属性 0.3 秒缓动过渡 */
.btn {
background: #1677ff;
transform: scale(1);
transition: all 0.3s ease;
}
.btn:hover {
background: #4096ff;
transform: scale(1.05);
}
五、特点与边界
- 优势:实现简单,声明式配置,性能优异,状态切换时自动反向过渡
- 局限:仅支持两个状态之间的过渡,无法控制中间帧;无法主动播放,必须依赖状态触发;部分属性不可过渡(如 display 、float)
- 最佳实践:优先使用 transform 和 opacity 实现动效,触发 GPU 合成,避免重排重绘,性能最优;避免使用 all,指定具体属性减少性能开销
7.1.2 animation 关键帧动画
一、核心原理
关键帧动画通过 @keyframes 规则定义动画过程中的多个关键帧状态,浏览器自动补全中间帧,支持多阶段、循环、反向等复杂动画效果,无需状态触发即可独立播放,是复杂 CSS 动效的标准实现方式。
二、核心属性
animation 是简写属性,包含 8 个子属性,完整控制动画的全生命周期。
|-------------------------------|----------------------|-----------------------------------------------------------------|
| 属性 | 说明 | 取值示例 |
| animation-name | 绑定的 @keyframes 关键帧名称 | fadeIn |
| animation-duration | 单次动画持续时间 | 1s |
| animation-timing-function | 单帧间的缓动函数 | ease-out |
| animation-delay | 动画延迟播放时间 | 0.2s |
| animation-iteration-count | 动画播放次数 | 1 、infinite(无限循环) |
| animation-direction | 动画播放方向 | normal (正向)、reverse (反向)、alternate(交替往返) |
| animation-fill-mode | 动画填充模式,控制动画前后的状态 | none 、forwards (停在结束帧)、backwards (提前应用起始帧)、both |
| animation-play-state | 动画播放状态 | running (运行)、paused(暂停) |
三、@keyframes 定义方式
支持两种关键帧定义方式:
- from / to:两帧动画,等价于 0% 和 100%
- 百分比:多帧动画,定义任意百分比位置的状态
css
/* 淡入上移动画 */
@keyframes fadeInUp {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
/* 呼吸动画:无限循环交替 */
@keyframes breathe {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
/* 应用动画 */
.fade-in {
animation: fadeInUp 0.5s ease-out forwards;
}
.breathe {
animation: breathe 2s ease-in-out infinite alternate;
}
四、与 transition 的核心对比
|----------|-----------------|---------------------|
| 对比维度 | transition 过渡 | animation 关键帧 |
| 状态数量 | 仅两个状态(起始-结束) | 支持任意多个关键帧状态 |
| 触发方式 | 依赖状态变化触发,无法主动播放 | 可自动播放,也可通过类名控制 |
| 循环能力 | 不支持循环,仅往返过渡 | 支持任意次数循环、无限循环 |
| 中间控制 | 无法控制中间过程 | 可精准控制每一帧的状态 |
| 事件回调 | 单次过渡对应一次事件 | 支持帧迭代、结束等多种事件 |
| 适用场景 | 交互状态反馈、悬停切换 | 复杂动画、入场出场、循环装饰、加载动效 |
五、fill-mode 重点说明
animation-fill-mode 是最容易被忽略的核心属性,决定动画播放前后元素的状态:
- none:默认值,动画开始前和结束后都保持元素原有样式
- forwards:动画结束后,保持最后一帧的状态,是入场动画最常用的配置
- backwards:动画延迟期间,提前应用第一帧的状态,避免动画开始前的跳变
- both:同时应用 forwards 和 backwards 的效果
7.1.3 动画事件:transitionend、animationend
CSS 动画原生提供生命周期事件,用于监听动画的开始、结束、迭代等节点,实现动画与业务逻辑的联动,是链式动画、状态切换、入场出场流程控制的核心。
一、transitionend 事件
触发时机
过渡动画完成时触发,每个过渡属性会独立触发一次。如果同时过渡多个属性,会触发多次事件。
核心属性
- event.propertyName:完成过渡的属性名,用于区分不同属性的过渡
- event.elapsedTime:过渡持续的时间,单位秒
基础示例
css
const box = document.querySelector('.box');
box.addEventListener('transitionend', (e) => {
// 只处理 opacity 过渡完成的逻辑
if (e.propertyName === 'opacity') {
console.log('透明度过渡完成');
// 过渡结束后移除元素
if (box.style.opacity === '0') {
box.remove();
}
}
});
注意事项
- 过渡中途被中断(如状态再次变化、元素隐藏),不会触发 transitionend 事件。
- 过渡时长为 0 时不会触发事件。
- 多个属性过渡会触发多次,需通过 propertyName 过滤目标事件。
二、animation 相关事件
关键帧动画提供三个生命周期事件,覆盖完整动画流程:
|------------------------|--------------|-----------------------------------------|
| 事件名 | 触发时机 | 核心说明 |
| animationstart | 动画开始播放时触发 | 延迟时间结束、动画正式启动时触发 |
| animationiteration | 动画每完成一次迭代时触发 | 循环动画每次循环结束触发,仅 iteration-count > 1 时有效 |
| animationend | 动画全部播放完成时触发 | 所有迭代完成后触发,无限循环不会触发 |
基础示例:入场动画结束后执行业务逻辑
css
const modal = document.querySelector('.modal');
modal.addEventListener('animationend', () => {
console.log('弹窗入场动画完成');
// 动画结束后聚焦输入框
modal.querySelector('input').focus();
});
三、典型应用场景
- 离场动画结束后移除 DOM 元素,避免元素残留
- 链式动画:前一个动画结束后触发下一个动画,实现序列动效
- 动画完成回调:入场动画结束后初始化业务逻辑、聚焦交互
- 状态同步:动画结束后更新业务状态,避免动画未完成就执行操作