先记住这个答案
cubic-bezier(x1, y1, x2, y2) 用四个数字定义一条贝塞尔曲线。曲线的横轴代表时间进度(0 到 1),纵轴代表动画完成比例。起点 (0,0) 和终点 (1,1) 是固定的,你能控制的是中间两个控制点的位置。x1 和 x2 必须在 0 到 1 之间,因为时间不能倒流;y1 和 y2 没有这个限制,超出 0-1 就会出现「冲过头再弹回来」的效果。
- 两个 x 值限制在 0 到 1 之间
- y 值超出范围会产生回弹效果
ease等关键字就是预设的贝塞尔值- Chrome DevTools 可以可视化调曲线
四个参数怎么影响动画节奏
想象一条从左下角到右上角的曲线。前半段曲线越陡,动画启动越猛;后半段曲线越平,动画收尾越慢。两个控制点就像两只手拽着曲线,把它往不同方向拉。
/* ease: 慢启动、快中段、慢收尾 */
.box-ease { transition: transform 0.4s cubic-bezier(0.25, 0.1, 0.25, 1); }
/* linear: 匀速,曲线就是一条直线 */
.box-linear { transition: transform 0.4s cubic-bezier(0, 0, 1, 1); }
/* ease-in-out: 两头慢、中间快 */
.box-in-out { transition: transform 0.4s cubic-bezier(0.42, 0, 0.58, 1); }
/* 自定义弹性:y2 超过 1,结尾会冲过终点再回来 */
.box-bounce { transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); }ease、linear、ease-in、ease-out、ease-in-out 这五个关键字并不是独立的算法,它们就是五组固定的 cubic-bezier 值。写关键字和写对应的四个数字效果完全一样。
为什么 x 必须在 0 到 1 之间
x 轴代表时间进度。如果 x 允许超出 0-1,曲线就可能在某一段向左拐,意味着时间在倒流。浏览器无法在同一个时刻算出两个不同的进度值,所以规范要求 x1 和 x2 必须在 0 到 1 之间。写了超出范围的值,浏览器会忽略整条 transition 声明。
/* y1 = 1.56 超过 1,动画开头会先冲过终点位置 */
.overshoot {
transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* 鼠标悬停时元素会先放大到比目标值还大,再弹回来 */
.overshoot:hover {
transform: scale(1.1);
}y 超过 1 时,动画中间某一帧的属性值会超过目标值。比如目标是 scale(1.1),中间可能到 scale(1.15) 再弹回来。这就是 CSS 里不用 JavaScript 也能做弹性效果的原理。
怎么调出想要的曲线
Chrome DevTools 的 Styles 面板里,点击 transition 值前面的紫色曲线图标,会弹出一个可视化编辑器。拖动两个控制点就能实时预览效果,调好后直接复制 cubic-bezier(...) 值。这比靠猜数字高效得多。
除了贝塞尔曲线,CSS 还有 steps() 函数用来做逐帧动画,比如精灵图的帧切换。steps(5) 会把动画分成 5 步跳跃式完成,没有中间的平滑过渡。
容易答错的地方
- 以为
ease就是匀速 ease是默认值,但它不是匀速。它等价于cubic-bezier(0.25, 0.1, 0.25, 1),前段慢、中段快、尾段减速。匀速要用linear。- 以为 y 也不能超出 0-1
- 只有 x 有限制,y 没有。y 超出范围是做弹性动画的常见手法,完全合法。
面试官还会怎么问?
transition-timing-function 和 animation-timing-function 有区别吗?
写法完全一样,都接受关键字或 cubic-bezier()。区别在于 animation-timing-function 作用在每两个关键帧之间,而不是整段动画。比如一个 @keyframes 有三帧,缓动函数会分别应用在第一帧到第二帧、第二帧到第三帧。
steps() 和 cubic-bezier() 能混着用吗?
不能写在同一个值里,但同一个元素的不同属性可以分别指定。比如 transition: opacity 0.3s ease, background-image 0.3s steps(3) 是合法的。
怎么做一个先快后慢再弹一下的效果?
用 cubic-bezier 把 y2 设成大于 1 的值,比如 cubic-bezier(0.22, 0.61, 0.36, 1.2)。动画末尾会超过目标值再回弹。如果需要多次弹跳,cubic-bezier 做不到,要换成 @keyframes 手动写多个关键帧。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。