steps()是animation-timing-function的取值,将动画切分为等距离散步进,实现像素风逐帧动画的“啪”式跳变;需配合雪碧图、精确keyframes和匹配帧数的steps(n,end)三要素,控制变化时机而非内容。

steps() 是什么,和逐帧动画有什么关系
steps() 不是动画本身,而是 animation-timing-function 的一种取值,它把一段连续的过渡「切片」成等距、无缓动的离散步进。做像素风逐帧动画时,你并不希望元素平滑移动或缩放——那会模糊像素边界、破坏复古感;你需要它在每一帧「啪」地跳到下一个状态,就像老式 Game Boy 或 NES 的 sprite 动画。
关键在于:steps() 控制的是「变化发生的时机」,不是「变化的内容」。你仍需用 @keyframes 定义每帧的样式(比如 background-position 偏移),而 steps(n, jump-end) 决定这 n 帧如何被均匀分配到整个动画时长里。
常见错误现象:
— 动画看起来“拖影”或“糊成一片”,其实是用了默认的 cubic-bezier(.25,.1,.25,1);
— 帧数对不上,比如雪碧图有 8 帧却只看到 7 次切换,大概率是 jump-start 或 jump-none 用错了。
怎么写一个能跑通的像素动画 CSS 规则
核心三要素必须同时存在:雪碧图(sprite)、精确的 @keyframes 关键帧、匹配帧数的 steps()。
立即学习“前端免费学习笔记(深入)”;
.sprite-run {
width: 32px;
height: 32px;
background-image: url('run-sprite.png'); /* 8帧横向排列,每帧32×32 */
background-size: 256px 32px; /* 8×32=256 */
animation: run 0.8s steps(8, end) infinite;
}
<p>@keyframes run {
0% { background-position: 0 0; }
100% { background-position: -256px 0; }
}</p>说明:
— steps(8, end) 表示将 0.8s 分为 8 段,每段末尾「跳」一次;end 是最常用值,保证第 1 帧从 0% 开始显示,不闪白;
— background-position 从 0 到 -256px,是因为雪碧图向左拼接,负偏移才能逐帧露出;
— 动画时长除以帧数(0.8s ÷ 8 = 0.1s/帧)就是单帧停留时间,这是节奏感的关键,别盲目套用 1s。
为什么 background-position 经常偏移不准
这不是 steps() 的问题,而是雪碧图尺寸、容器尺寸、定位基准三者没对齐。
- 雪碧图每帧必须严格等宽等高,且无缝拼接(导出时关闭抗锯齿、用整数像素对齐);
-
background-size必须等于「总宽 × 单帧高」,不能靠contain或cover自适应; - 如果容器宽高 ≠ 单帧尺寸,要用
background-repeat: no-repeat+background-position精确控制起始点; - 某些浏览器对小数像素
background-position渲染有偏差,建议所有尺寸用整数 px,避免 rem/em。
典型报错场景:
— Chrome 显示最后一帧卡住不动 → 实际是 100% 位置超出了雪碧图右边界,应设为 -224px(7 帧)而非 -256px(8 帧);
— Firefox 帧跳变不匀速 → 检查是否误写了 steps(8, start),导致第 0 帧不显示,首帧延迟 0.1s 才出现。
兼容性和性能要注意什么
steps() 在所有现代浏览器中支持良好(Chrome 23+、Firefox 16+、Safari 7+),但 IE10 及以下完全不支持,且无降级方案——它不能被 linear 或 cubic-bezier 模拟。
性能方面:
— 避免在大范围元素上批量使用 steps() 动画(比如 100 个像素小人同时跑),改用 will-change: transform 或转为 Canvas;
— 不要用 steps() 配合 transform: scale() 做像素放大,会触发亚像素渲染,反而失真;
— 如果动画只是装饰性(比如 loading 图标),优先用 GIF,体积更小、兼容性更好、CPU 占用更低。
像素动画的“复古感”其实来自克制:帧数少、运动简单、颜色有限。别试图用 steps(32, end) 做丝滑转身,那已经不是复古,是自讨苦吃。


















