Menu

CSS transition 过渡:属性、时长、缓动与延迟

CSS 过渡会在属性值改变时(比如悬停时)把它从旧值平滑地变到新值。本文讲解 transition 简写、并排对比的所有缓动函数、哪些属性能过渡哪些不能、height: auto 的变通写法,以及减少动态效果。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

一个例子看懂过渡

过渡告诉浏览器:这个属性改变时,在这段时间内平滑地变化,而不是瞬间切换。把它写在元素的常规规则里,而不是只写在 :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 或 ms0s
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,可以有很多步,页面加载时就能自己开始,还可以重复或循环。

Coddy 编程语言插图

和 Coddy 一起学编程

开始