ease 是有惯性启停的缓动,非匀速或静止启停;steps() 是离散时间切片,步数和 jump 关键字决定状态切换时机,混用 ease 与 steps 无效。

ease 是缓动,不是匀速,更不是“慢慢开始再慢慢结束”的模糊描述
很多人看到 ease 就默认它是“先慢后快再慢”,其实这是对 cubic-bezier(0.25, 0.1, 0.25, 1) 的过度简化。它的真实曲线在开头和结尾斜率不为零,意味着起始和结束都有加速度——不是停顿,而是“有惯性地启停”。
实操建议:
- 用
devtools的动画面板拖拽贝塞尔曲线手柄,观察时间轴上progress的实际分布,比背口诀管用 - 如果想真正“从静止启动+静止停止”,
ease-in-out更接近(cubic-bezier(0.42, 0, 0.58, 1)),但依然不是完全归零斜率 -
ease在长周期动画(>1s)中容易显得“拖沓”,因为中间段速度偏高,人眼会忽略加速/减速段
steps() 不是“帧动画”,是离散时间切片,步数和 jump 关键字决定落点
steps(4, end) 不等于“播放4帧 GIF”,它把整个动画时长等分为4段,在每段**结束时**瞬间跳到下一个状态;而 steps(4, start) 是在每段**开始时**就跳。这个“jump”位置直接影响视觉节奏感。
常见错误现象:
立即学习“前端免费学习笔记(深入)”;
- 写
steps(5)却没意识到默认是end,导致第一帧空白(第0%状态不显示),动画像“少播一帧” - 用
steps(1)想实现“开关式切换”,结果发现还是有过渡——因为steps(1, end)表示:0–100% 全程保持初始值,100% 时刻才跳终值;想立刻跳,得用steps(1, start) - 配合
@keyframes使用时,若关键帧只有0%和100%,steps(n)实际只产生 n+1 个离散状态(含起始),不是 n 次变化
ease 和 steps 混用会失效,transition-timing-function 只认一个值
写成 transition-timing-function: ease, steps(3); 是无效的——CSS 不支持多 timing-function 同时作用于同一属性。浏览器会直接丢弃整条声明或只取第一个(取决于解析策略),结果往往是回退到 ease。
使用场景与替代方案:
- 想让位移用
ease、透明度用steps(2)?必须拆成独立transition:transition: transform 0.3s ease, opacity 0.3s steps(2); - 想在一个动画里先缓动再步进?不能靠单个
transition,得用@keyframes+animation-timing-function: steps(2)配合关键帧位置控制 - 注意
transition的 timing-function 对所有过渡属性生效,除非显式按属性拆分
steps 的兼容性没问题,但 Safari 对 start/end 的处理曾有偏差
iOS 13.3 之前,Safari 把 steps(2, start) 解析成类似 steps(2, end),导致第一帧意外显示终值。现在已修复,但如果你还要支持老版本 iOS,得加降级:
animation-timing-function: steps(2, start); animation-timing-function: -webkit-steps(2, start); /* 旧 Safari */
性能影响很小——steps() 是纯时间切片,不触发重排,但若配合频繁变化的 transform 或 opacity,仍需确保元素开启硬件加速(will-change: transform 或 transform: translateZ(0))。
最容易被忽略的是:steps 的“步数”必须是整数,传 steps(2.5) 会被忽略,回退到 ease,且不会报错,很难排查。










