3. CSS过渡(Transition)实战:transition-property、duration、timing-function、delay的完整用法
好,咱们今天来聊聊CSS过渡。说实话,过渡是前端动画里最基础、也最容易被忽视的一块。很多人觉得不就是个transition嘛,写个all 0.3s就完事了。嗯,我以前也这么干过,直到有一次在移动端项目里被性能问题狠狠教育了一顿。
过渡的核心,说白了就是让属性值的变化「不那么生硬」。你想想看,一个按钮hover时突然变红,跟它慢慢变红,用户的感受是完全不同的。今天我就把transition的四个核心属性掰开揉碎了讲给你听。
3.1 transition-property:别偷懒用all
这个属性指定哪些CSS属性要参与过渡。很多人习惯写transition: all 0.3s,我个人建议——别这么干。
正确的做法是:只列出你需要过渡的属性。
/* 不推荐 */
.element {
transition: all 0.3s ease;
}
/* 推荐 */
.element {
transition: opacity 0.3s ease, transform 0.3s ease;
}
哪些属性可以过渡?我整理了一张表:
| 属性类别 | 示例 | 性能表现 |
|---|---|---|
| 变换类 | transform | GPU加速,流畅 |
| 透明度 | opacity | GPU加速,流畅 |
| 颜色类 | color, background-color | CPU计算,一般 |
| 尺寸类 | width, height | 触发重排,较慢 |
| 定位类 | top, left | 触发重排,较慢 |
3.2 transition-duration:时间不是越长越好
这个控制动画时长。单位是秒(s)或毫秒(ms)。
.box {
transition-property: opacity;
transition-duration: 0.3s; /* 300ms */
}
时长怎么选?我有个经验法则:
- 100-200ms:微交互,比如按钮点击反馈、开关切换
- 300-400ms:常规过渡,比如面板展开、元素出现
- 500-800ms:页面转场、大范围动画
- 超过1s:慎用,除非是强调性的加载动画
为什么会这样?因为人眼的视觉暂留和注意力集中时间就那么多。太短了看不清,太长了用户会烦躁。我记得有次做登录页的loading动画,设了1.5s,测试的时候自己都觉得煎熬。
3.3 transition-timing-function:动画的灵魂
这个属性决定了动画的「节奏感」。说白了就是速度怎么变化。
常用的几个值:
.box {
/* 匀速 - 最生硬,不推荐 */
transition-timing-function: linear;
/* 慢快慢 - 最自然,推荐 */
transition-timing-function: ease;
/* 慢入 - 适合元素出现 */
transition-timing-function: ease-in;
/* 慢出 - 适合元素消失 */
transition-timing-function: ease-out;
/* 慢入慢出 - 适合强调动画 */
transition-timing-function: ease-in-out;
/* 自定义贝塞尔曲线 */
transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1);
}
重点来了:cubic-bezier才是真正体现功力的地方。我常用的几个曲线:
cubic-bezier(0.68, -0.55, 0.27, 1.55)— 带弹性效果cubic-bezier(0.4, 0, 0.2, 1)— Material Design标准曲线cubic-bezier(0.22, 1, 0.36, 1)— 快速弹出效果
你想想看,一个卡片从底部滑入,如果用linear,就像机器人推出来一样僵硬。但用ease-out,就像被弹簧弹出来,视觉上舒服多了。
3.4 transition-delay:让动画有层次
这个属性设置动画开始前的等待时间。很多人忽略它,但用好delay能让动画有「编排感」。
.item {
transition: opacity 0.3s ease;
}
/* 列表项依次出现 */
.item:nth-child(1) { transition-delay: 0s; }
.item:nth-child(2) { transition-delay: 0.1s; }
.item:nth-child(3) { transition-delay: 0.2s; }
.item:nth-child(4) { transition-delay: 0.3s; }
我在项目中遇到过这样一个场景:一个弹窗里有三个按钮,如果同时出现,用户会不知道该先看哪个。后来我给每个按钮加了0.05s的延迟差,视觉上就有了「主次感」。
3.5 完整实战:一个卡片列表的入场动画
来,咱们写一个完整的例子。假设有一个卡片列表,需要依次淡入并上移:
<div class="card-list">
<div class="card">卡片1</div>
<div class="card">卡片2</div>
<div class="card">卡片3</div>
<div class="card">卡片4</div>
</div>
.card {
opacity: 0;
transform: translateY(20px);
transition-property: opacity, transform;
transition-duration: 0.4s;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
/* 页面加载后添加这个类 */
.card-list.loaded .card {
opacity: 1;
transform: translateY(0);
}
/* 依次延迟 */
.card-list.loaded .card:nth-child(1) { transition-delay: 0s; }
.card-list.loaded .card:nth-child(2) { transition-delay: 0.1s; }
.card-list.loaded .card:nth-child(3) { transition-delay: 0.2s; }
.card-list.loaded .card:nth-child(4) { transition-delay: 0.3s; }
这个效果我用了不下几十次。卡片依次出现,每个间隔0.1s,视觉上就像多米诺骨牌一样流畅。而且只用了transform和opacity,性能完全没问题。
3.6 避坑指南
最后,分享几个我踩过的坑:
- display不能用过渡:display: none到block是瞬间切换的,过渡无效。要用opacity + visibility组合。
- auto值不能过渡:比如width: auto到width: 100px,浏览器不知道中间值是什么。得用具体数值。
- height: auto的折叠动画:这个比较麻烦,我一般用max-height来模拟,或者直接用transform: scaleY。
- 过渡和动画冲突:如果同时用了transition和animation,后者会覆盖前者。注意优先级。
我的习惯:写过渡前先问自己三个问题——
- 这个属性会不会触发重排?(会的话尽量换方案)
- 时长是否在合理范围内?(300ms左右最安全)
- 缓动函数是否自然?(别用linear,除非你故意的)
好了,过渡这块就聊到这儿。说白了就是四个属性:property、duration、timing-function、delay。但真正用好它们,需要你在项目里不断积累手感。下次咱们聊聊更复杂的keyframes动画,那个自由度更高,但也更容易翻车。