steps(n, end) 表示将动画均分为n段、每段末跳变,共呈现n+1个静止状态;实现N帧动画应设steps(N-1, end),配合background-position切换雪碧图帧,禁用缓动以确保离散效果。

step() 函数的参数含义和常见误用
steps() 不是“帧数”,而是“跳转次数”——它控制动画在时间轴上被切成几段,每段内值保持不变,到段尾才突变。传参 steps(4, end) 表示把整个动画时长均分为 4 段,每段末尾跳一次(共跳 4 次),最终呈现 5 个静止状态(含起始态)。很多人误写成 steps(4) 却期望看到 4 帧动画,结果发现最后一帧一闪而过或缺失。
关键点:
-
steps(n, start):第 0% 时刻就跳第一次,适合“先动后停”逻辑(如像素角色抬脚瞬间) -
steps(n, end):默认行为,在 100% 时刻才完成最后一次跳变,更符合逐帧播放直觉 - 若要精确控制 N 帧动画(N 张图/状态),应设
steps(N - 1, end),否则会多出一个冗余静止帧
配合 background-position 实现像素图集动画
最常用且兼容性好的方案:把所有帧横向拼成一张雪碧图(sprite),用 background-position 水平偏移切换帧。注意图集帧宽必须一致,且动画需禁用缓动——否则 steps() 失效。
示例(16×16 像素每帧,共 6 帧,图集总宽 96px):
.pixel-walk {
width: 16px;
height: 16px;
background-image: url('walk-sprite.png');
background-size: 96px 16px;
animation: walk 0.6s steps(5, end) infinite;
}
@keyframes walk {
0% { background-position-x: 0; }
100% { background-position-x: -96px; }
}
这里 steps(5, end) 对应 6 帧,background-position-x 从 0 到 -96px 跨越 6 个位置(0, -16, -32, -48, -64, -80),但注意:终点设为 -96px 是为了触发第 6 次跳变(因 steps(5) 只有 5 次跳变,需靠位移范围兜住第 6 帧显示)。
transform + step() 做位移类像素动画的陷阱
用 transform: translateX() 做行走位移时,steps() 容易导致“瞬移感过强”或“卡顿不均”。根本原因是像素动画依赖离散坐标,而 CSS transform 的数值是浮点计算,浏览器可能插值渲染中间态(尤其在缩放非 100% 的页面中)。
稳妥做法:
- 确保容器
image-rendering: pixelated(对背景图生效,对 transform 无效,但能提示渲染器) - 位移值严格用整数像素,例如
translateX(-16px),避免translateX(-16.5px) - 若必须用 transform 动画,加
will-change: transform提前升层,减少合成抖动 - 慎用
transform: scale()配合 step——缩放本身就会模糊像素,与风格冲突
Firefox 和 Safari 中 steps() 的兼容细节
Firefox 低版本(steps(n, start) 支持不稳定,建议统一用 end;Safari(iOS 15.4 之前)在 animation-timing-function 中解析 steps() 时,若省略第二个参数,可能回退为 ease 而非默认 end。
生产环境建议写全:
- 始终显式写
steps(5, end),不写steps(5) - 搭配
@supports (animation-timing-function: steps(1, end))做特性检测,降级为 JS 控制 class 切换 - 不要依赖
step-start/step-end别名——它们语义模糊且部分安卓 WebView 不识别
真正难的不是写出第一帧,而是让第 37 帧在旧 iPad 上也不糊边、不丢帧、不抢主线程。step() 只是开关,背后是图集精度、timing function 解析差异、以及你有没有在 DevTools 里真机录屏看每一帧的实际渲染耗时。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











