贝塞尔缓动曲线实战:用四个控制点写出有手感的 CSS 动效
讲清 cubic-bezier 四个控制点怎么塑造 CSS 缓动曲线,ease 与 ease-in-out 的真实区别,以及怎么调出回弹超调,让 transition 和 animation 的过渡不再生硬。
贝塞尔缓动曲线实战:用四个控制点写出有手感的 CSS 动效
一个过渡动画好不好看,八成不在时长,而在缓动。同样是 0.3 秒,线性匀速看着像机器,而一条调好的三次贝塞尔曲线能让元素"先慢后快再稳稳落定",一下就有了手感。这篇我把 cubic-bezier 从控制点讲到回弹,带你把 CSS 缓动彻底吃透。
四个数到底在描述什么
CSS 里写 transition-timing-function: cubic-bezier(x1, y1, x2, y2),这四个数其实是两个控制点的坐标。整条曲线的起点固定在 (0,0),终点固定在 (1,1),你只能动中间这两个点:第一个控制点 P1 是 (x1, y1),负责拉扯曲线的起段;第二个控制点 P2 是 (x2, y2),负责拉扯收尾段。
横轴 x 是时间进度,纵轴 y 是缓动后的输出值。x 必须落在 0 到 1 之间,因为时间只能向前走,x 超过 1 或小于 0 会让曲线向回折叠,缓动直接失效。y 没这个限制,它可以低于 0 也可以高于 1,这正是回弹和超调的来源。
举个最常见的例子,内置关键字 ease 等价于 cubic-bezier(0.25, 0.1, 0.25, 1)。把它拆开看:P1 在 (0.25, 0.1),起段被压得很低,所以开头慢慢起步;P2 在 (0.25, 1),收尾段被拉到顶,所以结尾冲向终点。开头温柔、结尾利落,这就是 ease 给人的那种"自然"感。
ease 和 ease-in-out 差在哪
很多人随手就写 ease,但分不清它和 ease-in-out 的区别。把它们的等价 cubic-bezier 摆出来就清楚了:
linear=cubic-bezier(0, 0, 1, 1),匀速,一条直线。ease=cubic-bezier(0.25, 0.1, 0.25, 1),起段稍慢,收尾偏快,整体偏向后段加速。ease-in=cubic-bezier(0.42, 0, 1, 1),开头慢,一路加速到底。ease-out=cubic-bezier(0, 0, 0.58, 1),开头快,结尾减速。ease-in-out=cubic-bezier(0.42, 0, 0.58, 1),两头都慢、中间快,左右对称。
关键差别在对称性。ease 不对称,它的减速主要发生在后半段,看起来更利落;ease-in-out 是对称的,进出都收着,显得更平稳克制。按钮点击我偏向 ease 或自定义曲线,而抽屉、弹窗这种大面积位移,ease-in-out 往往更稳。
把曲线调到回弹和超调
关键字最大的局限是表达不了超调。想让一个通知角标弹出时带点"过冲再回落"的弹性,你得把某个 y 推到 1 以上,或者推到 0 以下。常见的 back 缓动是 cubic-bezier(0.68, -0.55, 0.27, 1.55):起段的 y 先压到 -0.55,元素先略微向后缩一下蓄力,收尾的 y 冲到 1.55,越过目标位置再缓缓落回。这种欲扬先抑的节奏,光靠 ease-out 是做不出来的。
第一次手调这种曲线时,我盯着数字改了半天也没找到那个"弹一下"的甜点,后来直接拖手柄:把代表 P2 的手柄拖到曲线框顶部以上,动效预览里的小球立刻冲过头再弹回来,几下就调到了想要的力度。可视化拖拽比盲调数字快太多,这也是我现在写动效一定先开 CSS 缓动曲线生成器 的原因,拖两个手柄,确切的 cubic-bezier() 自动写好,一键复制。
落到真实的 CSS 里
调好的值在 transition 和 animation 两个场景都能用。过渡里这样写:
.card {
transition: transform 0.3s cubic-bezier(0.25, 0.1, 0.25, 1);
}
.card:hover {
transform: translateY(-6px);
}
@keyframes 动画里则换成 animation-timing-function,同一个 cubic-bezier 值直接复用,卡片抬升和淡入就能共享同一种手感。这里有个常被忽略的坑:计时函数只塑造"怎么走",它必须配着时长才有意义。要是忘了写 transition 或 animation 的 duration,曲线再漂亮也没有东西给它去弯,效果完全看不出来。
还有一点别想当然:手柄的 y 位置不等于那一刻的实际值。曲线是把时间 x 映射成输出 y,想知道进度 0.5 处缓动到哪了,得用工具里的反解器读 y,而不是目测手柄落在哪。我排查过一个"动画走到一半像猛地一窜"的 bug,就是用反解器读出进度 0.5 处 y 已经到了 0.85,动作全堆在前半段,重新把手柄往后挪才正常。
把动效曲线定下来后,顺手用 CSS 代码格式化工具 把这些规则整理整齐再提交,团队里每个人拿到的就是同一份排版、同一条计时曲线,谁也不会偷偷改成另一种手感。
缓动这件事没有标准答案,但有方法:理解四个控制点怎么塑造曲线,记住几个关键字的等价值,再用可视化工具把感觉调成精确的数字。这样写出来的过渡,才像是设计过的,而不是默认值凑出来的。
Made by Toolora · Updated 2026-06-13