steps() 的动画触发时机在每个时间区间的末尾,如 steps(6) 将 600ms 分为 6 段,第1帧显示 0–100ms,第2帧 100–200ms……第6帧 500–600ms。

steps() 函数的动画触发时机到底在哪?
steps() 本身不驱动帧序列,它只是告诉 CSS 动画引擎:把一段连续的过渡过程“切片”成指定数量的离散步进。真正决定哪一帧显示哪张位图的,是背景图的 background-position(或 clip-path、mask-position)配合关键帧的线性变化。
常见错误是直接对 opacity 或 transform 用 steps(8),结果发现画面“闪”或“跳”,因为这些属性无法对应到图集中的具体帧位置。
- 必须用单张雪碧图(sprite sheet),所有帧水平或垂直等距排列
- 动画属性必须是能精确映射到帧坐标的,比如
background-position-x - 总动画时长要和帧数、单帧时长对齐,例如 8 帧、每帧 100ms → 总时长
800ms
水平雪碧图 + background-position 的标准写法
假设你有一张横向排列的 6 帧雪碧图,每帧宽 120px,总宽 720px,容器宽高均为 120px:
@keyframes sprite-roll {
0% { background-position-x: 0; }
100% { background-position-x: -600px; }
}
.element {
width: 120px;
height: 120px;
background-image: url("sprite.png");
background-size: 720px 120px;
animation: sprite-roll 600ms steps(6) infinite;
}
注意三点:
-
background-position-x从0到-600px(即移动 5 个间隔,不是 6 个) -
steps(6)表示把 600ms 分成 6 段,每段 100ms 显示固定一帧 -
background-size必须显式声明,否则缩放会导致定位偏移
为什么用 steps(6) 而不是 steps(5) 或 steps(6, end)?
steps(6) 默认等价于 steps(6, end):动画在每个时间区间的末尾立即跳到下一帧。这意味着第 1 帧显示在 0–100ms,第 2 帧在 100–200ms……第 6 帧在 500–600ms。
如果误写成 steps(5),CSS 会把 600ms 分成 5 段(每段 120ms),只显示前 5 帧,最后一帧永远不出现;
如果用 steps(6, start),第一帧只在 0ms 瞬间显示,随后立刻跳到第 2 帧,造成首帧丢失。
实际中几乎总是用 steps(N) 或 steps(N, end),start 仅在需要“提前占位”的极少数 UI 反馈场景中考虑。
移动端兼容性与性能隐患
steps() 在 iOS Safari 13.4+ 和 Android Chrome 80+ 中支持良好,但老版本(如 iOS 12)会退化为 ease,导致动画糊成一片。
更隐蔽的问题是:
- 雪碧图过大会触发 GPU 内存警告,尤其在低端安卓机上容易卡顿或白屏
- 不要用
background-image加will-change: background-position强制硬件加速——多数情况下反而降低帧率 - 替代方案:用
clip-path配合steps()切换可见区域,对大图更友好,但 IE/Edge Legacy 完全不支持
逐帧动画的复杂点不在写法,而在于帧对齐精度和设备渲染路径的不可见差异。一张图里像素偏移 1px,或动画时长算错 10ms,在某些机型上就表现为卡顿或循环错位。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











