你有没有遇到过这种情况:照着别人的代码把 transition: all 0.3s ease 抄上去,页面确实动了,但动起来总有一种说不出来的“假”。按钮变色像是在硬切,卡片伸缩像根老化弹簧,鼠标一移走元素“啪”地弹回原样。于是你开始怀疑自己代码写错了,把属性位置换了又换,最后干脆一句“浏览器渲染问题”把锅甩了出去。
其实问题不在浏览器,也不在你不够细心,而是 CSS 过渡缓动这条线,绝大多数教程只让你背语法,从没告诉你怎么用。这篇不绕弯子,先从“浏览器到底在过渡什么”讲起,再把缓动函数掰开揉碎,最后给三个可以直接抄进项目的案例(涟漪扩散、流光边框、数字滚动)和几个新手必踩的坑位。适合谁看?刚写完静态页面想给交互加点质感的同学,以及在面试里被问到“transition 和 animation 区别”会卡壳的同学。看完你会发现,缓动这件事,本质上就是给元素一点“人的反应速度”。
1. transition 全写在一起就能上手,但真正决定成败的是你写在哪个状态
1.1 一条 transition 简写里到底藏了什么
transition: all 0.3s ease; 是新手最常写的“全家桶”,它能动起来,但大概率不是最优解。拆开看,这条简写一次性设置了四个子属性:
transition-property:要过渡的属性,all表示全部transition-duration:过渡耗时transition-timing-function:缓动函数transition-delay:延迟多久才开始过渡
写成完整形式是:
css复制.btn {
transition-property: all;
transition-duration: 0.3s;
transition-timing-function: ease;
transition-delay: 0s;
}
如果你想同时过渡多个属性,可以用逗号分隔,比如:
css复制.btn {
transition: background-color 0.25s ease, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
很多人只知道“写属性加时长”,却不知道 transition-property 指定的是“你想让哪一个属性在变化时生成补间帧”。如果写 all,相当于告诉浏览器“这个元素上所有可过渡属性发生变化时都给我补间”,看起来省事,但窗口 resize、样式刷新时会把大量无关属性纳入过渡计算,体感就是卡顿和抖动。
还有个隐藏细节:transition 的默认值并不是很多人以为的“没有动画”。就算你只写 transition: 1s,浏览器也会自动补上 all、ease,但因为 transition-duration 有了具体值,所有属性变化都会被纳入过渡。如果你只想要某个属性动,就必须显式列出来。
1.2 一个真实按钮:为什么鼠标移开后反而跳变
先看一段再普通不过的代码:
css复制.btn {
width: 160px;
height: 44px;
border: none;
border-radius: 8px;
background: #4f8cff;
color: #fff;
transition: background-color 0.25s ease;
}
.btn:hover {
background: #2b6bff;
}
这段没问题。transition 写在 .btn 默认状态上,鼠标移入和移出时,background-color 都有过渡效果。
但很多新手会不自觉写成这样:
css复制.btn:hover {
background: #2b6bff;
transition: background-color 0.25s ease;
}
这时候鼠标移入,过渡正常播放;鼠标一移出,:hover 状态被撤销,transition 属性连同 background-color 一起被移除,元素瞬间弹回浅蓝色。这个“还原跳变”是很多人口中“越写越僵硬”的源头。
结论一句话:transition 应该写在元素的默认状态上,让它成为元素的“固有属性”;:hover、:focus 等状态只负责修改属性值,不要负责“开启动画”。
1.3 时间和延迟的直觉:不是越长越好
过渡时间的选择,直接决定用户觉得“灵敏”还是“拖沓”。我做交互时有个基准线:100ms 到 300ms 是交互反馈最舒服的范围。少于 100ms 几乎感觉不到过渡,超过 400ms 用户会开始觉得系统变慢了。
具体场景可以这样拆:按钮颜色变化用 200ms 左右;弹窗入场用 200ms 到 300ms;大区块移动可以放到 350ms;那种需要刻意强调的“超级慢动作”另说。至于 transition-delay,新手常常忽略它,但它非常适合做“错峰”效果。比如涟漪扩散时,第二层光圈延迟 80ms 再开始,视觉上就有了一前一后的层次感,而不是糊成一团。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 缓动函数是手感差异的根源:从 linear 到 cubic-bezier 的自定义逻辑
2.1 浏览器在过渡时到底做了什么
transition 的本质是补间动画。起始值和目标值中间有无数可能的中间值,浏览器拿到这两个值之后,会在设定的时长内,按帧把中间值算出来填进去。
比如一个元素 left 从 0px 变到 200px,耗时 0.4s。浏览器不会瞬间把 200px 丢给你,而是每 16ms(一帧)取一个中间值,让你看到元素从 0 平滑走到 200 的过程。
`transition-timing-functi
