0

0

CSS中如何使用steps()函数?通过steps()控制动画的步进效果以实现帧动画

看不見的法師

看不見的法師

发布时间:2025-08-28 09:10:01

|

199人浏览过

|

来源于php中文网

原创

steps()函数通过将动画分解为离散步进实现帧动画效果,常用于精灵图、打字机、计数器等场景,配合background-position精确控制每帧显示,相比连续时间函数更具节奏感与性能优势。

css中如何使用steps()函数?通过steps()控制动画的步进效果以实现帧动画

steps()
函数在CSS动画中扮演着关键角色,它能将一个平滑的动画过程分解成一系列离散的“步进”,从而模拟出传统帧动画的效果。简而言之,它允许你精确控制动画在特定时间点只展示预设的几个状态,而不是连续过渡,这对于实现像精灵图动画(sprite sheet animation)这样的逐帧效果简直是量身定制。

解决方案

使用

steps()
函数来控制动画的步进效果,核心在于将其应用于
animation-timing-function
属性。它的基本语法是
steps(number_of_steps, [start | end])

  • number_of_steps
    :这是最重要的参数,它定义了动画总共应该分成多少个离散的步骤。比如,如果你有一个包含10帧的精灵图,通常会设置为10。
  • start | end
    :这个可选参数决定了动画在每个步进间隔的哪个时间点发生状态变化。
    • end
      (默认值):表示动画状态的变化发生在每个步进区间的末尾。这意味着动画会在步进开始时保持当前状态,直到步进结束时才跳到下一个状态。对于大多数从第一帧开始显示,然后逐帧播放的精灵图动画,
      end
      通常是更直观的选择。
    • start
      :表示动画状态的变化发生在每个步进区间的开始。这意味着动画会在步进开始时立即跳到下一个状态,并在整个步进区间内保持这个新状态。在某些需要立即显示下一帧的场景下可能会用到,但初次接触时可能会觉得有点反直觉。

让我们通过一个经典的精灵图动画例子来具体说明:

假设我们有一个横向排列的精灵图,包含了5帧动画,每帧宽度是100px。

立即学习前端免费学习笔记(深入)”;

HTML:

CSS:

.sprite-animation {
    width: 100px; /* 单帧宽度 */
    height: 100px; /* 单帧高度 */
    background-image: url('your-sprite-sheet.png'); /* 你的精灵图路径 */
    background-repeat: no-repeat;
    /* 精灵图总宽度为 5帧 * 100px/帧 = 500px */
    /* 动画总时长 */
    animation: playSprite 1s steps(5) infinite; /* 1秒完成5步,无限循环 */
}

@keyframes playSprite {
    from {
        background-position: 0 0; /* 从第一帧开始 */
    }
    to {
        /* 移动到显示最后一张帧的下一个位置,
           这样 steps(5) 才能在5步中完整显示 0, -100px, -200px, -300px, -400px 这5个位置 */
        background-position: -500px 0;
    }
}

在这个例子中:

  • animation: playSprite 1s steps(5) infinite;
    这行是关键。它告诉浏览器
    playSprite
    这个动画应该在1秒内完成,并且将其过程分解成5个离散的步骤。
    infinite
    让动画无限循环。
  • @keyframes playSprite
    定义了动画的起始和结束状态。
    from { background-position: 0 0; }
    表示动画从精灵图的第一个位置(即第一帧)开始。
  • to { background-position: -500px 0; }
    表示动画结束时,背景图片的位置会移动到-500px。
    • 这里有个小细节,如果你的精灵图有N帧,每帧宽度W,那么
      to
      background-position
      通常设置为
      -(N * W)px 0
      。配合
      steps(N)
      ,浏览器会在N个步进中,每次移动
      W
      像素,从而精确地展示出N个不同的帧。
    • 例如,对于5帧,每帧100px宽的精灵图,
      steps(5)
      会把
      0px
      -500px
      的距离分成5个等长区间。
      end
      模式下,它会在每个区间结束时跳到下一个位置:
      0px
      ->
      -100px
      ->
      -200px
      ->
      -300px
      ->
      -400px
      。当到达
      -500px
      时,动画循环,回到
      0px
      。这样就完美地显示了5帧。

steps()
函数与
linear
ease
等其他时间函数有何不同?

steps()
函数与我们平时更常用的
linear
ease
cubic-bezier
等动画时间函数在本质上有着根本的区别,这不只是表现形式上的不同,更是它们设计哲学的差异。

linear
ease
以及自定义的
cubic-bezier
函数,它们的核心目标是创建平滑、连续的动画过渡。无论你设定的是元素移动、大小变化还是颜色渐变,这些函数都会让属性值在动画持续时间内以一个连续的曲线进行变化,使得视觉上看起来非常流畅,符合我们对现实世界运动的直觉。比如,一个
ease-in-out
的动画,会先慢后快再慢,但整个过程是无缝的。它们模拟的是物理世界中物体加速、减速的过程。

steps()
函数则完全相反,它的目标是实现离散、跳跃式的动画效果。它将一个连续的动画时间轴“切割”成指定数量的等长片段,在每个片段内,动画属性值保持不变,直到片段结束(或开始,取决于
start
/
end
参数),然后立即“跳变”到下一个状态。这就好比翻阅一本动画书,每一页都是一个独立的画面,而不是电影那种连续的运动。

从用途上讲:

  • 连续函数
    linear
    ,
    ease
    等)适用于需要展现平滑、自然过渡的场景,比如按钮的悬停效果、元素的淡入淡出、平滑滚动等。
  • steps()
    函数
    则专为需要模拟逐帧动画、计时器、打字机效果、或任何需要“一步到位”而非“渐进变化”的场景而生。它能赋予动画一种独特的、有时甚至带点复古的风格,就像老式游戏机或早期网络动画那样。

我个人觉得,很多时候我们潜意识里总想把所有动画都做得“丝滑”,但其实

steps()
提供了一种强大的能力,去打破这种惯性思维。它不是为了让动画更流畅,而是为了让动画更有特点,更有节奏感。选择哪种函数,完全取决于你想要传达的视觉感受和动画的叙事方式。

如何利用
steps()
实现一个完美的帧动画,避免画面闪烁或不连贯?

要用

steps()
实现一个完美无瑕的帧动画,避免那些恼人的闪烁或者帧与帧之间的跳动感,确实需要一些技巧和对细节的关注。这不仅仅是写几行CSS那么简单,它涉及到资源准备、CSS配置的精确匹配,以及对
steps()
工作原理的深入理解。

  1. 精心准备精灵图(Sprite Sheet)

    绘蛙-多图成片
    绘蛙-多图成片

    绘蛙新推出的AI图生视频工具

    下载
    • 统一帧尺寸:这是基础中的基础。所有帧的宽度和高度必须完全一致。任何微小的偏差都会导致动画在切换时出现“抖动”或“跳跃感”。
    • 无缝连接:帧与帧之间不能有任何空隙或边框。如果你的精灵图是从多个独立图片拼接而成,确保它们是像素级紧密相连的。
    • 优化尺寸:尽量将精灵图的尺寸优化到最小,减少文件大小,这有助于加快加载速度,尤其是在移动设备上。
  2. 精确计算

    background-position

    • 假设你的精灵图有
      N
      帧,每帧宽度为
      W
      像素。
    • @keyframes
      中的
      from
      通常是
      background-position: 0 0;
    • to
      的关键是设置为
      background-position: -(N * W)px 0;
      。这里是
      -(总帧数 * 单帧宽度)
      ,而不是
      -(总帧数 - 1 * 单帧宽度)
    • 为什么是
      -(N * W)px
      ?因为
      steps(N)
      会将
      0
      -(N * W)px
      这个总距离平均分成
      N
      个步进。在
      end
      模式下,它会在每个步进结束时跳到下一个位置。这样,它会依次显示
      0
      -W
      -2W
      ...一直到
      -(N-1)W
      ,正好覆盖了N帧的所有视觉位置。如果你的
      to
      -(N-1)W
      ,那么最后一个步进就可能无法完整显示最后一帧,或者显示不正确。
  3. animation-duration
    number_of_steps
    的和谐

    • animation-duration
      是你动画的总时长,
      number_of_steps
      是你精灵图的帧数。
    • animation-duration / number_of_steps
      就是每一帧的显示时间。确保这个时间足够长,让用户能看清每一帧,但又足够短,保持动画的流畅感。
    • 例如,10帧的动画,如果
      animation-duration
      是1秒,那么每帧显示0.1秒。
  4. animation-fill-mode
    的使用

    • 如果你希望动画在播放完毕后停留在最后一帧,而不是跳回初始状态,务必设置
      animation-fill-mode: forwards;
      。这对于非循环动画尤为重要。
    • 对于循环动画,通常会用
      animation-iteration-count: infinite;
      ,这时
      forwards
      的影响就不那么明显了。
  5. 理解

    start
    end
    的细微差别

    • 对于大多数精灵图动画,
      steps(N, end)
      是更直观和常用的选择。它确保了在动画的每个“瞬间”都能完整地展示一帧,并且从第一帧开始。
    • steps(N, start)
      会在每个步进开始时立即跳到下一帧。这意味着,如果你有N帧,使用
      steps(N, start)
      ,你可能需要将
      @keyframes
      to
      值调整为
      -( (N-1) * W )px
      ,或者调整
      number_of_steps
      N+1
      并配合
      to
      -(N * W)px
      ,这取决于你如何定义第一帧和最后一帧。我个人觉得
      end
      模式在处理精灵图时逻辑更清晰,出错的概率也小。

我遇到过不少人,包括我自己,在刚开始使用

steps()
时,常常会因为
number_of_steps
to
background-position
值不匹配,或者对
start
/
end
理解不清,导致动画出现“少一帧”或“多跳一下”的问题。记住,关键是让
steps(N)
能够精确地在
N
个步进中,将
background-position
0
移动到
-(N * W)px
,并且确保每个步进都完整地展示一帧。

steps()
函数在实际项目中有哪些进阶应用场景和性能考量?

steps()
函数远不止于制作简单的精灵图动画,它在实际项目中的应用场景其实非常广阔,而且在性能方面也有其独特的优势。

进阶应用场景:

  1. 打字机效果(Typewriter Effect)

    • 通过动画
      width
      clip-path
      属性,配合
      steps()
      函数,可以实现文字一个字符一个字符地逐渐显示出来,就像老式打字机一样。
    • 例如,一个包含文字的
      span
      ,初始
      width: 0; overflow: hidden;
      ,然后动画
      width
      到文字的完整宽度,同时
      animation-timing-function: steps(字符数量, end);
      。这样每个步进就对应显示一个字符。
  2. 数字计数器/加载进度条

    • 当需要显示一个从0到100的计数器,但又不希望数字平滑滚动,而是离散地跳动时,
      steps()
      就很有用。
    • 可以创建一个包含0-9所有数字的精灵图,或者直接对一个数字元素进行
      transform: translateY()
      动画,配合
      steps(10)
      来实现数字的跳动。
    • 加载进度条也可以用
      steps()
      来模拟分段加载的效果,比如每20%一个步进,而不是平滑填充。
  3. 自定义加载动画(Loading Spinners)

    • 除了传统的平滑旋转加载动画,
      steps()
      可以制作出更有趣、更具风格的加载指示器,例如模拟老式电影胶片转动、齿轮转动等效果。
    • 结合SVG图形和
      steps()
      ,可以创造出非常独特且性能优异的加载动画。
  4. 游戏UI和特效

    • 在网页游戏中,
      steps()
      是实现角色动画、爆炸效果、技能图标切换等离散视觉效果的利器。它能轻松复刻像素游戏的复古感,或者实现精确到帧的UI反馈。
    • 一些交互式的按钮或图标,在点击或悬停时,也可以通过
      steps()
      展现出分阶段的反馈动画。

性能考量:

steps()
函数在性能方面通常表现出色,甚至在某些情况下优于JavaScript实现的帧动画。

  1. GPU加速:CSS动画,尤其是那些不涉及布局或绘制(如
    transform
    opacity
    )的动画,通常可以由浏览器的GPU进行加速。
    steps()
    作为
    animation-timing-function
    的一部分,同样受益于此。这意味着动画在视觉上会更流畅,对CPU的负担更小。
  2. 浏览器原生优化
    steps()
    是浏览器原生支持的功能,它在底层被高度优化。与通过JavaScript手动改变
    background-position
    src
    来切换帧相比,
    steps()
    将动画逻辑交给了浏览器渲染引擎处理,通常效率更高。
  3. 资源大小:性能瓶颈更多地在于你使用的精灵图本身。如果精灵图过大(尺寸巨大或文件体积庞大),那么加载和渲染它可能会消耗更多资源,但这与
    steps()
    函数本身无关。优化图片资源(压缩、选择合适的格式)是更关键的。
  4. 避免重绘和回流:在使用
    steps()
    进行
    background-position
    动画时,它通常只涉及重绘(repaint),而不会触发耗性能的回流(reflow/layout)。如果动画涉及到
    width
    height
    等属性,则可能会触发回流,这时需要评估其影响。

我个人经验是,当需要实现帧动画时,

steps()
几乎总是我的首选,因为它在保持代码简洁的同时,提供了出色的性能和精确的控制。唯一的限制可能就是它的“步进”特性,不适用于需要平滑连续过渡的场景。但在它擅长的领域,
steps()
无疑是一个非常强大且可靠的工具

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

552

2023.06.20

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

374

2023.07.04

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

731

2023.07.04

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

475

2023.09.01

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

394

2023.09.04

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

990

2023.09.04

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

656

2023.09.12

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

551

2023.09.20

PHP 表单处理与文件上传安全实战
PHP 表单处理与文件上传安全实战

本专题聚焦 PHP 在表单处理与文件上传场景中的实战与安全问题,系统讲解表单数据获取与校验、XSS 与 CSRF 防护、文件类型与大小限制、上传目录安全配置、恶意文件识别以及常见安全漏洞的防范策略。通过贴近真实业务的案例,帮助学习者掌握 安全、规范地处理用户输入与文件上传的完整开发流程。

3

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 0.6万人学习

Node.js 教程
Node.js 教程

共57课时 | 8.5万人学习

CSS3 教程
CSS3 教程

共18课时 | 4.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号