一个例子看懂过渡
过渡告诉浏览器:这个属性改变时,在这段时间内平滑地变化,而不是瞬间切换。把它写在元素的常规规则里,而不是只写在 :hover 上:
把鼠标悬停在第一个按钮上:它的颜色在 0.4 秒内渐变成绿色,同时上移 4px;鼠标移开时,同样的过渡会反向播放。第二个按钮设置了 transition: none,会瞬间切换。键盘用户通过 :focus-visible 也能得到同样的效果。
transition 简写
transition: background-color 0.3s ease-in-out 0.1s;
/* property duration timing delay */
| 完整属性 | 设置的内容 | 默认值 |
|---|---|---|
transition-property | 哪些属性做动画(all 或一个列表) | all |
transition-duration | 持续多久,单位为 s 或 ms | 0s |
transition-timing-function | 速度曲线 | ease |
transition-delay | 开始前等待多久(可以为负值) | 0s |
时长的默认值是 0s,所以在你设置时长之前什么都不会动。在简写中,第一个时间值总是时长,第二个是延迟。要给不同属性设置不同参数,用逗号分隔:
.card {
transition: transform 0.2s ease-out, box-shadow 0.4s ease-out;
}
缓动函数并排对比
缓动函数决定过渡过程中速度如何变化。点击 Play,七个条会在同样的 1.5 秒内移动同样的距离:
| 缓动函数 | 感觉 |
|---|---|
linear | 匀速,机械感 |
ease(默认) | 起步快,停得轻柔 |
ease-in | 起步慢,结尾快:适合离开的元素 |
ease-out | 起步快,结尾慢:适合进入的元素 |
ease-in-out | 两头都慢 |
cubic-bezier(x1, y1, x2, y2) | 自定义曲线;y 大于 1 时会冲过头再回落 |
steps(n) | 分成 n 个相等的步骤跳变,没有中间帧 |
对于悬停效果和菜单,ease-out 配合 150ms 到 300ms 之间的时长,手感最灵敏。
哪些属性可以过渡
有中间值的属性都可以过渡:颜色、长度(width、padding、margin)、opacity、transform、box-shadow、border-radius、filter、background-color。在关键字之间跳变的属性,比如 display 或 font-family,会瞬间切换。
最常踩的坑是 height: auto:一个从 height: 0 展开到 height: auto 的面板默认不会有动画,因为浏览器不会计算 0 和“auto”之间的值。一个可靠的变通方法是使用 grid 包裹元素,让它唯一的一行从 0fr 变到 1fr:
aria-expanded 告诉读屏软件用户面板是否已展开,这是动画本身无法传达的。
过渡结束时运行代码
过渡结束时会触发 transitionend 事件,每个属性触发一次。可以用它在动画之后做些事情,比如移动焦点或移除元素:
尊重减少动态效果的设置
有些人会在系统设置里打开“减少动态效果”,因为屏幕上的运动会让他们头晕或分心。用一个媒体查询保留颜色和透明度的渐变,去掉大幅度的移动:
@media (prefers-reduced-motion: reduce) {
.btn { transition: background-color 0.2s ease; } /* no movement, only the color */
}
让过渡保持流畅
transform 和 opacity 是做动画开销最小的属性:浏览器移动元素或让它淡入淡出时,不需要重新计算布局。过渡 width、height、top 或 margin 会让浏览器在每一帧都重新排版页面,在性能较差的手机上可能会卡顿。要移动某个元素,优先用 transform: translate(),而不是 left 或 margin。transform 页面介绍了这些函数。
常见错误
transition只写在:hover规则里。效果进入时有动画,但悬停结束时会直接跳回。把transition写在基础规则上。- 时长没有单位。
transition: opacity 300是无效的。写成300ms或0.3s。 - 使用
transition: all。它会让每个发生变化的属性都做动画,包括你并不想做动画的属性,还可能让布局变化变得迟缓。把属性逐一列出来。 - 以为
display: none会淡出。display会瞬间切换。改为让opacity淡出(并在淡出结束时设置visibility: hidden)。 - 过渡到
height: auto。它会直接跳变。使用上面介绍的grid-template-rows技巧。 - 小交互用了很长的时长。按钮或菜单上超过大约 400ms 的过渡都会显得慢。
常见问题
CSS 过渡是怎么工作的?
你在元素上声明哪些属性要做动画、持续多久,例如 transition: background-color 0.3s ease;。当这个属性的值改变时(通过 :hover、类名或 JavaScript),浏览器会从旧值平滑过渡到新值,而不是瞬间切换。
transition 简写中值的顺序是什么?
transition: property duration timing-function delay;,例如 transition: opacity 0.3s ease-in 0.1s;。唯一的硬性规则是:第一个时间值是时长,第二个是延迟。多个过渡之间用逗号分隔。
为什么我的 CSS 过渡不生效?
常见原因:transition 只写在 :hover 规则里(所以进入时有动画,离开时直接跳回);属性不能做动画(display,或 height 变到 auto);时长没有单位(写成 0.3 而不是 0.3s);或者值在元素加入页面的同一刻就改变了。
height: auto 能做过渡吗?
并非所有浏览器都能用普通的 height 过渡做到:插值到 auto 需要较新的 interpolate-size 属性,而它还没有得到全面支持。常见的变通方法是在 grid 包裹元素上把 grid-template-rows 从 0fr 过渡到 1fr,或者把 max-height 过渡到一个比内容更大的值。
CSS 中 transition 和 animation 有什么区别?
过渡在属性改变时运行一次,只在两个值之间变化。动画使用 @keyframes,可以有很多步,页面加载时就能自己开始,还可以重复或循环。