Web动画模块 第二级

W3C 工作草案,

关于本文档的更多信息
本版本:
https://www.w3.org/TR/2025/WD-web-animations-2-20251115/
最新发布版本:
https://www.w3.org/TR/web-animations-2/
编辑草稿:
https://drafts.csswg.org/web-animations-2/
历史记录:
https://www.w3.org/standards/history/web-animations-2/
反馈:
CSSWG 问题库
规范内反馈
编辑者:
特邀专家
Google
前任编辑者:
Google
Google
对本规范建议修改:
GitHub 编辑
差异规范:
参与进来:
通过 GitHub 修改文本
加入 ‘waapi’ 频道,位于 Animation at Work Slack
IRC:#webanimations(W3C IRC)
问题追踪:
GitHub

摘要

本规范定义了用于同步和控制网页展示变更的模型。 本规范还定义了与该模型交互的应用程序编程接口,并预期后续规范将定义以声明式方式暴露这些特性的手段。

CSS 是一种用于描述结构化文档(如 HTML 和 XML)如何在屏幕、纸张等媒介上呈现的语言。

本文档状态

本节描述了本文档在发布时的状态。 当前所有 W3C 出版物及本技术报告的最新修订版可在 W3C 标准与草案索引中找到。

本文档由 CSS 工作组 作为工作草案,采用推荐标准流程进行发布。 作为工作草案发布,并不代表 W3C 及其成员的认可以及支持。

这是一个草案文档,可能随时被更新、替换或废弃, 除非作为正在进行的工作,否则不应将本文件作为正式引用来源。

如需反馈, 请在 GitHub 提交 issue(推荐), 标题中请包含规范代码 “web-animations”,如下所示:“[web-animations] …评论摘要…”。 所有 issue 和评论都会存档。 另外,反馈也可发送至(存档) 公共邮件列表 www-style@w3.org

本文件适用于 2025 年 8 月 18 日 W3C 流程文件

本文档由遵循 W3C 专利政策 的团队编写。 W3C 维护着 公开的专利披露列表 (与本组交付物相关的专利披露会列在此页面); 此页面也包含专利披露说明。 如果个人知晓某个专利,并认为该专利包含 必要声明, 则必须按照 W3C 专利政策第6节进行披露。

1. 差异规范

这是一个差异规范,这意味着目前仅包含与 Web Animations Level 1 [WEB-ANIMATIONS-1] 的不同之处。当第一级规范接近完成时,将会把这里的新增内容与其合并,形成完整的第二级规范。

2. 时间模型

本节描述并定义了 Web Animations 时间模型的行为。

2.1. 时间模型概述

本节为非规范性内容

2.1.1. 分层结构

本级规范包含了更新后的时间层级结构示意图。

A hierarchy of timing nodes
时间节点的层级结构。树中的每个节点都从其父节点获得时间。

以及以下更新描述:

这种分层结构带来的结果是,复杂的动画可以作为一个整体被反转、调度、加速等,因为对父节点的操作会层叠到其后代上。此外,由于时间有共同的来源,动画的同步也变得很容易。

2.2. 时间线

新增:

时间线时长 指的是时间线可以为其当前时间生成的最大值。此值用于计算与该时间线关联的动画目标效果的固有迭代时长,当该效果的迭代时长为 "auto" 时。该值的计算方式是让效果填满可用时间。对于单调递增的时间线,当前时间没有上限,时间线时长为未定。对于非单调的(例如滚动)时间线,时长有固定的上限。在这种情况下,该时间线是基于进度的时间线,其 时间线时长 为 100%。

2.3. 动画帧

为算法第一步的说明更新,添加如下内容:

2.4. 动画

附加:

基于进度的时间线关联的动画效果需要将其时间属性转换为比例。将的过程如下:

  1. 如果迭代时长为 auto,执行以下步骤。

    • 起始延迟结束延迟设置为 0,因为不能混合时间与比例。

      注意: 未来版本可能允许这些属性赋值为百分比;届时,仅当其值用时间表示时才应忽略延迟,百分比则不受影响。

    否则:

    1. 总时长 等于结束时间

    2. 起始延迟设为 指定起始延迟 / 总时长 * 时间线时长 的结果。

    3. 迭代时长设为 指定迭代时长 / 总时长 * 时间线时长 的结果。

    4. 结束延迟设为 指定结束延迟 / 总时长 * 时间线时长 的结果。

规格化指定的计时的过程如下:

如果时间线时长已确定:

否则:
  1. 起始延迟 = 指定起始延迟

  2. 结束延迟 = 指定结束延迟

  3. 如果迭代时长为 auto:

    否则:

2.4.1. 设置动画的时间线

设定动画的时间线 animationnew timeline,可能为 null 的过程如下:

  1. old timelineanimation 当前的时间线(如果有)。

  2. 如果 new timelineold timeline 为同一对象,则终止此过程。

  3. previous play stateanimation播放状态

  4. previous current timeanimation当前时间

  5. 根据以下条件设置 previous progress

    如果 previous current time 未定:

    previous progress 设为未定。

    如果结束时间为0:

    previous progress设为0。

    否则

    previous progress = previous current time / 结束时间

  6. old timeline 非 null 且非单调递增时,from finite timeline 为 true。

  7. timeline 非 null 且非单调递增时,to finite timeline 为 true。

  8. animation时间线设为 new timeline

  9. 按下列首个匹配条件执行步骤:

    如果 to finite timeline
    1. animation应用所有待处理播放速率

    2. auto align start time 为 true。

    3. 起始时间设为未定。

    4. 保持时间设为未定。

    5. 如果previous play state为"finished"或"running"

      1. 安排一个待播放任务

    6. 如果previous play state为"paused"且previous progress已定:

      1. 保持时间设为 previous progress × 结束时间

    此步骤确保即使是处于待暂停状态且起始时间已定的动画,previous progress也能被保留。

    如果from finite timelineprevious progress已定,

    运行设置当前时间的过程,参数为previous progress × 结束时间

  10. 如果animation起始时间未定,将animation保持时间设为未定

    此步骤确保animation完成播放状态不会“粘住”,而是会根据其更新后的当前时间重新评估。

  11. did seek 标记为 false,synchronously notify 标记为 false,运行更新动画完成状态过程。

议题:如果new timeline为 null,需确保自定义效果被调用时传递未定迭代进度(除非同一脚本执行上下文中的后续更改使之冗余)。

2.4.2. 设置动画的目标效果

在重新安排待播放任务的步骤后,添加以下步骤:

  1. 如果new effect不为nullnew effect拥有父组,则new effect从其父组移除。

在将new effect赋为animation关联效果后,补充以下步骤:
  1. 如果old effect不为null,则将与包含后代相关的所有自定义效果,以未定迭代进度为参数排入队列调用。

    这并不完全正确。如果 old effect 已被关联到另一动画且在同一任务内,我们可能不应多做一次带未定值的回调。

    需要审查并很可能重写关于何时调用自定义效果的定义。

2.4.3. 等待关联效果

替换:

动画在以下两个条件均成立的最早时刻准备就绪:

修改为:

动画在以下所有条件均成立的最早时刻准备就绪

注意: 没有起始时间保持时间时动画并不准备就绪。对于滚动驱动动画,如果 auto align start time 为 true,则会在更新时间线时确定起始时间。

2.4.4. 校验 CSSNumberish 时间值

用于输入值 time校验 CSSNumberish 时间值的过程,基于首个匹配条件如下:

如果全部如下条件为真:

抛出 TypeError

返回 false;

如果全部如下条件为真:

抛出 TypeError

返回 false。

否则

返回 true。

2.4.5. 设置 Animation 的当前时间

动画的当前时间可以被设置为新值,从而定位(seek)动画。设置当前时间的流程分为两部分。

静默设置当前时间的过程如下,将动画 animation 的当前时间设为 seek time

  1. 如果 seek time未定时间值,则执行以下步骤。

    1. 如果当前时间已定,则抛出 TypeError

    2. 终止这些步骤。

  2. valid seek time 为以 seek time 作为输入,执行校验 CSSNumberish 时间值过程的结果。

  3. 如果 valid seek time 为 false,则终止此过程。

  4. 设置 auto align start time 为 false。

  5. 如下所示,更新 animation保持时间起始时间

    如有任一条件为真:

    1. animation保持时间设为seek time

    否则,

    animation起始时间设为以下计算结果:timeline time - (seek time / 播放速率),其中timeline timeanimation关联时间线的当前时间值

  6. 如果animation 没有关联的时间线,或关联的时间线不活动,将animation起始时间设为未定

    此举用于保证:当没有活动时间线时,只能设置起始时间或动画的当前时间其中之一。

  7. animationprevious current time设为未定

设置当前时间的过程如下,将动画 animation 的当前时间设为 seek time

  1. 执行静默设置当前时间过程,将 animation 的当前时间设置为 seek time

  2. 如果 animation 存在待暂停任务,则同步完成暂停操作,执行下列步骤:

    1. animation保持时间设为seek time

    2. animation应用所有待处理播放速率

    3. animation起始时间设为未定

    4. 取消待暂停任务

    5. 解决 animation当前 ready promise,传入 animation

  3. did seek 标志为 true,synchronously notify 标志为 false,执行更新动画完成状态过程。

2.4.6. 设置 Animation 的起始时间

animation的起始时间设置为 new start time设置起始时间过程如下:

  1. valid start time 为以 new start time 为输入,执行校验 CSSNumberish 时间值过程的结果。

  2. 如果 valid start time 为 false,则终止此过程。

  3. 设置 auto align start time 为 false。

  4. timeline timeanimation 关联时间线的当前时间值。如果animation 没有关联的 时间线 或时间线不活动(inactive),则设timeline time未定

  5. 如果 timeline time未定 并且 new start time已定,将 animation保持时间设为 未定

    此举用于保证:当没有活动时间线时,只能设置起始时间或动画的当前时间其一。

  6. previous current timeanimation当前时间

    注意: 这是上一过程变更后的当前时间,可能会导致当前时间变为未定

  7. animation应用所有待处理播放速率

  8. animation起始时间设为new start time

  9. 根据如下首个匹配条件,更新 animation保持时间

    如果new start time已定

    如果animation播放速率不为零,则将animation保持时间设为未定

    否则(new start time未定),
    1. animation保持时间设为previous current time,即使previous current time未定

  10. 如果animation待播放任务待暂停任务,则取消该任务并解决 animation当前 ready promise,传入 animation

  11. did seek 标志为 true,synchronously notify 标志为 false,执行更新动画完成状态过程。

2.4.7. 播放动画

播放动画 animation 的过程,以及传入的 auto-rewind 标志如下:

注意:auto-rewind 标志用于基于该模型但不要求回退行为的其他规范,如 CSS Animations [CSS-ANIMATIONS-1]

  1. aborted pause 为布尔 flag,当 animation待暂停任务为 true,否则为 false。

  2. has pending ready promise 为布尔标志,初始为 false。

  3. 如果 animation 关联的时间线单调递增,则 has finite timeline 为 true。

  4. previous current timeanimation当前时间

  5. 如果 auto-rewind 为 true 且 has finite timeline 为 false,则 enable seek 为 true,否则初始为 false。

  6. 执行下列首次匹配的条件:

    如果 animation有效播放速率 > 0,且 enable seek 为 true,且满足下列任一:

    animation保持时间设为 0。

    如果 animation有效播放速率 < 0,enable seek 为 true,且满足下列任一:

    关联效果结束为正无穷

    抛出 "InvalidStateError" DOMException 并终止本步骤。

    否则,

    animation保持时间设为 animation关联效果结束

    如果 animation有效播放速率 = 0 且 animation当前时间未定

    animation保持时间设为 0。

  7. 如果 has finite timeline,并且 previous current time 是未定:

    • auto align start time 标志设为 true。

    注意: 如果在样式更新期间调用 play(如 CSS 动画),则动画 起始时间在布局后才能可靠计算,因为起始时间需与动画区间的开始或结束对齐(依据播放速率)。此时动画称为具有自动对齐起始时间,即动画起始时间会自动调整,使动画进度对齐到动画区间。

  8. 如果 animation保持时间已定,则将其 起始时间设为未定

  9. 如果 animation 存在待播放任务待暂停任务

    1. 取消该任务。

    2. has pending ready promise 设为 true。

  10. 如果下列三条件成立:

    终止本过程。

  11. 如果 has pending ready promise 为 false,将 animation当前 ready promise 设为新的 promise,位于 animation相关 Realm

  12. 调度一个任务,当 animation 准备就绪 时执行。该任务将执行以下步骤:

    1. 断言 animation起始时间保持时间中至少一个为已定

    2. ready timeanimation 在变为 准备就绪时,关联时间线时间值

    3. 执行下列首个匹配条件:

      如果 animation保持时间已定
      1. animation应用所有待处理播放速率

      2. new start timeready time - 保持时间 / 播放速率。如果播放速率为 0,则new start time 直接为 ready time

      3. animation起始时间设为 new start time

      4. 如果 animation播放速率不为 0,则将保持时间设为未定

      如果 animation起始时间为已定,且 animation待设置播放速率
      1. current time to match = (ready time - 起始时间) × 播放速率

      2. animation应用所有待处理播放速率

      3. 如果 animation播放速率为 0,则将保持时间设为 current time to match

      4. new start time = ready time - current time to match / 播放速率。如果播放速率为 0,则new start timeready time

      5. animation起始时间设为 new start time

    4. 解决 animation当前 ready promise,传入 animation

    5. animationdid seek 标志为 false,synchronously notify 标志为 false,运行更新动画完成状态过程。

      注意以上两个步骤的顺序很重要,因为对于长度为零的关联效果的动画,其当前 ready promise会早于当前 finished promise被解决。

    只要上述任务被调度但尚未执行时,animation被描述为有待播放任务。而当该任务正在运行时,animation没有待播放任务

    如果用户代理确定 animation 立即准备就绪,则可以将上述任务调度为微任务,使其在下一个微任务检查点执行,但不得同步执行该任务。

    上述要求异步处理待播放任务,确保如下代码在各实现中行为一致:

    animation.play();
    animation.ready.then(
      () => { console.log('Playback commenced'); },
      () => { console.log('Playback was canceled'); }
    );
    // Suppose some condition requires playback to be canceled...
    animation.cancel();
    // "Playback was canceled" will be printed to the console.
    

    如上所示,如果待播放任务是同步执行,则当前 ready promise将不会被拒绝。

  13. did seek 标志为 false,synchronously notify 标志为 false,运行更新动画完成状态过程。

播放动画的过程需要包含调度用于更新自定义效果的任务。

2.4.7.1. 自动对齐起始时间

当附加到非单调时间线时,动画的起始时间可能依赖于布局。在这种情况下,我们会推迟起始时间的计算直到时间线在布局后更新。当更新时间线当前时间时,任意附加动画的起始时间会被有条件地更新。计算自动对齐起始时间的过程如下:

  1. 如果 auto-align start time 标志为 false,则终止本过程。

  2. 如果时间线不活动,则终止本过程。

  3. 如果播放状态为 idle,则终止本过程。

  4. 如果播放状态为 paused 且保持时间为已定,则终止本过程。

  5. start offset 为动画附加区间开始所对应的已定时间线时间。对于视图时间线(view timeline),需要基于覆盖区间比例进行计算。

  6. end offset 为动画附加区间结束所对应的已定时间线时间。对于视图时间线,需要基于覆盖区间比例进行计算。

  7. 如果有效播放速率 ≥ 0,则将起始时间设为 start offset,否则为 end offset

  8. 清除保持时间

2.4.8. 暂停动画

暂停动画的过程不仅需要涉及关联效果,还需要涉及关联效果的所有后代。

同样,暂停动画的过程也需要包含调度用于更新自定义效果的任务。

更新设置 seek time 的约束,使其只在使用单调时间线时适用。滚动驱动动画需要等到动画区间计算完毕后再设置保持时间。

替换:

  1. seek time时间值,初始为未定

  2. has finite timeline 为 true, 如果 animation 关联的时间线不是单调递增

  3. 如果 animation当前时间未定,则按下列首个匹配条件执行:

    如果 animation播放速率 ≥ 0,

    seek time 设为 0。

    否则,
    如果 关联效果结束为正无穷,

    抛出 "InvalidStateError" DOMException 并终止。

    否则,

    seek time 设为 animation关联效果结束

  4. 如果 seek time已定

    如果 has finite timeline 为 true,

    animation起始时间设为 seek time

    否则,

    animation保持时间设为 seek time

改为:

  1. has finite timeline 为 true, 如果 animation 关联的时间线不是单调递增

  2. 如果 animation当前时间未定has finite timeline 为 false,则按下列首个匹配条件执行:

    如果 animation播放速率 ≥ 0,

    保持时间设为0。

    否则,
    如果 关联效果结束为正无穷,

    抛出 "InvalidStateError" DOMException 并终止。

    否则,

    保持时间设为 animation关联效果结束

  3. 如果 has finite timeline 为 true,且 animation当前时间未定

    • auto align start time 标志设为 true。

替换:

调度一个任务,在同时满足以下两个条件的第一个时刻执行:

改为:

调度一个任务,在同时满足以下所有条件的第一个时刻执行:

注意:如果auto-align start time标志为 true,为了正确对齐动画区间,暂停待决动画仍然需要起始时间。起始时间需按自动对齐起始时间的过程设置。

2.4.9. 取消动画

补充最后一步:
  1. 将与animation关联效果所有包含后代相关的自定义效果排入队列调用,并传入未定的迭代进度。

    调用自定义效果的流程需要重构。目前可能针对可合并的变更调用了过多次。

2.4.10. 速度控制

补充如下句:

注意动画效果同样拥有播放速率,但其行为与此处定义有所不同。

2.4.11. 计算动画整体进度

动画的overallProgress为其当前时间关联效果结束的比值。

一个动画 animationoverallProgress的计算如下:

任一条件为真:

animationoverallProgress为 null。

如果 animation关联效果结束为0,
如果animation当前时间为负值,

animationoverallProgress为0。

否则,

animationoverallProgress为1。

如果animation关联效果结束为无穷大,

animationoverallProgress为0。

否则,
overallProgress = min(max(current time / animation关联效果结束, 0), 1)

2.5. 动画效果

加入如下内容:

动画关联效果称为直接关联到该动画。

动画效果可以通过分组效果组合为层级结构(见§ 2.10 分组与同步)。只有这种层级结构中的动画效果可以直接关联到动画。如果一个具有父组动画效果被指定为动画的关联效果,该动画效果会先从其父组移除,再与动画关联。

若一个动画效果关联于动画,则其为直接关联,或其拥有祖先分组效果,且该分组效果直接关联于动画。

2.5.1. 动画效果的类型

本规范定义了两种动画效果

2.5.2. 活动区间

在本级规范中,活动区间下界定义为:

活动区间的下界由动画效果起始时间决定,但可被动画效果上的起始延迟所偏移。

下图也应提及动画效果起始时间而非动画起始时间

最后,结束延迟的描述更新为:

结束延迟也可以指定,但主要在使用序列效果等实现动画串联时有用。

规范性描述更新如下:

活动区间的下界由动画效果的起始时间起始延迟共同决定。

动画效果起始时间父组(如有)调度动画效果开始的时刻,以继承时间表示。

大多数情况下,包括动画效果没有父组时,起始时间为0。唯一例外是序列效果会如§ 2.10.4.1 序列效果子项的起始时间描述为其子项设定起始时间。

起始时间外,动画效果还拥有起始延迟,即相对于起始时间的偏移。

与由父组确定的起始时间不同,起始延迟动画效果本身的属性。

动画效果活动区间下界(以继承时间空间表示)为其起始时间起始延迟之和。

这些定义被用于本地时间(参见§ 2.5.3 本地时间和继承时间)与活动时间的计算。

最后,结束时间定义如下:

动画效果结束时间为如下表达式的结果:max(起始时间 + 起始延迟 + 活动时长 + 结束延迟, 0)

2.5.3. 本地时间与继承时间

本节为新增内容。

在 Web Animations 中,所有时间都是相对于某个参考点的。这些不同的参考点构成了不同的时间空间

这可以类比于计算机图形学中的坐标空间。时间空间的零点类似于坐标空间的原点。

同坐标空间一样,时间空间也可以嵌套。分组效果通常会对其从父节点动画接收到的时间值做一定的变换,再将变换后的时间值传递给子元素。子动画效果随后在这种变换后的时间空间工作。

子对象会从父对象那里获取变换后的时间值,也就是继承时间,并加上自身的起始时间,来建立自己的本地时间空间,如下图所示。

Inherited time and local time.
继承时间与本地时间。
t 时刻,继承时间 为 2.5。
对于动画效果 (a),起始时间为 1,则本地时间为 1.5。
对于 (b) 动画效果,起始时间为 1,起始延迟为 1,本地时间仍为 1.5,因为本地时间仅基于动画效果起始时间而非起始延迟

对于动画效果,某一时刻的继承时间,根据以下条件首个匹配项确定:

如果动画效果父组

继承时间为父组的当前变换后时间

如果动画效果直接关联于动画

继承时间为动画的当前时间。

否则,

继承时间为未定

本地时间动画效果继承时间减去其起始时间。如果继承时间未定,则本地时间也为未定

2.5.4. 动画效果阶段与状态

本节为非规范性内容

关于播放中(in play)状态的非规范性描述如下:

动画效果及其所有祖先均处于活动阶段时发生。动画效果仅在播放中时才“运动”。

动画效果可能处于活动阶段,但未处于播放中。举例:若某动画效果父组导致当前效果的活动区间被裁剪,且父子均应用相同填充模式,则子动画效果可能即使在活动阶段内也被快照,实际已不再播放

Current 状态同理:

如果动画效果处于播放中前置阶段其某个祖先为真(例如因重复,因此该动画效果可能再次播放),则成立。

播放中的规范定义增添如下条件:

  1. 动画效果父组,且父组播放中,或该动画效果直接关联于未完成(not finished)的动画。

替换:

  1. 动画效果关联于动画且动画未完成。

Current 状态规范定义增加如下条件:

补充以下定义于动画效果阶段判断条件列表:

处于进度时间线边界

判断流程如下:

  1. 任一条件为真:

    返回 false

  2. effective start time动画起始时间,若未定则为零。

  3. 按下列首个匹配条件设 unlimited current time

    起始时间已定:

    (timeline time - 起始时间) × 播放速率

    否则,

    animation 的当前时间

  4. effective timeline timeunlimited current time / 动画播放速率 + effective start time

  5. effective timeline progresseffective timeline time / 时间线时长

  6. effective timeline progress 为 0 或 1,返回 true,否则 false。

若暂停动画期间明确设定动画的当前时间,上述流程不完全正确,可能导致动画当前时间与时间线当前时间出现偏差。

此流程还可简化,判断滚动边界时可忽略播放速率和起始时间。其目的是避免动画在滚动极限处变为非活动状态,无需对 ScrollTimeline 设置填充模式。直接检查时间线当前时间是否为0或时间线时长可能更合适。

替换:

动画效果本地时间不是未定,且满足以下任一条件,则处于前置阶段(before phase):

  1. 本地时间小于before-active 边界时间

  2. 动画方向为 "backwards" 且本地时间等于before-active 边界时间

改为:

动画效果本地时间不是未定,且满足以下任一条件,则处于前置阶段:

  1. 本地时间小于before-active 边界时间

  2. 动画方向为 "backwards" 且本地时间等于before-active 边界时间,且不 处于进度时间线边界

替换:

动画效果本地时间不是未定,且满足以下任一条件,则处于后置阶段(after phase):

  1. 本地时间大于active-after 边界时间

  2. 动画方向为 "forwards" 且本地时间等于active-after 边界时间

改为:

动画效果本地时间不是未定,且满足以下任一条件,则处于后置阶段:

  1. 本地时间大于active-after 边界时间

  2. 动画方向为 "forwards" 且本地时间等于active-after 边界时间且不 处于进度时间线边界

2.5.5. 填充模式

本节为非规范性内容

forwards 填充模式描述由:

当动画效果处于后置阶段时,……

更新为:

当动画效果处于后置阶段,或当动画效果处于活动阶段但其祖先处于后置阶段时,……

backwards 填充模式描述由:

当动画效果处于前置阶段时,……

更新为:

当动画效果处于前置阶段,或当动画效果处于活动阶段但其祖先处于前置阶段时,……

both 填充模式描述由:

当动画效果……

更新为:

当动画效果或其祖先……

(出现两次)。

当前若缓动函数生成结果超出 [0, 1],在分组效果下会出现意外行为——子项可能增加迭代次数或进入填充模式,而不是按照其本身的外推行为继续(如果缓动函数直接用于子项则会如此)。

为解决此问题,可能以后会引入“溢出”填充模式,以针对超出活动时间区间的时间值采用外推而非填充处理。

2013年东京F2F会议笔记第15节(Overflowing fill)

2.6. 重复

2.6.1. 迭代区间

补充:

单次迭代的长度称为迭代时长(iteration duration)。

新增:

动画效果的初始迭代时长即为它的固有迭代时长固有迭代时长的计算按以下首个匹配条件:

如果动画效果为分组效果

参见§ 2.10.3 分组效果的固有迭代时长

如果动画效果为序列效果

参见§ 2.10.4.2 序列效果的固有迭代时长

如果时间线时长未定,或迭代次数为 0,

返回 0

否则

返回 (100% - 起始延迟 - 结束延迟) / 迭代次数

注意: 目前起始和结束延迟均为零,直到支持百分比延迟。

动画效果迭代时长可由作者设置为除固有迭代时长以外的值。

2.6.2. 迭代时间空间

本节为非规范性内容

本节前几段替换为:

我们在描述本地时间继承时间时(见§ 2.5.3 本地时间和继承时间)已经遇到过不同的时间空间。重复又引入了另一种时间空间:迭代时间空间。

2.6.3. 区间计时

本节为非规范性内容

描述更新如下:

下方示例中,对于重复的效果,在本地时间为 1s 时,迭代时间为 0。对于序列效果,在继承时间为 1s 时,只有效果 B 处于播放中;两者区间边界都没有重叠。

下方图示同样更新:

Illustration of end-point exclusive timing.
端点排除型计时图示。对于重复与序列动画效果,区间边界处均无重叠。

2.7. 动画效果速度控制

(本节为新增)

动画类似,动画效果也有一个播放速率参数。动画效果播放速率是一个有限实数,在根据本地时间计算动画效果变换后时间时作为乘数。

动画效果设置播放速率,与为动画设置播放速率的效果不同。详细行为见§ 2.8 动画效果核心计算

本节为非规范性内容

总结如下,动画效果的播放速率行为:

2.8. 动画效果核心计算

2.8.1. 概述

本节为非规范性内容

描述由:

在 Web Animations 时间模型的核心,是把本地时间值转换为迭代进度的过程。

改为:

在 Web Animations 时间模型的核心,是把继承时间值转换为迭代进度的过程。

更新图示,展示动画效果播放速率:

Calculation of the active duration.
活动时长的计算,基于迭代时长乘以迭代次数,再除以播放速率

更新如下:

确定活动时长后,将本地时间转换为变换后进度迭代进度)的过程如下图所示。

改为:

确定活动时长后,将继承时间转换为变换后进度迭代进度)的过程如下图所示。

更新时间计算图:

An overview of timing model calculations.
计时模型计算流程概览。
(1) 将继承时间结合起始时间转换为本地时间
(2) 本地时间结合起始延迟转换为活动时间
(3) 活动时间除以迭代时长,结合迭代起始播放速率生成整体进度
(4) 整体进度被转换为单次迭代内的偏移量:简单迭代进度
(5) 简单迭代进度结合定向进度播放方向
(6) 最后,对定向进度应用计时函数,得到变换后进度

更新:

第一步,本地时间的计算见 Local time。

改为:

第一步,本地时间的计算见§ 2.5.3 本地时间与继承时间

2.8.2. 计算活动时长

更新为:

为计算活动时长,首先定义重复时长如下:

重复时长 = 迭代时长 × 迭代次数

如果迭代时长迭代次数为零,则重复时长为零。

此澄清是因为根据 IEEE 754-2008,无穷大与零相乘结果未定义。

活动时长按以下步骤计算:

  1. 如果播放速率为零,返回Infinity

  2. 否则,返回重复时长 / abs(播放速率)

2.8.3. 本地时间的变换

2.8.3.1. 计算活动时间

活动时间定义的说明中,加入父组

不过,只有当动画效果应产生输出时该值才有定义,因此取决于其填充模式和阶段,以及(如有)其父组的阶段:

在每个阶段的定义中补充如下步骤:

如果动画效果处于前置阶段

结果依下列首个匹配条件:

如其有父组且父组处于后置阶段

返回未定时间值

……

如果动画效果处于活动阶段

结果依下列首个匹配条件:

如其有父组且父组处于前置阶段,且当前效果的填充模式为 none 或 forwards,

返回未定时间值

如其有父组且父组处于后置阶段,且当前效果的填充模式为 none 或 backwards,

返回未定时间值

否则,

返回本地时间 - 起始延迟 的结果。

如果动画效果处于后置阶段

结果依下列首个匹配条件:

如其有父组且父组处于前置阶段

返回未定时间值

……

否则(本地时间未定),

返回未定时间值

2.8.3.2. 计算整体进度

整体进度定义中,更新initial progress的“否则”分支如下:

否则,

按下列首个匹配条件计算overall progress

如果播放速率小于零,

overall progress(活动时间 - 活动时长) × 播放速率 / 迭代时长

如果播放速率为零,

overall progress为零。

否则,

overall progress(活动时间 × 播放速率) / 迭代时长

2.9. 时间变换

目前,对时间函数分组效果上的使用并没有限制。 这让实现复杂度和与填充模式相关的行为复杂度都引发了一些担忧。 因此,在分组效果上允许全部时间函数被认为是风险项

可替代方案包括:只能在分组效果上使用线性时间函数, 或者只允许一组“简单”时间函数,这些函数在某些特性上能缓解复杂时间函数带来的部分问题。

参见 2013年8月讨论第2节

2.9.1. 计算变换后进度

将第二步替换为针对before flag的计算(以适应效果播放速率):

  1. 如果任一下列条件为真:current directionforwards,或播放速率 ≥ 0(但不是两者都为真时), 那么令going forwards为 true,否则为 false。

2.9.2. 计算变换后时间

(本节为新增。)

动画效果的变换后时间很简单,就是 变换后进度乘以 迭代时长

如果变换后进度未定,则变换后时间也为未定

如果变换后进度为0,且迭代时长为无穷大,则变换后时间为0。

2.10. 分组与同步

本节为非规范性内容

虽然可以分别设置动画效果的计时属性,但通常将多个动画效果同步,使它们共享公共计时属性并保持它们的时序关系会更有用。这通过分组效果实现。

下面是一个简单示例:

使用分组共享公共计时属性。
使用分组共享公共计时属性。
(a) 展示分别给各个动画设置5秒延迟。
(b) 通过给分组设置延迟,达到同样效果。

分组效果直接关联到动画时,相关联的动画效果可以作为一个整体 seek、暂停和停止。

分组效果是一种动画效果,包含若干个(零个或多个)有序的动画效果,称为子效果

某一时刻,一个动画效果至多只能是一个分组效果子效果,该分组效果称为父组父组不能与动画效果自身相同。

通过嵌套分组效果可以构建树状结构。用于描述此类结构组成与属性的术语如下,并见 [DOM] 的定义:

注意: 在应用这些定义到动画效果时,父节点专指父组包括动画,即使后者实际上是动画效果的时间源。

子效果与其父组之间的时序关系体现在继承时间的定义中(参见§ 2.5.3 本地时间与继承时间)。

2.10.1. 分组时间与子项时间的关系

本节为非规范性内容

分组效果的子项的计时基于分组的计时。具体来说,子项的时间基于父分组的变换后时间。对于重复来说,这意味着子项父分组的每一次迭代内执行。

例如,若分组效果迭代次数为2,则该组的所有子项会播放两次,因为它们有效地分组的每轮迭代内被播放。

对分组内子项的多次迭代影响。
因为分组效果的子项时间基于分组的变换后时间,所以当分组重复时,子项也会再次播放。

注意,即便如此,每个子动画效果只有一个活动区间,但是由于父分组的计时安排,该活动区间会被播放两次。

如果对子项和分组本身同时设置迭代次数,等同于将分组的迭代次数与子项的迭代次数相乘。

迭代次数的乘法关系。
分组效果上指定 2 次迭代,在某个子项上指定 3 次迭代,则该子项将播放6次。

由于分组效果的子项的计时基于分组的变换后时间,所以它们无法超出分组的活动区间进行动画。 这是因为分组的变换后时间在其活动区间之外不会变化,因此分组合可以裁剪其子项的播放。

分组裁剪子项活动区间。
首先,某动画效果有负延迟和无限迭代次数
但当类似的动画效果被放入一个指定了迭代时长分组效果时,会导致子动画效果活动区间被裁剪。

此外,由于分组效果的子项计时基于父组的变换后时间,带来了进一步影响:

2.10.2. 分组效果子项的起始时间

分组效果子效果起始时间为0。

注意,某些特定类型的分组效果可以重写此定义,实现其它类型的同步行为。

2.10.3. 分组效果的固有迭代时长

分组效果固有迭代时长等于该分组内最后一个子效果完成其活动区间的时间,具体取决于子项数量,规则如下:

如果分组没有子效果

固有迭代时长为0。

否则,
  1. maximum end time为分组内各子效果结束时间最大值。结束时间可以是时间类型或百分比(针对基于进度时间线)。如类型混用,则以最大时间型为100%用于缩放。

  2. 固有迭代时长为max(0, maximum end time)

某些特定类型的分组效果可以重写上述固有迭代时长的定义。

2.10.4. 序列效果

本节为非规范性内容

某些特定类型的分组效果可用于对子项实现不同的同步行为。本规范定义了一种额外类型的分组效果序列效果序列效果将其子项的起始时间顺序排列,使其逐一依次执行。

对比如下两种安排:

分组效果与序列效果。
分组效果与序列效果。
(a) 是常规分组效果,所有子项同时执行。
(b) 是序列效果,子项依次执行。

由于分组效果还可以嵌套其他分组效果,下图展示了组合不同类型分组以实现复杂同步的可能性:

分组效果的嵌套。
一个包含普通分组效果序列效果
分组效果会等待序列中的前一个子项结束,才与该分组的所有子项一起同时播放。播放完后,序列效果的下一个子项才会播放。

序列效果分组效果的一个类型,它会安排其子效果按顺序依次执行。这种序列方式是通过调整组内每个子项的起始时间实现的。

2.10.4.1. 序列特效子项的开始时间

子特效开始时间(属于序列特效)是该子项前一个兄弟节点结束时间。 如果子项没有前一个兄弟节点,那么开始时间为零。

活跃时长为正无穷大时, 计算结束时间和随后子项的开始时间的行为遵循IEEE 754-2008规定的常规处理方式。 因此,如果序列特效的某个子项具有无限的活跃时长,则序列中后续的子项将无法播放。

同样,上述定义并未限制开始时间为正值,因此,由于分组中较早子项具有负的启动延迟,导致其活跃区间可能在分组的开始时间前结束,某些子项可能不会被播放。

本节为非规范性内容

因为活跃区间的起点是基于动画特效开始时间 加上 启动延迟, 所以序列特效子项的活跃区间未必严格按顺序顺次运行,可以通过启动延迟前后移动,如下图所示。

使用负的启动延迟使序列组的子项重叠
展示了对子项使用启动延迟的例子, 序列特效可以通过调整启动延迟让子项分别重叠(负延迟)或间隔(正延迟)。

可以通过负的启动延迟 让两个子项的活跃区间发生重叠。 注意,启动延迟会影响分组中后续子项的开始时间

2.10.4.2. 序列特效的固有迭代时长

序列特效固有迭代时长等价于假定 在组的子项末尾追加一个子特效后,按§ 2.10.4.1 序列特效子项的开始时间的定义计算得到的开始时间,如果该值为负,则固有迭代时长为零。

因此,如果序列特效没有任何子特效,其固有迭代时长为零。

3. 动画模型

3.1. 动画类型

3.1.1. 不可动画化

将针对不可动画属性的特效的描述更新为:

针对不可动画属性的 动画特效,依然会表现出 动画特效的常规行为,例如在序列特效中占用时间, 以及延后动画current finished promise的实现时机。

3.2. 关键帧特效

3.2.1. 关键帧特效的效果值

针对由关键帧特效作为 目标属性之一引用的某个属性, 在指定iteration progresscurrent iterationunderlying value下, 用于计算效果值的过程, 需在应用关键帧特效合成模式步骤后插入以下步骤。

  1. interval endpoints中的每个keyframe

    1. (同 web-animations-1)

    2. 如果此关键帧特效迭代合成操作累加, 则按以下步骤执行current iteration次:

      • property-specific keyframes中最终关键帧上的属性值(Va)和当前keyframe上的属性值(Vb), 按目标属性的累加过程, 将keyframe上的属性值替换为二者累加的结果。

      注:这里参数顺序很重要。当目标属性的动画类型没有定义累加或相加过程时,这些过程的默认定义会返回Vb。 在对不支持累加的属性进行迭代合成时,结果应为keyframe上目标属性的初始值, 因此上述步骤中将其设置为Vb

3.3. 效果合成

3.3.1. 效果栈

为效果排序过程追加以下步骤:

  1. 按照树顺序AB排序。 (此时,AB必须拥有相同的动画,否则顺序会在上一步中被解决。)

排序动画特效时所用的 “动画特效的关联动画” 定义需要根据本级规范引入的与动画关联的定义进行修订,以正确处理群组特效。

3.4. 效果累加

类似于效果值之间的合成 (参见 Web Animations § 5.4.4 效果合成), 迭代合成操作 决定了同一个关键帧特效连续多次迭代时如何合成属性值。

本规范定义了如下两种迭代合成操作

replace

每次迭代独立计算,不依赖于前次迭代的结果。

accumulate

动画的连续多次迭代,将与上一次迭代的最终值累加

迭代合成操作的应用已被整合进效果值的计算中, 可参见§ 3.2.1 关键帧特效的效果值

3.5. 自定义特效

(本节新增。)

该功能需要重新讨论。目前的想法是,与其单独设置自定义特效,不如在每个动画特效上直接支持onupdate回调即可。 这样,比如要给已有的特效添加日志、或在特定时刻触发额外行为等,就不需要单独加一个父分组,仅用回调即可实现。

本节为非规范性内容

在某些场景下,Web Animations原生提供的动画特效可能无法满足需求。 比如,此处定义的动画特效只能作用于特定CSS属性, 无法用来平滑缩放SVG viewport的currentScale 属性而不影响文档内容。

如果内置动画特效无法满足功能需求,可以通过脚本自定义特效。 这样定义的自定义特效会从计时模型接收迭代进度当前迭代,并负责根据指定时刻产生对应的效果。

通过脚本定义的特效不仅能动画化原本不能动画的属性,还可以动画化任何脚本可访问目标,包括生成音频或控制震动等。

例如,利用自定义特效 绘制到canvas元素, 就可以实现复杂的动画,比如用CSS或SVG难以创建的图案。 与脚本动画定时控制技术相比, 这种方式保证了动画不会受帧率影响,可暂停、反向、支持缓动效果、加速、与其他动画同步,并且可像其它Web Animations动画一样受控,无需额外编程。

自定义特效 是开发者自定义的回调,会在更新动画并发送事件流程中被传递计时信息调用。

需要在计时属性更新时也调用回调,对吗?

3.5.1. 自定义特效的取样

每次执行更新动画并发送事件流程(下称更新)时, 会根据以下方式为每个引用自定义特效动画特效调用回调。

  1. 如果在上一次更新中,引用自定义特效的动画特效:

    未定迭代进度 及上一次更新的目标元素两项作为参数调用回调。

  2. 根据以下第一条命中条件,为当前目标元素调用回调:

    如果引用自定义特效的动画特效当前不处于激活,但上一次更新时激活

    未定迭代进度 和当前目标元素作为参数调用回调。

    否则,如果引用自定义特效的动画特效

    用当前动画特效迭代进度目标元素作为参数调用回调。

可能有需求需要在动画树的特定时间点触发动作。 在许多场景下,可以通过插入一步进缓动的自定义特效覆盖所需期间来实现; 但这也可能对内容布局提出不便于适配的额外要求。

待讨论的其他备选方案:

  • 可以为零宽度的自定义特效定义额外的回调调用条件。例如可要求,若在上一次和当前更新时间之间(假定无限精度)恰好有某时刻落在自定义特效区间内,则必须调用回调。

  • 也可以不为自定义特效增加特殊调用逻辑,而是另行引入新的动画特效类型:—如Trigger,该类型只作为零宽度自定义特效生效,其构造函数仅需回调,不要求目标或计时参数。也可以指定其它调用方式,比如限定仅在某种播放方向下触发等。

3.5.2. 自定义特效的执行顺序

由于自定义特效不像动画特效那样仅限于单一的目标属性,因此评估它们的执行顺序的步骤与动画特效不同。

自定义特效会在所有 动画特效都已完成并将其结果应用到对应目标之后执行(参见“应用合成结果”)。

需要更精确地定义此处。 样式是否已刷新? 推测应当如此。 能否在执行基于脚本的动画特效的期间暂停回流,待所有执行后再进行一次回流?

在所有自定义特效之间, 执行顺序与动画特效在[[#the-effect-stack]]中定义的排序顺序相同, 排序靠前的会优先执行,排序靠后的会后执行。

3.6. 动画触发器

3.6.1. 概述

动画触发器用于控制其关联的 动画在基于时间的动画中的播放。 与动画类似,动画触发器会与时间轴相关联,并附加到一个附着区间上。

触发器是否应该对滚动驱动动画产生影响?

3.6.2. 动画触发器内部状态

动画触发器 trigger 具有一个内部布尔型did trigger标志,初始为false, 以及一个内部state,可取如下值:

idle
animation关联的动画特效保持在其开始前阶段,并将当前时间固定为零。
primary
切换到此值时,由type指定的primary行为类型会应用到animation
inverse
切换到此值时,由type指定的inverse行为类型会应用到animation

statedid trigger的值会在下面描述的更新动画触发器状态步骤中被更新。

idle 状态是否需要在规范中作出正式决议?

3.6.3. 动画触发器行为

动画触发器 具有一个关联的动画触发器行为, 该行为结合did triggerstate, 决定该触发器对其关联动画播放的影响。

动画触发器关联的动画特效idle状态下, 会始终处于其开始前阶段,并且当前时间保持为零, 无论触发器的did trigger标志或state为何。 其他情况下,不同行为behavior的播放效果如下:

once
如果stateprimary

则其效果是触发关联动画。

否则,

触发器不产生任何效果。

repeat
如果stateprimary

则其效果是触发关联动画。

如果stateinverse

其效果是将关联的动画特效重置为开始前阶段,并将动画开始时间置零。

alternate
如果stateprimarydid trigger为false,

则其效果是触发关联动画。

如果stateprimarydid trigger为true,

则其效果是反转关联动画。

如果stateinverse

则其效果是反转关联动画。

state
如果stateprimary

则其效果是触发或恢复关联动画。

如果stateinverse

则其效果是暂停关联动画。

“primary/inverse” 行为类型是否需要详细定义?

3.6.4. 动画触发器激活区间

每个动画触发器都定义了一个激活区间 ,且仅有一个。该区间为时间轴进度上state设置为primary期间的片段。

3.6.5. 动画触发器区间

一个动画触发器包含两个区间, 即默认区间退出区间退出区间用于替换默认区间并扩展激活区间

是否应规定退出区间的边界只能大于等于默认区间边界?

根据其最新state state的值, 激活区间定义如下:

如果stateprimary

interval被设为退出区间

否则,

interval默认区间

3.6.6. 设置动画的触发器

为动画设置触发器 animation的新trigger的过程如下:
  1. old triggeranimation当前的动画触发器(如有)。

  2. 如果new triggerold trigger为同一对象,则终止本过程。

  3. animationtriggernew trigger

  4. new trigger运行更新动画触发器状态过程。

3.6.7. 设置动画触发器的时间轴

为动画触发器设置时间轴 trigger的新timeline(可为null)过程如下:
  1. old timelinetrigger当前的timeline(如有)。

  2. 如果new timelineold timeline为同一对象,则终止本过程。

  3. triggertimeline 赋值为new timeline

  4. trigger运行更新动画触发器状态过程。

3.6.8. 更新动画触发器状态

更新动画触发器状态动画触发器trigger的过程如下:
  1. did triggertrigger当前did trigger标志的值。

  2. behaviortrigger关联的动画触发器行为

  3. 如下设置trigger.state

    如果trigger本地时间未定

    则终止本过程。

    否则,
    如果behavioroncedid trigger为true,

    则终止本过程。

    否则,
    如果trigger处于其激活区间内,
    1. trigger.state设为primary

    2. trigger.did trigger设为true。

    否则,
    如果did trigger为true,

    trigger.state设为inverse

当时间轴再次变为闲置时,"did trigger" 标志是否应重置为false才是正确/期望的行为?

4. 编程接口

4.1. AnimationTimeline 接口

[Exposed=Window]
partial interface AnimationTimeline {
    readonly attribute CSSNumberish? currentTime;
    readonly attribute CSSNumberish? duration;
    Animation play (optional AnimationEffect? effect = null);
};

更新 currentTime 属性类型。

currentTime, 类型为 CSSNumberish,只读,可为 null

返回该时间轴的当前时间,如果该时间轴 未激活则返回 null。对于基于进度的时间轴,该值表示为百分比;其他情况下为毫秒单位的 double 类型值。

duration, 类型为 CSSNumberish,只读,可为 null

返回该时间轴的时长

Animation play(optional AnimationEffect? effect = null)

创建一个新的 Animation 对象,关联到此时间轴,并在其 就绪时立即开始播放。

若指定了 effect,则作为动画的 关联特效

有建议将本方法重命名或移除 (见 TAG 意见)。

需定义 effect 为 null 时的启动行为。

新的 Animation 对象由 Animation() 构造函数创建,参数包含本 AnimationTimeline 对象(作为 timeline)和 effect(作为 effect 参数)。

在构造完 Animation 对象后,以 auto-rewind 标志为 true 对新对象执行 播放动画 的过程。

effect

用于分配给新创建的 Animation 对象的关联特效

4.2. Animation 接口

更新 Animation 接口的 startTime 和 currentTime,增加 rangeStart、rangeEnd 和 overallProgress,如下所示:

[Exposed=Window]
partial interface Animation {
    attribute CSSNumberish?       startTime;
    attribute CSSNumberish?       currentTime;
    attribute AnimationTrigger?   trigger;
    attribute (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeStart;
    attribute (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeEnd;
    readonly attribute double? overallProgress;
};

增加或更新如下属性描述:

startTime, 类型为 CSSNumberish,可为 null

按如下方式更新描述:

该动画的起始时间。若与基于进度的时间轴关联,则start time返回类型必须为 CSSNumericValue 且单位为百分比。否则 start time 返回毫秒单位的 double 值。 设置该属性时,会按照设置起始时间 的过程将此对象的起始时间变为新值。

currentTime, 类型为 CSSNumberish,可为 null

按如下方式更新描述:

该动画的当前时间。若与基于进度的时间轴关联,则current time返回类型为 CSSNumericValue 百分比单位。 否则 current time 必须为毫秒单位的 double 类型值。 设置该属性时将按照设置当前时间的流程,将当前时间更新为新值。

新增:

rangeStart, 类型为 (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString)

指定该动画动画附着区间的起点。 设置该属性遵循 KeyframeAnimationOption 的 rangeStart 的同样规则。 读取该属性时返回值为 TimelineRangeOffsetDOMString "normal"。

rangeEnd, 类型为 (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString)

指定该动画动画附着区间的终点。 设置该属性遵循 KeyframeAnimationOption 的 rangeEnd 的同样规则。 读取该属性时,返回值为 TimelineRangeOffsetDOMString "normal"。

overallProgress, 类型为 double,只读,可为 null

指定overallProgress,即该动画相对于其关联特效终点的进度比例。

trigger, 类型为 AnimationTrigger,可为 null

指定该动画关联动画触发器。 设置该属性规则同 KeyframeAnimationOption 的 trigger

4.3. AnimationEffect 接口

[Exposed=Window]
partial interface AnimationEffect {
    // Timing hierarchy
    readonly attribute GroupEffect?     parent;
    readonly attribute AnimationEffect? previousSibling;
    readonly attribute AnimationEffect? nextSibling;

    undefined before (AnimationEffect... effects);
    undefined after (AnimationEffect... effects);
    undefined replace (AnimationEffect... effects);
    undefined remove ();
};
getComputedTiming()

对象的 duration 属性描述需说明:如果 timing.duration 是字符串 auto,该属性会返回 当前计算出的固有迭代时长, 可能表示为毫秒的 double,或若特效关联到基于进度的时间轴,则为百分比。

parent, 类型为 GroupEffect,只读,可为 null

父组(parent group),或者 null 若此 动画特效父组

是否应命名为parentGroup

previousSibling, 类型为 AnimationEffect,只读,可为 null

前一个兄弟节点

nextSibling, 类型为 AnimationEffect,只读,可为 null

下一个兄弟节点

undefined before (AnimationEffect... effects)

在本动画特效之前插入 effects

  1. 如果没有父组,则终止步骤。

  2. 如果 effects 中任一动画特效是本节点的 自身或祖先抛出 HierarchyRequestError 异常并终止。

  3. effects 插入到 本动画特效之前。

注意:由于该定义,下述用法不可行,因为 effect 同时是自身或祖先:
effect.before(effect); // 抛出 HierarchyRequestError
undefined after(AnimationEffect... effects)

在本动画特效之后插入 effects

  1. 如果没有父组,则终止步骤。

  2. 如果 effects 中任一动画特效是本节点的 自身或祖先抛出 HierarchyRequestError 异常并终止。

  3. reference child不在 effects 内的下一个兄弟节点

  4. effects 插入到 reference child 之前。

undefined replace(AnimationEffect... effects)

将本 AnimationEffect 替换为传入的 effects

  1. 如果没有父组,则终止步骤。

  2. 如果 effects 中任一动画特效是父组自身或祖先抛出 HierarchyRequestError 异常并终止。

  3. reference child不在 effects 内的下一个兄弟节点

  4. 父组中移除本动画特效。

  5. effects 插入到 reference child 之前。

undefined remove()

移除当前动画特效从其父组动画中。

remove() 方法既可将特效从父组移除,也可从动画移除。我们是否应在第1级中保留该方法,并只定义为从动画对象移除?

4.4. EffectTimingOptionalEffectTiming 字典

partial dictionary EffectTiming {
    double delay;
    double endDelay;
    double playbackRate = 1.0;
    (unrestricted double or CSSNumericValue or DOMString) duration = "auto";
};

partial dictionary OptionalEffectTiming {
    double playbackRate;
};

注意: 本规范当前版本中,duration 暂不支持设置为 CSSNumericValue;但通过 getComputedTiming() 解析 duration 时,返回值可能为 CSSNumericValue。 未来版本有望允许将 duration 设置为 CSSNumeric 类型,需为有效时间单位或百分比。

delay, 类型为 double

将描述更新为:

指定启动延迟,表示从动画特效起始时间活跃区间起点的毫秒数。指定启动延迟会通过规范化指定计时过程转换为启动延迟

endDelay, 类型为 double

将描述更新为:

指定结束延迟,即从动画特效活跃区间结束到后续(如序列组中另一个序列特效动画特效起始时间的毫秒数。指定结束延迟会通过规范化指定计时过程转换为结束延迟

duration, 类型为 (unrestricted double or CSSNumericValue or DOMString),默认值为 "auto"

将描述更新为:

指定迭代时长,即大于或等于零的实数(包括正无穷),表示单次动画特效迭代所需的时间(毫秒),或字符串 auto,表示迭代时长以特效的固有迭代时长为准。指定迭代时长会通过规范化指定计时过程转换为迭代时长

playbackRate, 类型为 double,默认值为 1.0

动画特效播放速率 属性,为本地时间的倍数,可能导致特效以非自然速度运行。

4.5. 更新 AnimationEffect 的计时

替换:

input存在的每个成员赋值给 effect 的对应计时属性如下:

为:

input存在的每个成员赋值给 effect 的对应计时属性如下:

增加:

规范化指定计时流程操作。

样式变更也可能导致计时属性被更新。任何影响动画计时的 CSS 动画属性变化,都需要重新执行规范化指定计时流程。

4.6. ComputedEffectTiming 字典

partial dictionary ComputedEffectTiming {
    CSSNumberish         startTime;
    CSSNumberish         endTime;
    CSSNumberish         activeDuration;
    CSSNumberish?        localTime;
};
startTime, 类型为 CSSNumberish

动画特效起始时间。若特效关联的是基于进度的时间轴,则以百分比表示,否则为毫秒的 double 类型。

该时间是父组(如果有)在其变换时间空间内为该子项调度的时间,即继承时间空间

活跃区间的起点等于起始时间与启动延迟之和。

endTime, 类型为 CSSNumberish

将描述更新为:

动画特效继承时间空间下的结束时间。如果关联的是基于进度的时间轴,则该值以百分比表示,否则为毫秒单位的 double。 该时间为动画特效活跃区间的结束点加上结束延迟

activeDuration, 类型为 CSSNumberish

将描述更新为:

动画特效活跃时长。如关联到基于进度的时间轴,则为百分比;否则为毫秒单位的 double。

localTime, 类型为 CSSNumberish,可为 null

将第二段描述更新为:

如果该动画特效关联到动画,或其父组激活,则返回 null

补充:

如关联到基于进度的时间轴,则该值为百分比,否则为毫秒单位的 double 值。

4.6.1. FillMode 枚举

enum FillMode { "none", "forwards", "backwards", "both", "auto" };
auto

将描述更新为:

应用于 GroupEffect 时前后都填充,应用于 KeyframeEffect 时不填充。

4.7. GroupEffect 接口

(本节新增。)

组特效GroupEffect 接口表示。

[Exposed=Window]
interface GroupEffect {
  constructor(sequence<AnimationEffect>? children,
              optional (unrestricted double or EffectTiming) timing = {});

  readonly attribute AnimationNodeList  children;
  readonly attribute AnimationEffect?   firstChild;
  readonly attribute AnimationEffect?   lastChild;
  GroupEffect clone ();

  undefined prepend (AnimationEffect... effects);
  undefined append (AnimationEffect... effects);
};
GroupEffect ()

按照如下过程创建一个新的 GroupEffect 对象:

  1. 创建一个新的 GroupEffect 对象 group

  2. timing input 为对 timing 执行处理 timing 参数的结果。

  3. timing input 为如下第一个符合条件的结果。

    如果 options 是一个 EffectTiming 对象,

    timing inputoptions

    否则(如果 optionsdouble),

    timing input 为 新建的 EffectTiming ,其所有成员为默认值,durationoptions

  4. 按照 更新动画特效计时属性 的流程用 timing input 更新 group 的计时属性。

    如果过程中抛出异常,则传播该异常并中止本流程。

  5. 插入 childrennull 之前。

children

要作为本组子项添加的一系列动画特效。

这些子项会按照 append() 方法的语义依次插入到 GroupEffect 接口中。

timing

该新组特效的计时属性或迭代时长

children, 类型为 AnimationNodeList,只读

本组中子特效的列表。

firstChild, 类型为 AnimationEffect,只读,可为 null

组特效第一个子项

lastChild, 类型为 AnimationEffect,只读,可为 null

组特效最后一个子项

undefined prepend (AnimationEffect... effects)
  1. 如果 effects 中任一动画特效是本动画特效自身或祖先抛出 HierarchyRequestError 异常并终止。

  2. 插入 effects第一个子项之前。

undefined append (AnimationEffect... effects)
  1. 如果 effects 中任一动画特效是本动画特效自身或祖先抛出 HierarchyRequestError 异常并终止。

  2. 插入 effectsnull之前。

GroupEffect clone ()

按如下过程深拷贝本 GroupEffect 对象:

  1. source 为当前 GroupEffect 对象,即需克隆的对象。

  2. cloned timing 为新建的 EffectTiming ,其每个成员都赋值为 source.getTiming() 中同名属性的值。

  3. cloned children 为空序列 AnimationEffect 对象。

  4. source.children 中的每个 child,调用 child.clone() 并添加到 cloned children

  5. 返回通过调用 GroupEffect() 构造的新GroupEffect ,参数为 GroupEffect(cloned children, cloned timing)

4.7.1. 处理 timing 参数

传递给 GroupEffect()SequenceEffect() 构造函数的 timing 参数可以是一个 EffectTiming 对象、表示 动画特效时长(毫秒)的 double 或 undefined。

以下流程处理 timing 参数 ,用于将上述输入归一化为 EffectTiming 对象。

如果 timingEffectTiming 对象,

直接返回 timing

如果 timingdouble

返回新建的 EffectTiming ,其所有成员为默认值,durationtiming

否则(timing 未定义),

返回新建的 EffectTiming ,其所有成员为默认值。

4.7.2. 层级操作定义

用于确定动画特效 effect 在一组动画特效 effects 中的 下一个未包含在该组内的兄弟节点 的步骤如下:

  1. context effecteffect

  2. context effect下一个兄弟节点null 时,执行下列步骤:

    1. context effect 变为其下一个兄弟节点

    2. 如果 context effect 不在 effects 中,返回 context effect 并中止。

  3. 返回 null

移除一个 effect 从其父组动画 ,按照首先匹配到的条件执行如下操作(如有):

如果 effect父组

effect 从该父组子特效列表中移除。

如果 effect 直接关联到动画,

effect动画中解除关联。

插入一系列 effectsparent子特效列表的 reference child 之前,对 effects 里每一个 effect 依次按以下步骤操作:

  1. 移除 effect 的父节点。

  2. effect 插入到 parent子特效列表的 reference child 之前。

4.8. AnimationNodeList 接口

一组动画特效可以用 AnimationNodeList 表示。

AnimationNodeList 接口支持索引属性,索引范围为 0 ≤ index < length

此接口存在的唯一原因,是为了让熟悉 DOM 接口(通过 children 成员访问子节点)的作者获得熟悉的体验。

[Exposed=Window]
interface AnimationNodeList {
    readonly attribute unsigned long length;
    getter AnimationEffect? item (unsigned long index);
};
length, 类型为 unsigned long,只读

列表中动画特效的数量。

getter AnimationEffect? item(unsigned long index)

返回 index 位置的动画特效。 如果 index 大于等于 length,则返回 null

4.9. SequenceEffect 接口

序列特效SequenceEffect 接口表示。

[Exposed=Window]
interface SequenceEffect : GroupEffect {
  constructor(sequence<AnimationEffect>? children,
              optional (unrestricted double or EffectTiming) timing = {});

  SequenceEffect clone ();
};
constructor (sequence<AnimationEffect>? children, optional (unrestricted double or EffectTiming) timing)

该构造函数中的每个参数的意义和处理方式,与 GroupEffect() 构造函数完全相同。

SequenceEffect clone ()

用与 clone() 方法相同的流程,对此 SequenceEffect 对象进行深拷贝;唯一不同点是会新建一个 SequenceEffect 对象。

4.10. KeyframeEffect 接口

KeyframeEffect 接口修改如下:

partial interface KeyframeEffect {
    attribute IterationCompositeOperation    iterationComposite;
};
KeyframeEffect (target, keyframes, options)

将创建新 KeyframeEffect 对象的流程第5步修改为如下:

  1. 如果 optionsKeyframeEffectOptions 对象, 则将 iterationCompositecomposite 属性分配给 effect 对应成员,赋值时如有异常应依 KeyframeEffect 的 setter 定义处理,并导致整个流程中止并抛出异常。

KeyframeEffect (source)

补充:用与原流程一致的方法创建新 KeyframeEffect 对象,且将 source迭代合成操作 赋值给 effect

iterationComposite, 类型为 IterationCompositeOperation

迭代合成操作属性由 关键帧特效选用 IterationCompositeOperation 枚举值指定。

设定时,会把本迭代合成操作属性赋值为参数值。

4.10.1. 创建新的 KeyframeEffect 对象

本节为非规范性内容

替换:

如未指定时长,则默认值为零。

为:

如未指定时长,则使用固有迭代时长

补充:

此点在与其他动画特效联用(如下例)时尤其实用。例如,可通过如下方式先淡出元素再切换 visibility 为 ‘hidden’:

new SequenceEffect(
  [
    new KeyframeEffect(elem, { opacity: 0 }, 1000),
    new KeyframeEffect(elem, { visibility: 'hidden' }, { fill: 'forwards' })
  ]
);

4.10.2. *Keyframe 字典的修改

ComputedKeyframeBaseComputedKeyframeBaseKeyframeBasePropertyIndexedKeyframe 等 字典中 offset 键的 double? 类型被替换为 (CSSNumberish? or TimelineRangeOffset or DOMString), 其中 double 依然表示百分比,DOMString 将按照 <keyframe-selector> 语法解析为 TimelineRangeOffset, 得到的 TimelineRangeOffset或者 CSSNumericValue@keyframes 语义解释。

如果 DOMString 不能正确解析为合法<keyframe-selector>, 则该关键帧无效,在 [[web-animations-1#processing-a-keyframes-argument|处理]] 时会抛出 TypeError (行为同 double 超出 [0,1] 时)。

4.10.3. KeyframeEffectOptions 字典

KeyframeEffectOptions 字典接口新增如下成员:

partial dictionary KeyframeEffectOptions {
    IterationCompositeOperation iterationComposite = "replace";
};
iterationComposite, 类型为 IterationCompositeOperation, 默认值为 "replace"

用于指定动画每次迭代间合成方式的迭代合成操作

4.11. IterationCompositeOperation 枚举

一个动画特效迭代合成操作 可用 IterationCompositeOperation 枚举表示。

enum IterationCompositeOperation { "replace", "accumulate" };
replace

对应replace 迭代合成操作值,即 效果值 不受当前迭代影响。

accumulate

对应accumulate 迭代合成操作,即后续迭代会在上次迭代的最终值基础上累加。

4.12. EffectCallback 回调函数

自定义特效可通过脚本提供 EffectCallback 类型的回调函数来定义。

callback EffectCallback = undefined (double? progress,
                                (Element or CSSPseudoElement) currentTarget,
                                Animation animation);

每当与 KeyframeEffect 对象关联时更新,该 EffectCallback 就会被调用一次。

double? progress

调用时提供的迭代进度。为 null 时,回调应移除效果。

(Element or CSSPseudoElement) currentTarget

此回调预期操作的目标元素

注意 currentTarget 可能与 animation.target 不同。

animation目标元素在不同更新之间发生变化,则本方法会先用上一次 目标元素nullprogress 调用一次,再用新的目标和当前 progress 调用一次。这样可确保动画效果会被从旧 目标元素移除。

Animation animation

被更新的 Animation 对象。

4.13. Animatable 接口

sequence<Animation> getAnimations(options)

补充:

如果此对象是两个或以上动画特效的目标元素,且这些特效属于同一个动画,则该 Animation 只会在返回列表中出现一次。

4.14. Animatable 接口混入

Animatable 的混入接口 修改如下,新增以下成员:

Animation animate(keyframes, options)

将该过程第6步修改如下:

  1. 如果 optionsKeyframeAnimationOptions 对象, 则令 triggeroptionstrigger 成员, 若 optionstrigger 成员不存在则创建一个空 AnimationTrigger ,其选项字典为新的 AnimationTriggerOptions

  2. auto-rewind 标志为 true, 对 animation 运行为动画设置触发器过程。

KeyframeAnimationOptions 字典接口 添加以下成员:

dictionary TimelineRangeOffset {
  CSSOMString? rangeName;
  CSSNumericValue offset;
};

partial dictionary KeyframeAnimationOptions {
    (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeStart = "normal";
    (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeEnd = "normal";
    AnimationTrigger? trigger;
};
rangeStart, 类型为 (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString), 默认值为 "normal"

如果该值存在且非 "normal",则指定该动画动画附着区间起点。 DOMString 类型将作为animation-range-end 语法解析, 得到 TimelineRangeOffsetrangeNameoffset,或 CSSKeywordValuevalue 为 "normal"; CSSNumericValue 被解释为 TimelineRangeOffset ,其 offset 为此值,rangeName 为 null。

rangeEnd, 类型为 (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString), 默认值为 "normal"

如有且非 "normal",指定该动画动画附着区间终点。 DOMString 类型按照 animation-range-end 语法解析得到 TimelineRangeOffsetrangeNameoffsetCSSNumericValue 被解释为 TimelineRangeOffset,包含该 offsetrangeName 为 null。

trigger, 类型为 AnimationTrigger,可为 null

如有此值,则指定该动画关联的动画触发器,会执行为动画设置触发器过程。

向任何接受 CSSKeywordValue ,其 value 不为 "normal" 的 rangeStartrangeEnd 传参到任何接受 KeyframeAnimationOptions 的 API 时会 抛出 TypeError

注意: rangeNameoffsetrangeStartrangeEnd 的组成部分,具体含义按 animation-range-startanimation-range-end 的语法执行,进而影响动画表现。

更多说明参见 [[web-animations-1#the-animatable-interface-mixin]]。

4.15. AnimationPlaybackEvent 接口

将 currentTime 和 timelineTime 属性的类型由 double 改为 CSSNumberish。

[Exposed=Window]
interface AnimationPlaybackEvent : Event {
    constructor(DOMString type, optional AnimationPlaybackEventInit
    eventInitDict = {});
    readonly attribute CSSNumberish? currentTime;
    readonly attribute CSSNumberish? timelineTime;
};
dictionary AnimationPlaybackEventInit : EventInit {
    CSSNumberish? currentTime = null;
    CSSNumberish? timelineTime = null;
};

更新 AnimationPlaybackEvent 属性类型。

currentTime, 类型为 CSSNumberish,只读,可为 null

在事件入队时,该动画的当前时间。 若 动画 在事件产生时 处于 idle,则为 null

timelineTime, 类型为 CSSNumberish,只读,可为 null

事件入队时,时间轴时间值,该时间轴与产生事件的动画相关联。 若 动画 当时未关联到 激活状态时间轴,则为 null

更新 AnimationPlaybackEventInit 成员类型。

currentTime, 类型为 CSSNumberish,可为 null,默认 null

currentTime 属性描述。

timelineTime, 类型为 CSSNumberish,可为 null,默认 null

timelineTime 属性描述。

4.16. AnimationTrigger 接口

[Exposed=Window]
interface AnimationTrigger {
  constructor(optional AnimationTriggerOptions options = {});
  attribute AnimationTimeline timeline;
  attribute AnimationTriggerBehavior behavior;
  attribute any rangeStart;
  attribute any rangeEnd;
  attribute any exitRangeStart;
  attribute any exitRangeEnd;
};
AnimationTrigger(options)

通过如下流程创建新的 AnimationTrigger 对象:

  1. 新建 AnimationTrigger 对象 trigger

  2. triggerdid trigger 设为 false。

  3. triggerstate 设为 idle

  4. triggerbehavior 设为 optionsbehavior

  5. 按 KeyframeAnimationOption 的 rangeStartrangeEnd 规则, 用 optionsrangeStartrangeEnd 设定 trigger默认区间

  6. 同上,配置 退出区间,若为"auto"则用默认区间对应边界。

  7. triggeroptions.timeline 作为参数,执行设置动画触发器时间轴过程。

options

新触发器的配置参数。

timeline, 类型为 AnimationTimeline

返回该触发器的 时间轴,如未激活则为 `null`。

behavior, 类型为 AnimationTriggerBehavior

返回该触发器的行为

rangeStart, 类型为 any

返回该触发器默认区间起点。

rangeEnd, 类型为 any

返回该触发器默认区间终点。

exitRangeStart, 类型为 any

返回该触发器退出区间起点。

exitRangeEnd, 类型为 any

返回该触发器退出区间终点。

4.17. AnimationTriggerOptions 字典

dictionary AnimationTriggerOptions {
  AnimationTimeline? timeline;
  AnimationTriggerBehavior? behavior = "once";
  (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeStart = "normal";
  (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeEnd = "normal";
  (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) exitRangeStart = "auto";
  (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) exitRangeEnd = "auto";
};
timeline, 类型为 AnimationTimeline,可为 null

触发器关联的时间轴。 如未指定则使用默认文档时间轴

behavior, 类型为 AnimationTriggerBehavior,可为 null,默认 "once"

触发器的行为类型。 未设置时默认为 once

rangeStart, 类型为 (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString), 默认 "normal"

触发器默认区间的起点。 未指定时 默认区间起点为 "normal"。

rangeEnd, 类型为 (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString), 默认 "normal"

触发器默认区间的终点。 未指定时 默认区间终点为 "normal"。

exitRangeStart, 类型为 (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString), 默认 "auto"

触发器退出区间的起点。 未指定时 退出区间起点为 "auto"。

exitRangeEnd, 类型为 (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString), 默认 "auto"

触发器退出区间的终点。 未指定时 退出区间终点为 "auto"。

4.18. AnimationTriggerBehavior 枚举

enum AnimationTriggerBehavior { "once", "repeat", "alternate", "state" };
once

once

repeat

repeat

alternate

alternate

state

state

4.19. 模型活动性

本节为非规范性内容

关于“Web 动画模型的变更立即生效”这一章节,补充如下:

同样的概念也适用于对 Web Animations 模型更复杂的修改,例如为 GroupEffect 添加或移除子元素。

补充:

通过编程接口对模型进行的更改 不会导致任何 EffectCallback 函数被调用

例如,在如下代码中,回调函数直到脚本块在常规更新期间完成后才会被调用。

var timesCalled = 0;
elem.animate(function() {
  timesCalled++;
}, 10000);
alert(timesCalled); // 显示 ‘0’

注意:需要在其它地方对其进行规范定义。

5. 变更说明

5.1. 自 Level 1 以来的变更

本规范相较于先前版本引入了以下变更:

本模块还包括一些关于 [[#custom-effects]] 的探索性提案,但对于该功能的设计仍存在一些顾虑,将来可能会被替换。 参见 讨论

5.2. 2023 年 2 月 21 日的第一个公开工作草案 以来的变更

6. 隐私注意事项

目前尚未收到与本模块相关的隐私注意事项报告。

7. 安全性注意事项

目前尚未收到与本模块相关的安全性注意事项报告。

一致性

文档惯例

一致性要求通过描写性断言与 RFC 2119 术语的组合方式表达。规范性部分中的关键字 “MUST”(必须)、“MUST NOT”(禁止)、“REQUIRED”(必需)、“SHALL”(应)、“SHALL NOT”(不得)、“SHOULD”(应该)、“SHOULD NOT”(不应该)、“RECOMMENDED”(推荐)、“MAY”(可以)及“OPTIONAL”(可选)应按照 RFC 2119 的描述进行解释。 但为了可读性,本规范未将这些单词全部大写。

除明确标记为非规范性、示例和注释的部分外,本规范全部内容为规范性内容。[RFC2119]

本规范中的示例通过“for example”引入,或通过 class="example" 与规范性文本区分,例如:

这是说明性示例。

说明性注解以 “Note” 开始,并通过 class="note" 与规范性文本区分,例如:

注意,这是说明性注解。

提示部分为规范性内容,会特别高亮显示,并通过 <strong class="advisement"> 与其他规范性文本区分,例如: 用户代理(UA)必须 提供可访问的替代方案。

一致性类别

本规范的一致性定义适用于三类对象:

样式表
一个 CSS 样式表
渲染器
一个 UA 可以解释样式表的语义并渲染使用这些样式表的文档。
创作工具
一个 UA 可以编写样式表。

如果其所有语句均根据本模块定义的语法以及通用 CSS 语法和每个特性定义的独立语法为有效,则样式表符合本规范要求。

渲染器若能按照相关规范解析样式表、支持本规范定义的所有特性并正确渲染文档,则符合本规范。但由于设备限制导致用户代理无法正确渲染,不属于不符合。例如,UA 并不要求在单色显示器上渲染彩色。

创作工具如果能编写语法正确、符合通用 CSS 语法以及本模块中每个特性独立语法的样式表,并满足本模块描述的其它样式表一致性要求,则视为符合本规范。

部分实现

为了让创作者能够利用向前兼容的解析规则进行降级,CSS 渲染器必须将任何不支持的 at 规则、属性、属性值、关键字等语法结构视为无效并按照规定忽略。特别是,用户代理不得选择性忽略一个多值声明中的某些不支持的值而保留支持的值:如果某个值被认定为无效(即不支持的值),则整个声明都需要被忽略。

不稳定及专有特性实现

为了避免与将来 CSS 正式特性发生冲突,CSSWG 建议遵循最佳做法实现不稳定特性及专有扩展

非实验性实现

规范进入候选推荐阶段后,允许非实验性实现。实现者应发布任何可以证明正确实现的 CR 级特性(无厂商前缀)。

为保证 CSS 在各实现间的互操作性,CSS 工作组建议非实验性渲染器在发布无前缀实现前,向 W3C 提交实现报告(如有需要,还需提交测试用例)。提交给 W3C 的测试用例受 CSS 工作组审查和校正。

关于提交测试用例与实现报告的进一步信息,请访问 CSS 工作组网站 https://www.w3.org/Style/CSS/Test/。 若有疑问,请联系 public-css-testsuite@w3.org 邮件列表。

索引

本规范定义的术语

外部引用定义的术语

参考文献

规范性引用

[CSS-ANIMATIONS-1]
David Baron 等. CSS Animations Level 1。2023年3月2日。WD。URL: https://www.w3.org/TR/css-animations-1/
[CSS-DISPLAY-4]
Elika Etemad;Tab Atkins Jr.。CSS Display Module Level 4。2025年11月6日。WD。URL: https://www.w3.org/TR/css-display-4/
[CSS-EASING-1]
Brian Birtles;Dean Jackson;Matt Rakow。CSS Easing Functions Level 1。2023年2月13日。CRD。URL: https://www.w3.org/TR/css-easing-1/
[CSS-EASING-2]
CSS Easing Functions Level 2。2024年8月29日。 FPWD。URL: https://www.w3.org/TR/css-easing-2/
[CSS-PSEUDO-4]
Elika Etemad;Alan Stearns。CSS Pseudo-Elements Module Level 4。2025年6月27日。WD。URL: https://www.w3.org/TR/css-pseudo-4/
[CSS-TYPED-OM-1]
Tab Atkins Jr.;François Remy。CSS Typed OM Level 1。2024年3月21日。WD。URL: https://www.w3.org/TR/css-typed-om-1/
[CSS-VALUES-4]
Tab Atkins Jr.;Elika Etemad。CSS Values and Units Module Level 4。2024年3月12日。WD。URL: https://www.w3.org/TR/css-values-4/
[CSSOM-1]
Daniel Glazman;Emilio Cobos Álvarez。CSS Object Model (CSSOM)。2021年8月26日。WD。URL: https://www.w3.org/TR/cssom-1/
[DOM]
Anne van Kesteren。DOM Standard。Living Standard。 URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren 等。HTML Standard。 Living Standard。URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren;Domenic Denicola。Infra Standard。Living Standard。URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner。Key words for use in RFCs to Indicate Requirement Levels。1997年3月。Best Current Practice。URL: https://datatracker.ietf.org/doc/html/rfc2119
[SCROLL-ANIMATIONS-1]
Brian Birtles 等。Scroll-driven Animations。2023年6月6日。WD。URL: https://www.w3.org/TR/scroll-animations-1/
[WEB-ANIMATIONS-1]
Brian Birtles 等。Web Animations。2023年6月5日。WD。URL: https://www.w3.org/TR/web-animations-1/
[WEBIDL]
Edgar Chen;Timothy Gu。Web IDL Standard。Living Standard。URL: https://webidl.spec.whatwg.org/

IDL 索引

[Exposed=Window]
partial interface AnimationTimeline {
    readonly attribute CSSNumberish? currentTime;
    readonly attribute CSSNumberish? duration;
    Animation play (optional AnimationEffect? effect = null);
};

[Exposed=Window]
partial interface Animation {
    attribute CSSNumberish?       startTime;
    attribute CSSNumberish?       currentTime;
    attribute AnimationTrigger?   trigger;
    attribute (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeStart;
    attribute (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeEnd;
    readonly attribute double? overallProgress;
};

[Exposed=Window]
partial interface AnimationEffect {
    // Timing hierarchy
    readonly attribute GroupEffect?     parent;
    readonly attribute AnimationEffect? previousSibling;
    readonly attribute AnimationEffect? nextSibling;

    undefined before (AnimationEffect... effects);
    undefined after (AnimationEffect... effects);
    undefined replace (AnimationEffect... effects);
    undefined remove ();
};

partial dictionary EffectTiming {
    double delay;
    double endDelay;
    double playbackRate = 1.0;
    (unrestricted double or CSSNumericValue or DOMString) duration = "auto";
};

partial dictionary OptionalEffectTiming {
    double playbackRate;
};

partial dictionary ComputedEffectTiming {
    CSSNumberish         startTime;
    CSSNumberish         endTime;
    CSSNumberish         activeDuration;
    CSSNumberish?        localTime;
};

enum FillMode { "none", "forwards", "backwards", "both", "auto" };

[Exposed=Window]
interface GroupEffect {
  constructor(sequence<AnimationEffect>? children,
              optional (unrestricted double or EffectTiming) timing = {});

  readonly attribute AnimationNodeList  children;
  readonly attribute AnimationEffect?   firstChild;
  readonly attribute AnimationEffect?   lastChild;
  GroupEffect clone ();

  undefined prepend (AnimationEffect... effects);
  undefined append (AnimationEffect... effects);
};

[Exposed=Window]
interface AnimationNodeList {
    readonly attribute unsigned long length;
    getter AnimationEffect? item (unsigned long index);
};

[Exposed=Window]
interface SequenceEffect : GroupEffect {
  constructor(sequence<AnimationEffect>? children,
              optional (unrestricted double or EffectTiming) timing = {});

  SequenceEffect clone ();
};

partial interface KeyframeEffect {
    attribute IterationCompositeOperation    iterationComposite;
};

partial dictionary KeyframeEffectOptions {
    IterationCompositeOperation iterationComposite = "replace";
};

enum IterationCompositeOperation { "replace", "accumulate" };

callback EffectCallback = undefined (double? progress,
                                (Element or CSSPseudoElement) currentTarget,
                                Animation animation);

dictionary TimelineRangeOffset {
  CSSOMString? rangeName;
  CSSNumericValue offset;
};

partial dictionary KeyframeAnimationOptions {
    (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeStart = "normal";
    (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeEnd = "normal";
    AnimationTrigger? trigger;
};

[Exposed=Window]
interface AnimationPlaybackEvent : Event {
    constructor(DOMString type, optional AnimationPlaybackEventInit
    eventInitDict = {});
    readonly attribute CSSNumberish? currentTime;
    readonly attribute CSSNumberish? timelineTime;
};
dictionary AnimationPlaybackEventInit : EventInit {
    CSSNumberish? currentTime = null;
    CSSNumberish? timelineTime = null;
};

[Exposed=Window]
interface AnimationTrigger {
  constructor(optional AnimationTriggerOptions options = {});
  attribute AnimationTimeline timeline;
  attribute AnimationTriggerBehavior behavior;
  attribute any rangeStart;
  attribute any rangeEnd;
  attribute any exitRangeStart;
  attribute any exitRangeEnd;
};

dictionary AnimationTriggerOptions {
  AnimationTimeline? timeline;
  AnimationTriggerBehavior? behavior = "once";
  (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeStart = "normal";
  (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeEnd = "normal";
  (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) exitRangeStart = "auto";
  (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) exitRangeEnd = "auto";
};

enum AnimationTriggerBehavior { "once", "repeat", "alternate", "state" };

问题索引

这并不完全正确。如果old effect在同一个任务中已经附加到另一个动画,我们可能不应该为unresolved做额外的回调。

关于何时调用自定义效果的定义,需要重新检查甚至重写。

播放动画 的流程需要包括调度任务以更新自定义效果
调用自定义效果的流程需要重构。目前可能会因为某些变化被合并而调用得过于频繁。
如果动画的当前时间被显式设置,对于暂停的动画来说,当前的流程并不严格正确,因为这会导致时间线的当前时间与动画当前时间间产生超前或滞后。
该流程很可能可以简化,并且无论播放速率还是开始时间,都直接判断是否处于滚动边界。本意是避免动画在滚动极限处失活,不必为ScrollTimeline设置fill-mode。只检查时间线的当前时间是否为0或者为时间线时长,可能就已足够。
当前缓动函数在组效果上应用时,若生成[0, 1]区间外的结果,将导致子项进入过多迭代或直接进入填充模式,而不是像直接应用到子项时继续按定义外推(直接应用时会外推)。

为了解决这个问题,可能需要引入“overflow”填充模式,从而让超出活动时间范围的时间值采用外推而非填充。

2013年东京F2F讨论记录第15节(溢出填充)

目前,缓动函数组效果上的使用没有限制。 这带来了实现复杂性和关于填充模式行为复杂性的担忧。 因此,允许在组效果上任意使用所有缓动函数被认为是风险点

可选方案: 要么将组效果上的缓动函数限制为线性缓动函数, 要么仅允许具有某些特性的“简单”缓动函数,以减轻复杂缓动带来的问题。

2013年8月讨论第2节

“动画效果的关联动画”定义在为动画效果排序时需要修订,使用本规范级中引入的与动画关联定义,以正确处理组效果。
整个功能需要重新审视。目前的想法是,与其使用自定义效果,不如直接在每个动画效果上挂一个onupdate回调。例如想记录日志、在某些时刻触发额外操作时,可以直接扩展现有效果。目前的设计要实现类似需求,还需要额外加一级父组。
计时属性更新时也需要回调,对吧?
某些场景下,需在动画树的特定时刻触发操作。 很多时候可以插入带step-start缓动的自定义效果,跨越要触发动作的时间段即可实现这一点。 但这样可能对内容带来额外布局约束,这种处理会变得繁琐。

目前考虑的替代方案:

需要更加精确定义。样式会被刷新吗?理论上应该会。能否在脚本型动画效果执行期暂停重排,执行完毕后一并重排?
触发器对滚动驱动动画应有行为影响吗?
规范中的空闲状态需要正式决议吗?
“主要/反向”行为类型需要正式定义吗?
是否应规定 exit range 的边界只能等于或大于 default range 的边界?
如果时间线再次变为空闲,"did trigger" 标记重置成 false 是否为预期行为?
有建议将此方法重命名,甚至移除(参见TAG 反馈)。
需要定义effect为 null 时的启动行为。
应该叫 parentGroup 吗?
remove() 方法可用于将效果从父组或动画中移除。我们要在level 1中保留它,并让它仅意味着将动画从动画中移除吗?