steps() 是一种分步计时函数,使动画按固定帧数跳变而非平滑过渡;它控制变化发生的时机与次数,而非变化速率曲线,常用于雪碧图、翻牌等离散状态切换场景。

steps() 是什么,它和 transition/timing-function 有什么区别
steps() 不是传统意义上的缓动函数,而是一种“分步计时函数”,它让动画不平滑过渡,而是按固定帧数跳变。比如雪碧图(sprite)动画、数字翻牌、进度条逐格推进等场景,都需要这种“卡顿感”而非 cubic-bezier() 那种流畅插值。
关键区别在于:普通 timing-function(如 ease)控制的是**变化速率曲线**,而 steps(n, [start | end]) 控制的是**变化发生的时机与次数**——它把整个动画时长均分为 n 段,每段内属性值保持不变,只在段边界处突变。
steps(5, start) 和 steps(5, end) 的行为差异
第二个参数决定“第一帧是否立即生效”。这是最容易混淆的点,直接影响视觉起始状态:
-
steps(5, start):动画开始时立刻跳到第2帧(即第1次变化发生在 t=0),共发生 5 次跳变,最后一帧持续到动画结束前一瞬间 -
steps(5, end):动画开始后等待 1/5 时长才跳第一次,共发生 5 次跳变,且第5次跳变发生在动画结束时刻(t=100%)
举例:一个 1s 动画用 steps(4, end),实际是每 250ms 跳一次,第4次跳在 1000ms 精确发生;而 steps(4, start) 第一次跳在 0ms,然后在 250ms、500ms、750ms 各跳一次,第4帧从 750ms 持续到 1000ms。
立即学习“前端免费学习笔记(深入)”;
用 steps 实现雪碧图水平切换动画
假设一张雪碧图宽 800px,包含 8 张等宽图标(每张 100px),想让它从左到右逐帧播放:
img {
width: 100px; height: 100px;
background: url('sprite.png') no-repeat;
animation: slide 0.8s steps(8, end) infinite;
}
@keyframes slide {
from { background-position: 0 0; }
to { background-position: -800px 0; }
}
注意三点:
- 总位移是
-800px(8 帧 × 每帧 100px),不是-700px——steps(8)会把这 800px 均分 8 次跳,每次跳-100px - 必须用
end(默认值),否则start会导致第1帧在 0ms 就偏移 -100px,首帧显示错位 - 容器宽高要严格匹配单帧尺寸,否则背景裁剪会露馅
steps 在数字递增动画中为什么不能直接用 counter-increment
counter-increment 是伪元素计数器,本身不触发重绘,无法被 animation 驱动。想做“0→1→2→…→100”的逐数动画,得换思路:
- 用
@keyframes定义 101 个to关键帧太笨重,不可维护 - 更可行的是 JS +
steps(100, end)控制 class 切换,或用 CSS 自定义属性 +calc()计算当前帧,再配合steps()触发渲染 - 但最简方案其实是:放弃纯 CSS,用
requestAnimationFrame控制数字更新,CSS 只负责数字容器的入场/高亮动画,steps()在这里没用武之地
也就是说:steps() 适合离散的、预定义的视觉状态切换,不适合动态生成的数值序列。


















