CSS 缓动函数 2 级

W3C 首个公开工作草案,

关于本文档的更多细节
此版本:
https://www.w3.org/TR/2024/WD-css-easing-2-20240829/
最新发布版本:
https://www.w3.org/TR/css-easing-2/
编辑草案:
https://drafts.csswg.org/css-easing/
历史:
https://www.w3.org/standards/history/css-easing-2/
实施报告:
https://wpt.fyi/results/css/css-easing
反馈:
CSSWG 问题库
编辑:
(Mozilla)
(苹果公司)
Tab Atkins Jr. (谷歌)
Chris Lilley (W3C)
前任编辑:
Matt Rakow (微软)
(谷歌)
(谷歌)
为此规范建议编辑:
GitHub 编辑器
参与:
IRC: #css 在 W3C 的 IRC
测试:
web-platform-tests css/css-easing
测试套件:
https://wpt.fyi/results/css/css-easing/

摘要

该 CSS 模块描述了一种方式,让作者定义一个转换,以控制某个值的变化速率。 应用于动画时,这种转换可以用来产生模拟物理现象(如动量)的动画,或使动画以离散步骤移动,产生机器人般的运动。 2 级增加了更复杂的自定义缓动曲线功能。

CSS 是一种描述结构化文档(如 HTML 和 XML) 在屏幕、纸张等上渲染的语言。

本文档的状态

本节描述了本文档在发布时的状态。 当前 W3C 出版物的列表 以及此技术报告的最新修订 可以在 W3C 技术报告索引找到。

本文档由 CSS 工作组发布,作为首个公开工作草案,使用推荐轨道。 作为首个公开工作草案的发布 并不意味着获得 W3C 及其成员的认可。

这是一个草案文件 并可能随时被其他文件更新、替代 或废弃。 将其引用为工作进展是不合适的。

请通过 在 GitHub 上提交问题(优先), 在标题中包含规范代码“css-easing”,格式如下: “[css-easing] …评论摘要…”。 所有问题和评论均归档。 或者,反馈可以发送至(归档)公共邮件列表www-style@w3.org

本文档受2023年11月3日 W3C 过程文件的约束。

本文档由一个遵循W3C 专利政策的团体制作。 W3C 维护一个公开专利披露列表 与该小组的交付成果相关; 该页面还包括专利披露的说明。 知道某项专利的个人如果认为该专利包含基本主张 必须根据W3C 专利政策第6节披露信息。

1. 引言

本节不是规范性内容。

通常希望控制某些值变化的速率。例如,逐渐增加元素移动的速度可以赋予该元素一种重量感,使其在积聚动能时表现得更加真实。这可以用于制作直观的用户界面元素或令人信服的卡通道具,使其表现得像物理对象一样。另一方面,有时希望动画以不同的步骤前进,例如一个分段的轮子旋转,使得段始终出现在相同的位置。

同样,控制梯度插值的变化速率可以用于产生不同的视觉效果,例如暗示凹面或凸面,或产生条纹效果。

缓动函数 提供了一种通过接受输入进度值并生成相应的输出进度值来转换这些值的方法。

示例缓动函数产生的加速效果。
示例缓动函数产生的加速效果。
给定输入进度为0.7,缓动函数缩放该值以产生输出进度0.52。
将此缓动函数应用于动画将导致动画最初进展较慢,但随后逐渐加快。

1.1. 值定义

本规范使用值定义语法来自[CSS-VALUES-3]。未在本规范中定义的值类型在CSS值与单位[CSS-VALUES-3]中定义。与其他CSS模块的组合可能会扩展这些值类型的定义。

2. 缓动函数

缓动函数 接受一个 输入进度值 并生成一个 输出进度值

缓动函数 必须是一个纯函数,这意味着对于给定的一组输入,它总是生成相同的 输出进度值

输入进度值 是一个范围为 [-∞, ∞] 的实数。通常,输入进度值 在 [0, 1] 的范围内,但当 缓动函数 被链式调用时,这种情况可能不适用。

当缓动函数链式调用时,出现此类情况。例如,在 Web 动画中,缓动函数指定的输出可能成为关键帧效果中某个关键帧上的缓动函数的输入。在这种情况下,关键帧效果上的缓动函数输入可能超出 [0, 1] 范围。

输出进度值 是一个范围为 [-∞, ∞] 的实数。

某些类型的缓动函数还接受一个额外的布尔 before 标志 输入,这将在后续内容中定义。

本规范定义了四种类型的缓动函数,其定义如下。

指定 缓动函数 的语法如下:

<easing-function> = linear | <linear-easing-function> | <cubic-bezier-easing-function> | <step-easing-function>
测试

2.1. 线性缓动函数:linear()

线性缓动函数 是一个 缓动函数,它在其之间进行线性插值。

线性缓动函数 具有 ,它是一个 列表线性缓动点。最初是一个新的空列表

线性缓动点 是一个 结构,具有以下内容:

输入

一个数字或 null

注意:创建线性缓动函数 期间,这只有在为空时才为 null。

输出

一个数字

2.1.1. 语法

线性缓动函数 的语法如下:

<linear-easing-function> = linear(<linear-stop-list>)
<linear-stop-list> = [ <linear-stop> ]#
<linear-stop> = <number> && <linear-stop-length>?
<linear-stop-length> = <percentage>{1,2}
测试

linear() 通过调用 线性缓动函数 并传入其 <linear-stop-list>,作为 列表<linear-stop> 解析为一个线性缓动函数。

2.1.2. 解析

创建线性缓动函数,给定一个 列表<linear-stop> stopList, 执行以下操作。 它返回一个 线性缓动函数失败

  1. function 为一个新的 线性缓动函数

  2. largestInput 为负无穷大。

  3. 如果 stopList 中的少于两个,则返回 失败

  4. 对于每个 stopstopList 中:

    1. point 为一个新的 线性缓动点,其 输出 设置为 stop<number> 作为数字。

    2. point 附加到 function

    3. 如果 stop 具有 <linear-stop-length>,则:

      1. point输入 设置为以下两个值中的较大者:stop<linear-stop-length> 的第一个 <percentage> 作为数字,或 largestInput

      2. largestInput 设置为 point输入

      3. 如果 stop<linear-stop-length> 有第二个 <percentage>,则:

        1. extraPoint 为一个新的 线性 缓动点,其 输出 设置为 stop<number> 作为数字。

        2. extraPoint 附加到 function

        3. extraPoint输入 设置为以下两个值中的较大者:stop<linear-stop-length> 的第二个 <percentage> 作为数字,或 largestInput

        4. largestInput 设置为 extraPoint输入

    4. 否则,如果 stopstopList 中的第一个 ,则:

      1. point输入 设置为 0。

      2. largestInput 设置为 0。

    5. 否则,如果 stopstopList 中的最后一个 , 则将 point输入 设置为以下两个值中的较大者:1 或 largestInput

  5. 对于 function 运行,将具有 null 输入 的值通过线性插值分配给最近的非 null

  6. 返回 function

2.1.3. 序列化

linear() 的序列化包括每个点的输入值,且输入值从不小于前一个点的输入值。

例如:

要获取 线性缓动函数 (linearEasingFunction) 的 序列化计算值,执行以下操作。 它返回一个 字符串

  1. output 为 "linear("。

  2. 对于每个 pointlinearEasingFunction 中:

    1. 如果 point 不是 linearEasingFunction 列表中的第一个项, 则向 output 追加 ", "。

    2. point输出 的计算值, 作为 <number>, 追加到 output

    3. output 追加 " "。

    4. point输入 的计算值, 作为 <percentage>, 追加到 output

  3. output 追加 ")"。

  4. 返回 output

2.1.4. 线性缓动函数的输出

计算线性缓动输出进度,给定一个 线性缓动函数 linearEasingFunction,以及一个 输入进度值 inputProgress,执行以下操作。 它返回一个 输出进度值

  1. pointslinearEasingFunction

  2. pointAIndexpoints 中最后一个具有 输入 小于或等于 inputProgress的索引, 如果没有匹配项,则为 0。

  3. 如果 pointAIndex 等于 points大小 减去 1,则将 pointAIndex 减 1。

    注意: 这确保我们有下一个 进行比较。

  4. pointApoints[pointAIndex]。

  5. pointBpoints[pointAIndex + 1]。

  6. 如果 pointA输入 等于 pointB输入, 则返回 pointB输出

  7. progressFromPointAinputProgress 减去 pointA输入

  8. pointInputRangepointB输入 减去 pointA输入

  9. progressBetweenPointsprogressFromPointA 除以 pointInputRange

  10. pointOutputRangepointB输出 减去 pointA输出

  11. outputFromLastPointprogressBetweenPoints 乘以 pointOutputRange

  12. 返回 pointA输出 加上 outputFromLastPoint

测试

2.1.5. 示例

linear() 允许定义在一组点之间线性插值的缓动函数。

例如,linear(0, 0.25, 1) 生成一个缓动函数,该函数从 0 线性移动到 0.25,再到 1:

linear(0, 0.25, 1) 绘制在图表上
默认情况下,未显式提供“输入”的条目之间的值均匀分布。 可以使用 <百分比> 提供输入值。

例如,linear(0, 0.25 75%, 1) 生成以下缓动函数,该函数在 75% 的时间内从 0 过渡到 .25, 然后在最后 25% 的时间内从 .25 过渡到 1

linear(0, 0.25 75%, 1) 绘制在图表上。
        图表上有三个点。
        第一个点在 0,0。
        第二个点在 0.75,0.25。
        第三个点在 1,1。
如果为单个输出提供两个输入值, 则会生成两个具有相同输出的点。

例如,linear(0, 0.25 25% 75%, 1) 等效于 linear(0, 0.25 25%, 0.25 75%, 1), 生成以下缓动函数:

linear(0, 0.25 75%, 1) 绘制在图表上。
        图表上有四个点。
        第一个点在 0,0。
        第二个点在 0.25,0.25。
        第三个点在 0.75,0.25。
        第四个点在 1,1。
如果输入超出提供的范围, 则继续沿最近两个点的轨迹进行延伸。

例如,以下是前一个函数的隐式值:

linear(0, 0.25 75%, 1) 绘制在图表上。
        图表上有四个点。
        第一个点在 0,0。
        第二个点在 0.25,0.25。
        第三个点在 0.75,0.25。
        第四个点在 1,1。
        图表的两端沿最近两条线的角度进行了延伸。
linear() 的一个典型用途是提供多个点来创建曲线的错觉。

例如,以下是如何使用 linear() 创建可重复使用的“弹跳”缓动函数:

:root {
  --bounce: linear(
    /* 从开始到第一个弹跳 */
    0, 0.063, 0.25, 0.563, 1 36.4%,
    /* 从第一个到第二个弹跳 */
    0.812, 0.75, 0.813, 1 72.7%,
    /* 从第二个到第三个弹跳 */
    0.953, 0.938, 0.953, 1 90.9%,
    /* 从第三个弹跳到结束 */
    0.984, 1 100% 100%
  );
}

.example {
  animation-timing-function: var(--bounce);
}

定义以 1 100% 100% 结束,以创建两个最终点, 因此大于 1 的输入始终输出 1。

粗略的弹跳缓动图

可以使用更多的点来创建更平滑的结果, 这在较慢的动画中可能是必需的。

2.2. 线性缓动关键字:linear

linear 关键字 生成一个恒等的 线性缓动函数,其 输出进度值 对所有输入均等于 输入进度值

这与 linear(0, 1) 生成相同的结果。

注意: 虽然这会生成一个 线性缓动函数, 但关键字 linear 总是按原样序列化为 linear。 而等效的函数 linear(0, 1) 会序列化为 linear(0 0%, 1 100%)。 这些规则在 序列化 部分有说明。

2.3. 三次贝塞尔缓动函数:easeease-inease-outease-in-outcubic-bezier()

三次贝塞尔缓动函数 是一种 缓动函数, 由四个实数定义, 这些实数指定了三次贝塞尔曲线的两个控制点 P1P2,其端点 P0P3 分别固定在 (0, 0) 和 (1, 1)。 P1P2x 坐标限制在 [0, 1] 范围内。

用作缓动函数的三次贝塞尔曲线。
用作缓动函数的三次贝塞尔曲线。
曲线的形状由控制点 P1P2 的位置确定。
输入进度值作为曲线的 x 值, 而 y 值则为输出进度值。

三次贝塞尔缓动函数 具有以下语法 (使用 [CSS-VALUES-3] 中的符号表示):

<cubic-bezier-easing-function> = ease | ease-in | ease-out | ease-in-out | cubic-bezier(<number [0,1]>, <number>, <number [0,1]>, <number>)

每个值的含义如下:

ease

等效于 cubic-bezier(0.25, 0.1, 0.25, 1)

ease-in

等效于 cubic-bezier(0.42, 0, 1, 1)

ease-out

等效于 cubic-bezier(0, 0, 0.58, 1)

ease-in-out

等效于 cubic-bezier(0.42, 0, 0.58, 1)

cubic-bezier(<number [0,1]>, <number>, <number [0,1]>, <number>)

指定了一个 三次贝塞尔缓动函数。 这四个数字指定了曲线的点 P1P2,表示为 (x1, y1, x2, y2)。 两个 x 值必须在 [0, 1] 范围内,否则定义无效。

上面列出的关键字值如下图所示。

由关键字值生成的缓动函数。
由每个三次贝塞尔缓动函数关键字值生成的缓动函数。

2.3.1. 三次贝塞尔缓动函数的输出

从输入进度映射到输出进度是通过确定给定 x 值(输入进度值)的对应 y 值(输出进度值)来实现的。 对该曲线的评估在许多来源中都有介绍,例如 [FUND-COMP-GRAPHICS]

对于超出 [0, 1] 范围的 输入进度值,曲线会根据最近端点的切线无限延伸,如下所示:

测试

2.4. 阶跃缓动函数:step-startstep-endsteps()

阶跃缓动函数 是一种 缓动函数,它将输入时间分成指定数量的等长区间。 该函数由若干 阶跃 和一个 阶跃位置 定义。 它的语法如下:

<step-easing-function> = step-start | step-end | steps(<整数> , <阶跃位置>?)

<阶跃位置> = jump-start | jump-end | jump-none | jump-both | start | end

测试

每个值的含义如下:

step-start

计算为 steps(1, start)

step-end

计算为 steps(1, end)

阶跃缓动关键字示例。
阶跃缓动函数关键字值示例。
steps(<整数>, <阶跃位置>?)

第一个参数指定函数中的间隔数量。 它必须是大于 0 的正整数, 除非第二个参数为 jump-none,此时它必须是大于 1 的正整数。

第二个参数是可选的,使用以下值之一来指定 阶跃位置

jump-start

第一次跃升发生在 输入进度值为 0 时。

jump-end

最后一次跃升发生在 输入进度值为 1 时。

jump-none

所有跃升发生在 (0, 1) 范围内。

jump-both

第一次跃升发生在 输入进度值为 0 时, 最后一次跃升发生在 输入进度值为 1 时。

start

表现如 jump-start

end

表现如 jump-end

如果省略第二个参数,则默认值为 end

这些值的示例如下:

阶跃缓动函数示例。
阶跃缓动函数示例。

2.4.1. 阶跃缓动函数的输出

在阶跃发生的确切点,函数的结果在概念上是阶跃的顶端。然而,如果传递的输入中附带了 前标志 并为 true,则函数结果将在阶跃点对应阶跃的底端。

以下是 前标志 如何影响该函数行为的示例, 假设动画使用一个 阶跃缓动函数,其 阶跃位置start,并且具有正延迟和向后填充。

例如,使用 CSS 动画:

animation: moveRight 5s 1s steps(5, start);

在延迟阶段,输入进度值将为零,但如果设置了 前标志 表明动画尚未达到其动画区间, 则缓动函数将输出零作为其 输出进度值,即第一个阶跃的底部。

当动画区间刚刚开始时,输入进度值仍为零,但此时不会设置 前标志,因此缓动函数的结果将对应于第一个阶跃的顶部。

为了计算 输出进度值阶跃位置 start 被视为等同于 jump-start。 同样,end 被视为等同于 jump-end。 因此,以下算法不会明确提及 startend

注意:用户代理仍然必须区分 jump-startstart,以便进行序列化(参见 § 2.5 序列化)。

输出进度值 根据 输入进度值前标志 计算如下:

  1. 计算 当前阶跃floor(输入进度值 × 阶跃数)

  2. 如果 阶跃位置 属性是以下之一:

    当前阶跃 加 1。

  3. 如果 同时 满足以下两个条件:

    当前阶跃 减 1。

  4. 如果 输入 进度值 ≥ 0 且 当前阶跃 < 0, 令 当前阶跃 为零。

  5. 根据 阶跃位置 计算 跃升数,如下所示:

    jump-startjump-end

    阶跃数

    jump-none

    阶跃数 - 1

    jump-both

    阶跃数 + 1

  6. 如果 输入 进度值 ≤ 1 且 当前阶跃 > 跃升数,则将 当前阶跃 设为 跃升数

    此过程中第 4 步和第 6 步确保对于处于 [0, 1] 范围内的 输入进度值,阶跃缓动函数不会产生超出该范围的 输出进度值

    例如,尽管从数学上我们可能期望具有 阶跃位置jump-start 的阶跃缓动函数在 输入进度值 为 1 时跃升(即超出 1), 直观上, 当我们将此缓动函数应用于一个向前填充的动画时, 我们期望其输出为 1, 因为动画正在向前填充。

    对于一个阶跃位置为 阶跃位置jump-end 的阶跃缓动函数,在应用到延迟阶段的动画时,也会出现类似的情况。

  7. 输出进度值为 当前阶跃 / 跃升数

测试

2.5. 序列化

缓动函数按照 [CSSOM] 中定义的通用序列化模式进行序列化,并具有以下附加要求:

测试

隐私考虑

在本规范中尚未报告任何新的隐私问题。

本规范不会直接为 Web 平台引入任何新功能,而是提供其他规范可能引用的通用定义。

安全考虑

引用本规范中定义的功能的规范应考虑到,尽管缓动函数通常会接受范围为 [0,1] 的 输入进度值, 并生成范围为 [0,1] 的 输出进度值, 但情况并非总是如此。 缓动函数的应用应定义超出该范围的输入和输出的行为, 以确保它们不会引入新的安全问题。

3. 更改

3.1. 自第 1 级以来的新增内容

4. 致谢

本规范基于由 L. David Baron、Dean Jackson、David Hyatt 和 Chris Marrin 编辑的 CSS 过渡 规范。 编辑们还要感谢 Douglas Stockwell、Steve Block、Tab Atkins、Rachel Nabors、Martin Pitt,以及 Animation at Work slack 社区提供的反馈和贡献。

合规性

文档约定

合规性要求通过描述性断言和 RFC 2119 术语的组合来表达。术语 “MUST”(必须)、“MUST NOT”(不得)、“REQUIRED”(必需)、“SHALL”(应当)、“SHALL NOT”(不得)、“SHOULD”(应该)、“SHOULD NOT”(不应)、“RECOMMENDED”(推荐)、“MAY”(可以)和 “OPTIONAL”(可选)在本文件的规范部分中应按照 RFC 2119 的描述进行解释。 但是,为了可读性,这些词在本规范中并未全部以大写字母显示。

除明确标记为非规范性的部分、示例和注释外,本规范的所有文本都是规范性的。[RFC2119]

本规范中的示例以“例如”一词引出,或者通过class="example"与规范性文本区分开来,例如:

这是一个信息性示例。

信息性注释以“注”一词开头,并通过class="note"与规范性文本区分开来,例如:

注:这是一个信息性注释。

建议性内容是规范性章节,样式设计为引起特别注意,并通过<strong class="advisement">与其他规范性文本区分开来,例如:用户代理 MUST 提供可访问的替代方案。

测试

与本规范内容相关的测试可能会记录在类似此处的“测试”块中。任何此类块都是非规范性的。


合规性类别

本规范的合规性定义为三类合规性类别:

样式表
一个 CSS 样式表
渲染器
一个 UA,用于解释样式表的语义并渲染使用它们的文档。
创作工具
一个 UA,用于编写样式表。

如果样式表中的所有使用本模块中定义的语法的声明根据通用 CSS 语法和本模块中每个功能的独立语法都是有效的,则该样式表符合本规范。

如果渲染器除了按照相关规范解释样式表之外,还正确解析并渲染本规范定义的所有功能,则该渲染器符合本规范。然而,由于设备限制导致 UA 无法正确渲染文档,并不会使其不符合规范。(例如,UA 不要求在单色显示器上渲染颜色。)

如果创作工具编写的样式表在语法上符合通用 CSS 语法和本模块中每个功能的独立语法,并符合本模块中描述的样式表的所有其他合规要求,则该创作工具符合本规范。

部分实现

为了让作者能够利用前向兼容的解析规则分配回退值,CSS 渲染器必须将任何不支持的 at-rule、属性、属性值、关键字和其他语法结构视为无效,并忽略(适当时根据需要忽略)。特别是,用户代理不得选择性地忽略多值属性声明中的不支持的组件值并保留支持的值:如果任何值被视为无效(如不支持的值必须如此),则 CSS 要求整个声明被忽略。

不稳定和专有功能的实现

为了避免与未来稳定的 CSS 功能冲突,CSSWG 建议遵循最佳实践来实现 不稳定功能和 专有扩展

非实验性实现

一旦规范达到候选推荐标准(CR)阶段,非实验性实现就有可能实现,实施者应当发布任何 CR 级别功能的非前缀实现,并能够证明其符合规范。

为了建立和保持 CSS 在实现之间的互操作性,CSS 工作组要求非实验性 CSS 渲染器在发布任何 CSS 功能的非前缀实现之前向 W3C 提交一个实现报告(如果必要,还需提交用于该实现报告的测试用例)。提交给 W3C 的测试用例将由 CSS 工作组进行审核和修正。

有关提交测试用例和实现报告的更多信息,请访问 CSS 工作组的网站 https://www.w3.org/Style/CSS/Test/。相关问题应发送至 public-css-testsuite@w3.org 邮件列表。

索引

本规范定义的术语

引用定义的术语

参考文献

规范性引用

[CSS-VALUES-3]
Tab Atkins Jr.; Elika Etemad. CSS 值和单位模块第 3 级。 2024 年 3 月 22 日。CR。URL: https://www.w3.org/TR/css-values-3/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS 值和单位模块第 4 级。 2024 年 3 月 12 日。WD。URL: https://www.w3.org/TR/css-values-4/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准。现行标准。URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. 用于指示需求级别的关键字。1997 年 3 月。最佳当前实践。URL: https://datatracker.ietf.org/doc/html/rfc2119

参考性引用

[CSSOM]
Daniel Glazman; Emilio Cobos Álvarez. CSS 对象模型 (CSSOM)。2021 年 8 月 26 日。WD。URL: https://www.w3.org/TR/cssom-1/
[FUND-COMP-GRAPHICS]
Peter Shirley; Michael Ashikhmin; Steve Marschner. 计算机图形学基础。2009 年。
[WEB-ANIMATIONS]
Brian Birtles 等人。 Web 动画。2023 年 6 月 5 日。WD。URL: https://www.w3.org/TR/web-animations-1/