1. 动画基础与CSS过渡:理解CSS transition属性
大家好,我是你们的前端实战课讲师。今天咱们来聊聊动画的起点——CSS过渡。
说实话,我刚入行那会儿,觉得动画就是jQuery的fadeIn、fadeOut。后来才发现,CSS原生的transition才是真正的宝藏。它轻量、高效,而且浏览器支持得特别好。
1.1 什么是CSS过渡?
CSS过渡,说白了就是让属性变化的过程变得平滑。你想想看,如果没有过渡,一个元素从红色变成蓝色,那是瞬间切换的,生硬得很。加了过渡,它就会慢慢渐变过去,视觉上舒服多了。
它的核心机制很简单:当某个CSS属性值发生变化时,浏览器按照你设定的时间和曲线,自动补全中间帧。这就是所谓的「补间动画」。
核心概念:过渡 = 起始状态 + 结束状态 + 变化过程
1.2 transition属性详解
transition是一个复合属性,包含四个子属性。我习惯把它们拆开写,这样可读性更好。
| 子属性 | 作用 | 示例值 |
|---|---|---|
| transition-property | 指定哪些属性参与过渡 | opacity, transform, all |
| transition-duration | 过渡持续时长 | 0.3s, 500ms |
| transition-timing-function | 速度曲线 | ease, linear, cubic-bezier() |
| transition-delay | 延迟触发时间 | 0s, 0.1s |
/* 完整写法 */
.element {
transition-property: opacity, transform;
transition-duration: 0.3s, 0.5s;
transition-timing-function: ease, cubic-bezier(0.25, 0.1, 0.25, 1);
transition-delay: 0s, 0.1s;
}
/* 简写形式(我推荐这种) */
.element {
transition: opacity 0.3s ease, transform 0.5s cubic-bezier(0.25, 0.1, 0.25, 1) 0.1s;
}
我的小技巧:别用all。虽然transition: all 0.3s写起来方便,但性能上会有损耗。指定具体属性,浏览器能更好地优化渲染路径。
1.3 触发条件:什么时候过渡会生效?
过渡不会自己跑起来。它需要「触发器」。常见的触发方式有这几种:
- 伪类触发::hover, :focus, :active, :checked
- JavaScript动态修改:通过JS改变元素的style或class
- 媒体查询变化:窗口尺寸改变导致样式重新计算
- 元素状态变化:比如新增/移除DOM节点
/* 最常见的hover触发 */
.button {
background-color: #4A90D9;
transform: scale(1);
transition: background-color 0.3s, transform 0.2s;
}
.button:hover {
background-color: #357ABD;
transform: scale(1.05);
}
/* JS触发示例 */
const box = document.querySelector('.box');
box.addEventListener('click', () => {
box.classList.toggle('expanded');
});
嗯,这里要注意一点:不是所有属性都能过渡。比如display就不行。为什么呢?因为display是离散的,没有中间值。你不能说「半显示半隐藏」。遇到这种情况,我一般用opacity + visibility组合拳。
我曾经踩过的坑:用display: none做隐藏动画,结果过渡完全没效果。后来改成opacity: 0 + visibility: hidden,配合transition,完美解决。
1.4 性能优化:别让动画卡成PPT
做动画最怕什么?卡顿。用户可不管你是用了什么高大上的框架,卡就是卡。
为什么会卡?说白了,就是浏览器忙不过来。每次属性变化,浏览器都要重新计算布局、绘制、合成。这个过程叫「渲染流水线」。
我给大家一个黄金法则:尽量只过渡 transform 和 opacity。为什么?
- transform:只触发合成层,不触发布局和绘制
- opacity:同样只触发合成层
- width/height/left/top:触发布局 → 绘制 → 合成,性能开销大
/* ❌ 不推荐:触发布局 */
.bad {
transition: left 0.3s;
left: 0;
}
.bad.active {
left: 100px;
}
/* ✅ 推荐:只触发合成 */
.good {
transition: transform 0.3s;
transform: translateX(0);
}
.good.active {
transform: translateX(100px);
}
我的经验:能用transform解决的位移,绝不用left/top。我在一个电商项目中,把所有的hover动画从修改width改为scale,帧率直接从30fps飙到60fps。
1.5 速度曲线:让动画更有「质感」
transition-timing-function 决定了动画的「节奏」。默认的ease是慢-快-慢,适合大多数场景。但有时候,你需要更精细的控制。
| 值 | 特点 | 适用场景 |
|---|---|---|
| ease | 慢-快-慢 | 通用,按钮悬停 |
| linear | 匀速 | 进度条、旋转加载 |
| ease-in | 慢-快 | 元素进入画面 |
| ease-out | 快-慢 | 元素离开画面 |
| ease-in-out | 慢-快-慢(更明显) | 卡片翻转 |
| cubic-bezier() | 自定义曲线 | 需要独特节奏时 |
/* 自定义贝塞尔曲线 */
.custom {
transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55);
/* 这个曲线有「回弹」效果,看起来更生动 */
}
我个人特别喜欢用cubic-bezier做「弹性」效果。你想想看,一个按钮按下去,稍微弹一下再复位,是不是比直来直去更有手感?
1.6 实战:一个完整的过渡示例
来,咱们写一个实际可用的卡片组件。它包含了hover放大、阴影变化、内容渐入效果。
<div class="card">
<div class="card-image"></div>
<div class="card-content">
<h3>卡片标题</h3>
<p>这是一段描述文字</p>
</div>
</div>
.card {
width: 300px;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
}
.card-image {
height: 200px;
background: linear-gradient(135deg, #667eea, #764ba2);
transition: transform 0.4s ease;
}
.card:hover .card-image {
transform: scale(1.05);
}
.card-content {
padding: 20px;
opacity: 0.9;
transition: opacity 0.3s ease 0.1s;
/* 0.1s的延迟,让内容在卡片抬起后再变化 */
}
.card:hover .card-content {
opacity: 1;
}
关键点总结:
- 卡片整体使用translateY模拟抬起效果
- 图片使用scale实现微缩放
- 内容使用opacity渐入,并加了小延迟
- 所有动画都只涉及transform和opacity,性能优秀
1.7 避坑指南
最后,分享几个我实际工作中遇到的坑:
- 过渡不生效?检查一下初始值是否显式声明了。比如opacity默认是1,如果你没写,hover时从1变到0.5,过渡可能不触发。
- 动画结束时突然跳一下?可能是transition-delay设置不当,或者属性值在动画过程中被其他样式覆盖了。
- 移动端卡顿?加上will-change: transform提示浏览器提前优化。但别滥用,每个元素都加反而会消耗内存。
/* 移动端优化 */
.smooth-mobile {
will-change: transform;
transition: transform 0.3s ease;
}
好了,这一章的内容就到这里。CSS过渡虽然基础,但它是所有复杂动画的基石。你把它吃透了,后面学keyframes动画、JS动画都会轻松很多。
下一章咱们聊聊CSS动画的进阶——@keyframes关键帧动画。到时候我会分享一个我做过的loading动画案例,挺有意思的。