网页动画

W3C 工作草案

更多关于本文档的信息
此版本:
https://www.w3.org/TR/2023/WD-web-animations-1-20230605/
最新发布的版本:
https://www.w3.org/TR/web-animations-1/
编辑草案:
https://drafts.csswg.org/web-animations-1/
以前的版本:
历史:
https://www.w3.org/standards/history/web-animations-1
测试套件:
https://github.com/web-platform-tests/wpt/tree/master/web-animations
反馈:
CSSWG 问题库
规范内联
编辑:
(受邀专家)
(谷歌)
(谷歌)
(苹果公司)
前编辑:
(谷歌)
(谷歌)
(谷歌)
(谷歌)
建议编辑此规范:
GitHub 编辑器
参与:
通过 GitHub 修改文本
加入Animation at Work slack 的 "waapi" 频道
IRC: #webanimations on W3C’s IRC

摘要

本规范定义了一个同步和定时模型,用于更改网页展示内容。 此规范还定义了与此模型交互的应用程序接口, 并且预计未来的规范将定义公开这些功能的声明性方法。

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

本文档的状态

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

本文档由 CSS 工作组 作为工作草案 发表,使用了 推荐标准流程。 作为工作草案的发表 并不意味着 W3C 及其成员的认可。

这是一个草案文件, 可能会在任何时候更新、替换 或被其他文件取代。 除了作为工作进展之外,引用本文档是不合适的。

请通过 在 GitHub 上提交问题(优先), 反馈意见,包括规范代码 “web-animations” 在标题中,例如: “[web-animations] …评论摘要…”。 所有问题和评论都会 被存档。 或者,也可以将反馈发送到 (存档的) 公共邮件列表 www-style@w3.org

本文档受 2021年11月2日 W3C 流程文档 管辖。

本文档由一个在 W3C 专利政策 下运作的小组产生。 W3C 维护了一个 公开的专利披露列表, 该页面还包括披露专利的说明。 任何知道自己所掌握的专利包含必要权利要求的人, 必须按照 W3C 专利政策第6节 的规定披露该信息。

1. 介绍

本节是非规范性的

Web Animations 定义了一个在 Web 平台上支持动画和同步的模型。 其他规范将基于此模型构建,并通过声明性方式公开其功能。 此外,本规范还定义了一个编程接口, 该接口可由提供脚本支持的用户代理实现。

1.1. 用例

Web Animations 模型旨在提供表达 CSS 过渡[CSS-TRANSITIONS-1]、 CSS 动画[CSS-ANIMATIONS-1]和 SVG [SVG11]所需的功能。 因此,Web Animations 模型的用例是这三个规范的用例的合集。

编程接口的用例包括以下内容:

检查运行中的动画

Web 应用程序通常必须等待某些动画效果完成后再更新某些状态。 本规范中的编程接口允许此类应用程序等待所有当前运行的动画完成, 无论它们是由 CSS 过渡、CSS 动画、SVG 动画定义的,还是通过编程接口直接创建的。

// 在删除元素之前等待所有动画完成
Promise.all(
  elem.getAnimations().map(animation => animation.finished)
).then(() => elem.remove());

或者,应用程序可能希望查询动画的播放状态,而无需等待。

const isAnimating = elem.getAnimations().some(
  animation => animation.playState === 'running'
);
控制运行中的动画

有时需要对动画进行播放控制,以便它们能够响应外部输入。 例如,可能需要在显示模态对话框之前暂停所有现有动画,以免它们分散用户的注意力。

// 暂停文档中的所有现有动画
for (const animation of document.getAnimations()) {
  animation.pause()
}
通过脚本创建动画

虽然可以使用 ECMAScript 通过 requestAnimationFrame [HTML]执行动画, 但在表示方式和可能的性能优化(如在单独线程上执行动画)方面,此类动画的行为与声明性动画有所不同。 使用 Web Animations 编程接口,可以通过脚本创建与声明性动画具有相同行为和性能特征的动画。

// 快速淡出
elem.animate({ transform: 'scale(0)', opacity: 0 }, 300);
动画调试

在复杂的应用程序中,可能很难确定元素如何达到其当前状态。 可以使用 Web Animations 编程接口检查正在运行的动画,以回答诸如“为什么这个元素的不透明度在变化?”等问题。

// 打印 elem 上任何不透明度动画的 id
for (const animation of elem.getAnimations()) {
  if (
    animation.effect instanceof KeyframeEffect &&
    animation.effect
      .getKeyframes()
      .some(frame => frame.hasOwnProperty('opacity'))
  ) {
    console.log(animation.id);
  }
}

同样,为了微调动画,通常需要降低它们的播放速度并重放它们。

// 减慢并重放任何变换动画
const transformAnimations = elem.getAnimations().filter(
  animation =>
    animation.effect instanceof KeyframeEffect &&
    animation.effect.getKeyframes().some(
      frame => frame.hasOwnProperty('transform')
    )
);

for (const animation of transformAnimations) {
  animation.currentTime = 0;
  animation.updatePlaybackRate(0.5);
}
测试动画

为了测试使用动画的应用程序,通常不实际等待这些动画运行完毕。 而是希望将动画调整到特定时间点。

// 将动画调整到中途并检查不透明度是否为50%
for (const animation of elem.getAnimations()) {
  const { delay, activeDuration } = animation.effect.getComputedTiming();
  animation.currentTime = delay + activeDuration / 2;
}
assert.strictEqual(getComputedStyle(elem).opacity, '0.5');

// 检查动画完成后加载屏幕是否隐藏
for (const animation of elem.getAnimations()) {
  animation.finish();
}
// 等待一帧以便事件处理程序有机会运行
requestAnimationFrame(() => {
  assert.strictEqual(
    getComputedStyle(document.querySelector('#loading')).display, 'none');
});

1.2. 与其他规范的关系

CSS 过渡[CSS-TRANSITIONS-1]、 CSS 动画[CSS-ANIMATIONS-1]和 SVG [SVG11]都提供了在网页上生成动画内容的机制。 虽然这三个规范提供了许多相似的功能, 但它们使用不同的术语来描述。 本规范提出了一个涵盖这三个规范的共同特征的抽象动画模型。 该模型与这些规范的当前行为向后兼容,因此可以根据此模型定义它们, 而不会发生任何可观察到的变化。

SVG 1.1 中的动画功能是基于 SMIL 动画[SMIL-ANIMATION]定义的。 通过根据 Web Animations 模型定义 SVG 的动画功能, 可以消除 SVG 和 SMIL 动画之间的依赖关系。

动画帧回调(通常称为“requestAnimationFrame”)[HTML]一样, 本规范的编程接口部分允许通过脚本创建动画。 使用本规范中定义的接口创建的动画,一旦创建, 由用户代理完全执行,这意味着它们与标记定义的动画共享相同的性能特性。 使用此接口可以以更简单和更高效的方式从脚本创建动画。

编程接口中使用的时间值 与动画帧回调 [HTML]中使用的时间值相对应,并且其执行顺序定义为使得可以同时使用这两个接口而不会发生冲突。

本规范的编程接口部分对 HTML 中定义的接口进行了一些补充[HTML]

1.3. 本规范概述

本规范首先定义了一个动画的抽象模型。 随后,通过抽象模型定义了一个编程接口。 编程接口是根据抽象模型定义的,仅与提供脚本支持的用户代理相关。

2. 规范约定

本规范首先描述了抽象概念,如动画动画效果及其所属的属性,如播放速率迭代持续时间。 除了这些属性之外,通常还有更新这些属性的特定过程,例如设置播放速率的过程或 设置动画开始时间的过程。

当本规范未明确链接到某个过程时,需要用户代理更新属性的文本,如“将动画开始时间设为未解析”,应理解为直接更新属性 而不调用任何相关过程。

有关本规范特有的文档约定,请参见文档约定部分。

3. Web Animations 模型概述

本节是非规范性的

简单来说,Web Animations 模型由两个主要独立的部分组成,即时间模型动画模型。这些部分的作用如下:

时间模型

获取某一时刻,并将其转换为动画单次迭代中的比例距离,称为迭代进度。 同时记录迭代索引,因为有些动画每次重复时会有所不同。

动画模型

获取时间模型生成的迭代进度值和迭代索引,并将其转换为要应用于目标属性的一系列值。

从图形上看,这个流程可以表示如下:

Web Animations 模型的操作概述
Web Animations 模型的操作概述。
当前时间作为输入进入时间模型,生成迭代进度值和迭代索引。
这些参数作为输入用于动画模型,生成要应用的值。

例如,考虑一个动画:

前三点适用于时间模型。 在6秒时,它会计算出动画应处于第二次迭代的中途,并生成结果0.5。 然后动画模型使用该信息来计算宽度。

本规范从时间模型开始,然后介绍动画模型。

4. 时间模型

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

4.1. 时间模型概述

本节是非规范性的

Web Animations 时间模型的两个特征是:无状态层次化

4.1.1. 无状态

Web Animations 时间模型通过接收输入时间并生成输出迭代进度来运行。 由于输出仅基于输入时间,并且与先前的输入无关,因此该模型可以被描述为无状态的。 这使得模型具有以下特性:

与帧率无关

由于输出与先前的输入无关,更新模型的速度不会影响其进度。 只要输入时间与现实世界时间的进展成比例,动画将以相同的速度进行, 而不受运行它们的设备能力的影响。

方向不可知

由于输入序列无关紧要,模型是无方向的。 这意味着模型可以更新到任意时刻,而无需任何特殊处理。

常时间寻址

由于每个输入都与先前的输入无关,执行寻址操作所需的处理,即使是很远的未来,也至少有可能是恒定的。

时间模型的无状态行为有一些例外情况。

首先,模型的编程接口中定义了一些方法, 提供播放控制功能,如暂停动画。 这些方法是根据调用它们的时间来定义的,因此是有状态的。 这些方法主要是为了方便而提供的,不属于核心时间模型,而是叠加在其上。

同样,动画的完成行为意味着 动画的媒体(关联效果)的结束时间的动态变化 可能会根据变化发生的时间产生不同的结果。 这种行为虽然有些不尽如人意,但被认为是直观的,并与 HTML 保持一致。 因此,模型只能在时间属性没有动态变化的情况下真正被描述为无状态的。

最后,每次更新模型时,都可以认为它建立了一个临时状态。 虽然这个临时状态会影响编程接口返回的值, 但它对后续更新没有影响,因此与上述无状态特性不冲突。

4.1.2. 层次化

时间模型的另一个特征是时间是继承的。 时间从时间线开始,逐步向下传递到每个动画效果。 在每个步骤中,时间可能被向前或向后移动、缩放、反转、暂停和重复。

时间节点的层次结构
时间节点的层次结构。 树中的每个节点都从其父节点继承时间。

在本规范的这一层次中,层次结构是浅显的。 规范的后续层次将引入组效果的概念,从而允许更深的时间层次结构。

4.2. 时间值

时间的基础是时间节点之间的时间关系层次结构。 父节点以时间值的形式向其子节点提供时间信息。

时间值是一个实数,通常表示 从某个时刻起的毫秒数。 时间值与挂钟时间毫秒数之间的联系 可能会因值在时间层次结构中传递时应用的任何数量的变换而模糊。

将来可能会有基于滚动位置或 UI 手势的时间线, 在这种情况下,时间值与毫秒之间的联系将进一步减弱。

如果时间节点没有处于生成时间值的状态, 则时间值也可能是未解析的

4.3. 时间线

时间线提供 时间值来源, 以便进行同步。

在任何给定时刻,时间线都有一个 单一的当前时间值, 简单地称为时间线的当前时间

时间线可能并不总能返回有意义的 时间值,而是仅返回 未解析的时间值。 例如,它可能是相对于尚未发生的时刻定义的, 如文档的加载事件触发的时间。 当时间线时间值未解析时, 该时间线被视为非活动

如果时间线报告的当前时间 始终大于或等于其先前报告的当前时间,则该 时间线单调递增的。

特定类型的时间线可以定义一个过程,将 时间线时间转换为起源相对时间, 以便可以比较由基于挂钟的时间线生成的时间值

时间线可以与文档相关联

当被要求为时间戳nowDocumentdoc更新动画并发送事件时,请执行以下步骤:

  1. 更新所有时间线的当前时间,这些时间线doc相关联,并传递now作为时间戳。

    由于时间模型的层次结构,更新当前时间时间线还涉及:

  2. doc移除被替换的动画

  3. 执行微任务检查点

    注意: 这样做是为了确保在上一步骤中更新时间线时排队的任何微任务 在分发动画事件之前运行其回调。

  4. doc待处理的动画事件队列复制为events to dispatch

  5. 清除doc待处理的动画事件队列

  6. 按以下方式对events to dispatch中的动画事件执行稳定排序:

    1. 按其预定事件时间排序,使得预定较早发生的事件排在预定较晚发生的事件之前, 而预定事件时间为未解析的事件排在预定事件时间已解析的事件之前。

    2. 预定事件时间相同的事件中,按其复合顺序排序。

    注意: 对事件进行排序的目的是为了尽可能确保,即使在设备性能不同从而帧率不同的情况下, 事件也能以一致的顺序分发。

    注意: 稳定排序的要求是因为有时可能会有相同预定事件时间的事件排队。 例如,持续时间为零的 CSS 动画可能会同时分发animationstartanimationend事件, 这些事件的顺序应该保持不变。

  7. 按照上一步确定的顺序,在其对应的目标上分发events to dispatch中的每个事件。

通常描述每次调用此过程时,都会建立一个新的动画帧是很方便的。 对动画动画效果的时间属性的更改,或对象的添加和删除可能会导致时间模型或动画模型的输出发生变化, 但这些操作本身并不会创建新的动画帧,它们只是更新当前的动画帧

4.3.1. 文档时间线

文档时间线是一种时间线,它与文档相关联,其 当前时间计算为每次 更新动画并发送事件过程运行时提供的now时间戳的固定偏移量。 此固定偏移量称为文档时间线的起始时间

“起始时间”这个术语可能有更好的替代方案—— 它与“时间起源”过于相似。[Issue #2079]

在为其关联文档建立时间起源之前, 文档时间线非活动的。

文档时间线变为 活动后,它是 单调递增的。

与不是活动文档Document相关联的文档时间线也被视为非活动的。

要将文档时间线timeline时间线时间timeline time转换为起源相对时间, 返回timeline timetimeline起始时间之和。如果timeline是非活动的,则返回 未解析的时间值

4.3.2. 文档的默认时间线

每个Document都有一个文档时间线,称为默认文档时间线默认文档时间线对于每个文档都是唯一的,并且在文档的生命周期内持久存在, 包括调用document.open()[HTML]时。

默认文档时间线起始时间为零。

本节是非规范性的

由于提供给文档时间线now时间戳值没有进行缩放,因此它生成的时间值将与挂钟时间毫秒数成比例。

此外,由于默认文档时间线时间值时间起源没有偏移, 因此document.timeline.currentTime将大致对应于 Performance.now()[HR-TIME],只是document.timeline.currentTime 不会在调用更新动画并发送事件过程之间发生变化。

4.4. 动画

本节是非规范性的

时间线的子节点称为动画。 动画接受一个动画效果,即一些定时行为的静态描述,并将其绑定到 时间线上以便运行。 动画还通过提供暂停、寻址和速度控制来允许在运行时控制 动画效果与其时间线之间的连接。 动画与动画效果之间的关系类似于 DVD 播放器和 DVD 之间的关系。

动画连接单个 动画效果,称为其 关联效果, 到一个时间线并提供播放控制。 这两个关联都是可选且可配置的,因此 动画在某个时刻可能没有 关联效果时间线

动画定时文档是与其 时间线相关联Document。 如果动画未与时间线关联,或其时间线未与文档关联,则它没有定时文档

动画开始时间是其 时间线在其关联效果计划开始播放时的 时间值。 动画的开始时间最初是未解析的。

动画还保持一个保持时间时间值用于在暂停等情况下固定动画的输出 时间值,称为其当前时间保持时间最初是 未解析的。

为了建立冲突动画的相对顺序, 动画按创建顺序附加到全局动画列表中。 但是,某些动画类可能会提供排序动画的替代方法 (参见§ 5.4.1 动画类)。

4.4.1. 设置动画的时间线

animation的时间线设置为new timeline(可能为null)的过程如下:

  1. old timeline设为当前的animation时间线(如果有)。

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

  3. animation时间线设置为 new timeline

  4. 如果animation开始时间解析,则将animation保持时间设为未解析

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

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

4.4.2. 设置动画的关联效果

animation的关联效果设置为new effect(可能为null)的过程如下:

  1. old effect设为当前的animation关联效果(如果有)。

  2. 如果new effectold effect是同一对象,则终止此过程。

  3. 如果animation待处理的暂停任务,则重新安排该任务,以便在animation 就绪时尽快运行。

  4. 如果animation待处理的播放任务,则重新安排该任务,以便在animation 就绪播放new effect时尽快运行。

  5. 如果new effect不为nullnew effect是另一个动画关联效果,则运行 设置动画的关联效果 (此过程)在previous animation上传递null作为new effect

  6. animation关联效果设为new effect

  7. 运行更新动画完成状态的过程, 对animationdid seek标志设置为false,并将synchronously notify标志设置为false。

4.4.3. 动画的当前时间

动画为其 关联效果提供一个 时间值,称为动画的当前时间

当前时间根据以下条件计算:

如果动画的保持时间解析

当前时间是动画的保持时间

如果以下任一条件为真:

  1. 动画没有关联的时间线,或

  2. 关联的时间线非活动的,或

  3. 动画的开始时间未解析的。

当前时间是一个未解析的时间值。

否则,
当前时间 = (时间线时间 - 开始时间) × 播放速度

其中时间线时间是关联的时间值播放速度值定义在 § 4.4.15 速度控制中。

4.4.4. 设置动画的当前时间

动画的当前时间可以设置为一个新值,以定位动画。设置当前时间的过程分为两部分。

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

  1. 如果seek time是一个未解析的时间值,则执行以下步骤。

    1. 如果当前时间解析,则抛出TypeError异常。

    2. 中止这些步骤。

  2. 根据以下情况更新animation保持时间开始时间

    如果以下任意条件为真:

    animation保持时间设置为seek time

    否则,

    animation开始时间设置为评估结果timeline time - (seek time / 播放速率),其中timeline time是与animation关联的时间线的当前时间值

  3. 如果animation没有关联的时间线或关联的时间线是非活动的,则将animation开始时间设为未解析

    这保留了一个不变性,即当我们没有活动时间线时,只能设置开始时间或动画的当前时间之一。

  4. animation先前的当前时间设为未解析

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

  1. 运行静默设置当前时间的步骤,将animation的时间设置为seek time

  2. 如果animation待处理的暂停任务,则通过执行以下步骤同步完成暂停操作:

    1. animation保持时间设置为seek time

    2. 应用任何待处理的播放速率animation

    3. animation开始时间设为未解析

    4. 取消待处理的暂停任务

    5. 解决animation当前准备就绪的承诺,并传递animation

  3. 运行更新动画完成状态的过程,对animationdid seek标志设置为true,并将synchronously notify标志设置为false。

4.4.5. 设置动画的开始时间

将动画animation的开始时间设置new start time的过程如下:

  1. timeline timeanimation所关联的时间线的当前时间值。如果没有与animation关联的时间线或关联的时间线是非活动的,则将timeline time设为未解析

  2. 如果timeline time未解析的,而new start time是已解析的,则将animation保持时间设为未解析。

    这保留了一个不变性,即当我们没有活动时间线时,只能设置开始时间或动画的当前时间之一。

  3. previous current timeanimation的当前时间。

    注意:这是应用上一步中的更改后animation的当前时间,这些更改可能导致当前时间变为未解析。

  4. 应用任何待处理的播放速率animation

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

  6. 根据以下匹配条件之一更新animation保持时间

    如果new start time是已解析的,

    如果animation播放速率不为0,则将animation保持时间设为未解析。

    否则(new start time未解析),

    animation保持时间设为previous current time,即使previous current time是未解析的。

  7. 如果animation待处理的播放任务待处理的暂停任务,则取消该任务,并解决animation当前准备就绪的承诺,传递animation

  8. 运行更新动画完成状态的过程,设置did seek标志为true,并将synchronously notify标志设置为false。

4.4.6. 等待关联效果

本节为非规范性内容

某些由动画执行的操作可能不会立即发生。 例如,一些用户代理可能会将动画的播放委托给单独的进程或专门的图形硬件,每一个都可能涉及一些设置开销。

如果此类动画从触发动画的时刻开始计时,则可能在动画的第一帧和第二帧之间产生显著的跳跃,这对应于所涉及的设置时间。

为了避免这个问题,Web Animations 通常从动画的第一帧完成时开始计时。 这由未解析开始时间表示,该时间在动画准备好时解析。 内容可以通过将开始时间设置为已解析的时间值来选择退出此行为。

在以下两个条件都为真时,动画被认为是准备好的:

4.4.7. 当前准备就绪的 Promise

每个动画都有一个当前准备就绪的 Promise。 该当前准备就绪的 Promise最初是一个已解析的Promise, 该 Promise 是使用创建新解析的 Promise的过程创建的, 其值为动画本身,并在动画的相关领域中创建。

每当动画在先前没有挂起任务时排队挂起的播放任务挂起的暂停任务,或当动画被取消时(参见§ 4.4.14 取消动画), 该对象将被新的Promise 对象替换。

请注意,由于同一个对象用于挂起播放和挂起暂停请求,因此建议作者在Promise 对象被解析时检查动画的状态。

例如,在以下代码片段中,当当前准备就绪的 Promise被解析时,动画的状态将为运行中。 这是因为当挂起的播放任务仍在队列中时,播放操作发生,因此当前准备就绪的 Promise被重用。

animation.pause();
animation.ready.then(function() {
  // 显示 'running'
  alert(animation.playState);
});
animation.play();

4.4.8. 播放动画

用于播放动画的过程,animation,给定一个标志auto-rewind,如下:

  1. aborted pause为布尔标志,如果animation有一个挂起的暂停任务,则为 true,否则为 false。

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

  3. seek time为一个初始为未解决的时间值

  4. 如果auto-rewind标志为 true,执行以下步骤中第一个匹配条件对应的步骤:

    如果animation有效播放速率 ≥ 0,且animation当前时间以下之一

    seek time设置为零。

    如果animation有效播放速率 < 0,且animation当前时间以下之一

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

    抛出一个"InvalidStateError"DOMException并中止这些步骤。

    否则,

    seek time设置为animation关联效果结束

  5. 如果以下三个条件满足:

    seek time设置为零。

    注意: 上述步骤确保无论auto-rewind标志如何设置,此过程都将播放一个闲置动画。

  6. has finite timeline为 true,如果animation有一个关联的时间轴,该时间轴不是单调递增的

  7. 如果seek time已解决的

    如果has finite timeline为 true,
    1. animation开始时间设置为seek time

    2. animation保持时间未解决的

    3. 应用任何挂起的播放速率animation

    否则,

    animation保持时间设置为seek time

  8. 如果animation保持时间已解决的,让其开始时间未解决的

  9. 如果animation有一个挂起的播放任务挂起的暂停任务

    1. 取消该任务。

    2. has pending ready promise设置为 true。

  10. 如果以下四个条件满足:

    中止此过程。

  11. 如果has pending ready promise为 false,让animation当前准备就绪的 Promise一个新的 promise,位于animation相关领域

  12. 安排一个任务,在animation准备就绪时立即运行。 该任务应执行以下步骤:

    1. 断言animation开始时间保持时间中的至少一个是已解决的

    2. ready timeanimation成为准备就绪时间轴时间值

    3. 执行以下条件中第一个匹配条件对应的步骤(如果有):

      如果animation保持时间已解决的
      1. 应用任何挂起的播放速率animation

      2. new start time为评估ready time - 保持时间 / 播放速率的结果。如果播放速率为零,让new start timeready time

      3. animation开始时间设置为new start time

      4. 如果animation播放速率不是 0,将animation保持时间设为未解决的

      如果animation开始时间已解决,并且animation有一个挂起的播放速率
      1. current time to match为评估(ready time - 开始时间) × 播放速率的结果。

      2. 应用任何挂起的播放速率animation

      3. 如果animation播放速率为零,让animation保持时间current time to match

      4. new start time为评估ready time - current time to match / 播放速率的结果。如果播放速率为零,让new start timeready time

      5. animation开始时间设置为new start time

    4. 解决animation当前准备就绪的 Promiseanimation

    5. 运行用于更新动画的完成状态的过程,将did seek标志设置为 false,并将synchronously notify标志设置为 false。

      注意,上述两步的顺序很重要,因为这意味着具有零长度关联效果的动画将在其当前完成的 Promise之前解决其当前准备就绪的 Promise

    只要上述任务已安排但尚未运行,animation就被描述为具有一个挂起的播放任务。然而,当任务正在运行时,animation具有挂起的播放任务

    如果用户代理确定animation立即准备就绪,它可能会将上述任务安排为微任务,使其在下一个微任务检查点运行,但不得同步执行任务。

    上述要求异步运行挂起的播放任务,确保如下代码在不同实现之间行为一致:

    animation.play();
    animation.ready.then(
      () => { console.log('Playback commenced'); },
      () => { console.log('Playback was canceled'); }
    );
    // 假设某种情况需要取消播放...
    animation.cancel();
    // "播放已取消" 将打印到控制台。
    

    在上述代码中,如果挂起的播放任务是同步运行的,则不会拒绝当前准备就绪的 Promise

  13. 运行用于更新动画的完成状态的过程,将did seek标志设置为 false,并将synchronously notify标志设置为 false。

4.4.9. 暂停动画

每当动画具有未解析的开始时间时,其当前时间将被暂停。

播放动画一样,暂停操作可能不会立即发生(参见§ 4.4.6 等待关联效果)。例如,如果动画由单独的进程执行,可能需要同步当前时间,以确保其反映动画进程绘制的状态。

将动画暂停动画,的过程如下:

  1. 如果动画待处理的暂停任务,中止这些步骤。

  2. 如果动画播放状态暂停,中止这些步骤。

  3. 寻找时间成为一个时间值,最初为未解析

  4. 如果动画具有关联的时间轴,且不是单调递增的时间轴,那么设具有有限时间轴为真。

  5. 如果动画当前时间未解析,根据以下第一个匹配条件执行步骤:

    如果动画播放速度≥ 0,

    寻找时间设置为零。

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

    抛出InvalidStateErrorDOMException并中止这些步骤。

    否则,

    寻找时间设置为动画关联效果结束

  6. 如果寻找时间已解析

    如果具有有限时间轴为真,

    动画开始时间设置为寻找时间

    否则,

    动画保持时间设置为寻找时间

  7. 具有待处理的准备承诺为一个布尔标志,初始为假。

  8. 如果动画待处理的播放任务,取消该任务并将具有待处理的准备承诺设为真。

  9. 如果具有待处理的准备承诺为假,将动画当前准备承诺设置为一个新的承诺,该承诺在动画相关领域中创建。

  10. 调度一个任务,在首次满足以下两个条件时执行:

    任务应执行以下步骤:

    1. 准备时间为完成暂停动画关联效果播放时,与动画关联的时间轴的时间值。

    2. 如果动画开始时间已解析并且其保持时间未解析,让动画保持时间为评估的结果。

    3. 应用任何待处理的播放速度动画上。

    4. 使动画开始时间未解析。

    5. 解析动画当前准备承诺,并将其与动画一同解决。

    6. 运行程序更新动画的完成状态did seek标志设置为假,synchronously notify标志设置为假。

    只要上述任务已调度但尚未运行,动画即被描述为有一个待处理的暂停任务。然而,在任务运行时,动画不会有待处理的暂停任务

    待处理的播放任务一样,用户代理必须异步运行待处理的暂停任务,尽管这可能是在下一个微任务检查点

  11. 运行程序更新动画的完成状态did seek标志设置为假,synchronously notify标志设置为假。

4.4.10. 到达结束

本节是非规范性的

DVD播放器或磁带播放器通常会继续播放,直到它们到达媒体的末尾,此时它们停止播放。如果这些播放器能够反向播放,它们通常会在到达媒体的开头时停止播放。为了模拟这种行为并与HTML的媒体元素 [HTML]保持一致,Web Animations的动画不会在关联效果结束时间之后继续向前播放,或在时间为零时向后播放。

达到其播放范围的自然边界的动画被称为已完成

限制当前时间的效果如下图所示。

限制动画当前时间的效果。
限制动画当前时间的效果,动画的开始时间为1秒,关联效果长度为3秒,并且具有正的播放速度。在动画的当前时间到达关联效果的末尾后,它被限制在3秒。

然而,可以将动画当前时间寻找到”超出关联效果结束后的时间。在这样做时,当前时间将不会继续推进,但动画将表现得像是已在寻找到的时间暂停。

这使得,例如,可以将没有关联效果的动画的当前时间寻找到5秒。如果稍后将一个具有晚于5秒的结束时间关联效果与动画相关联,则播放将从5秒标记处开始。

当动画的关联效果长度发生变化时,可能会出现类似的行为。

同样,当播放速度为负时,当前时间不会超过零时。

4.4.11. 当前的已完成 Promise

每个动画都有一个当前的已完成 Promise。 这个当前的已完成 Promise最初是一个待定的Promise对象。

每次动画离开已完成播放状态时,该对象将被替换为一个新的Promise

4.4.12. 更新已完成状态

对于具有正播放速率的动画,当前时间会不断增加,直到到达关联效果的结束时间

关联效果的结束时间等于动画的结束时间。如果动画没有关联效果,则关联效果的结束时间为零。

对于具有负播放速率的动画,当前时间会不断减少,直到到达零。

一个正在运行的动画,当达到这个边界(或超越它)并且其开始时间已解析时,即被称为已完成

在每次修改动画对象时,都会通过以下定义的更新动画的已完成状态过程检查是否越过了这个边界。这个过程还会作为更新动画并发送事件过程的一部分运行。在这两种情况下,did seek标志被设置为false。

对于每个动画,用户代理维护一个先前的当前时间 时间值,该值最初为未解析的

在正常播放期间,动画的当前时间被限制在上述边界内,然而,仍可以使用设置动画当前时间的过程将动画的当前时间设置为超出这些边界的时间。

用于更新动画的已完成状态的过程如下,参数包括did seek标志(指示是否在设置当前时间后进行的更新),以及synchronously notify标志(指示更新是否在我们期望立即完成事件队列和完成Promise解析的上下文中调用)。

  1. 计算unconstrained current time,即当前时间,若did seek为false,则使用保持时间代替未解析的时间值。如果did seek为true,则unconstrained current time等于current time

    注意:此定义是为了适应可能改变方向的时间轴。如果没有这个定义,即使时间轴在相反方向上进展,一旦完成的动画也将保持完成状态。

  2. 如果下列三个条件全部为真:

    则根据下列条件更新animation保持时间

    如果播放速率 > 0 并且unconstrained current time大于或等于关联效果结束时间

    如果did seek为true,则保持时间unconstrained current time的值。

    如果did seek为false,则保持时间previous current timeassociated effect end的最大值。如果previous current time未解析的,则保持时间associated effect end

    如果播放速率 < 0 并且unconstrained current time小于或等于0,

    如果did seek为true,则保持时间unconstrained current time的值。

    如果did seek为false,则保持时间previous current time和零的最小值。如果previous current time未解析的,则保持时间为零。

    如果播放速率 ≠ 0 并且animation活动时间轴关联,

    执行以下步骤:

    1. 如果did seek为true并且保持时间已解析的,则让animation开始时间等于以下表达式的结果:

      timeline time - (保持时间 / 播放速率)

      其中timeline timeanimation关联的时间轴的当前时间值

    2. 保持时间未解析的

  3. animationprevious current time设置为计算出的当前时间

  4. 如果current finished state为true并且当前完成的promise尚未解决,则执行以下步骤:

    1. 完成通知步骤指代以下过程:

      1. 如果animation播放状态不等于完成,则中止这些步骤。

      2. 解析animation当前已完成的promise对象,并传递animation

      3. 创建一个AnimationPlaybackEvent,即finishEvent

      4. finishEventtype属性设置为完成

      5. finishEventcurrentTime属性设置为animation当前时间

      6. finishEventtimelineTime属性设置为与animation关联的时间轴的当前时间。如果animation未与时间轴关联,或时间轴为非活动状态,则将timelineTime设置为null

      7. 如果animation具有时间文档,则将finishEvent及其目标animation附加到其待处理动画事件队列中。对于预定事件时间,使用将animation关联效果结束时间转换为与原点相关的时间的结果。

        否则,排队一个任务,以分发animation上的finishEvent。该任务的任务源是DOM操作任务源

    2. 如果synchronously notify为true,则取消任何已排队的微任务来运行animation完成通知步骤,并立即执行finish notification steps

      否则,如果synchronously notify为false,则排队一个微任务来运行animation完成通知步骤,除非已为animation排队了一个微任务来运行这些步骤。

  5. 如果current finished state为false并且animation当前已完成的promise已解析,则将animationcurrent finished promise设置为相关 Realm 中的新 Promise

通常情况下,有关动画已完成状态的通知是异步执行的。这使得动画能够暂时进入已完成状态而不会触发事件或解析Promise。

例如,在下面的代码片段中,animation会暂时进入已完成状态。如果完成状态的通知是同步进行的,则这段代码将导致完成事件被排队,并且当前已完成的Promise会被解析。然而,如果我们反转两个语句的顺序,使iterations首先更新,这种情况就不会发生。为了避免这种令人惊讶的行为,动画完成状态的通知通常是异步执行的。

var animation = elem.animate({ left: '100px' }, 2000);
animation.playbackRate = 2;
animation.currentTime = 1000; // animation is now finished
animation.effect.updateTiming({ iterations: 2 }); // animation is no longer finished

通知有关动画已完成状态的唯一例外情况是执行完成动画的过程(通常是通过调用finish()方法)。在这种情况下,作者的完成动画的意图是明确的,因此有关动画已完成状态的通知是同步进行的,如下所示。

var animation = elem.animate({ left: '100px' }, 1000);
animation.finish(); // finish event is queued immediately and finished promise
                    // is resolved despite the fact that the following statement
                    // causes the animation to leave the finished state
animation.currentTime = 0;

请注意,与完成动画的过程类似,取消动画的过程也会同步地将取消事件排队并拒绝当前已完成的Promise当前准备好的Promise

4.4.13. 完成动画

可以使用下面定义的完成动画过程,将animation推进到其当前播放方向的自然结束点:

  1. 如果animation有效播放速率为零,或者animation有效播放速率 > 0 并且关联效果结束时间为无限大,抛出一个 "InvalidStateError" DOMException并中止这些步骤。

  2. 应用任何待处理的播放速率animation

  3. 设置limit如下:

    如果播放速率 > 0,

    limit关联效果结束时间

    否则,

    limit为零。

  4. 静默设置当前时间limit

  5. 如果animation开始时间未解析的,并且animation有一个关联的活动时间轴,则将start time设置为以下表达式的结果: timeline time - (limit / 播放速率),其中timeline time是关联的时间值

  6. 如果存在待处理的暂停任务,并且开始时间已解析的,则:

    1. 保持时间设为未解析的

      通常,保持时间将已未解析,除非动画先前处于空闲状态。
    2. 取消待处理的暂停任务

    3. 解析animation当前就绪承诺并将animation作为其值。

  7. 如果存在待处理的播放任务,并且开始时间已解析的,则取消该任务并解析animation当前就绪承诺并将animation作为其值。

  8. 执行更新动画完成状态的过程,针对animation并将did seek标志设置为true,将synchronously notify标志设置为true。

4.4.14. 取消动画

可以取消动画,这将导致当前时间变为未解析的,从而移除由关联效果引起的任何效果。

取消动画的过程如下:

  1. 如果animation播放状态不是空闲,执行以下步骤:

    1. 运行重置动画的待处理任务的过程,针对animation

    2. 拒绝animation当前完成承诺并附上名为"AbortError"的DOMException。

    3. 当前完成承诺的[[PromiseIsHandled]]内部槽设置为true。

    4. 当前完成承诺成为新承诺,在animation相关领域中。

    5. 创建一个AnimationPlaybackEvent,命名为cancelEvent

    6. cancelEventtype属性设置为cancel

    7. cancelEventcurrentTime设置为null

    8. timeline time为与animation关联的时间轴的当前时间。如果animation未关联到一个活动时间轴,则让timeline time未解析时间值

    9. cancelEventtimelineTime设置为timeline time。如果timeline time未解析的,则将其设置为null

    10. 如果animation有一个时间控制文档,则将cancelEvent及其目标animation追加到该文档的待处理动画事件队列中。如果animation与定义了将时间轴时间转换为原点相对时间的过程的活动时间轴关联,则计划事件时间为应用该过程后得到的结果。否则,计划事件时间是一个未解析的时间值

      否则,排队一个任务,以分派animation上的cancelEvent。此任务的任务源是DOM操作任务源

  2. animation保持时间设置为未解析的

  3. animation开始时间设置为未解析的

重置动画的待处理任务的过程如下:

  1. 如果animation没有待处理的播放任务待处理的暂停任务,则中止此过程。

  2. 如果animation待处理的播放任务,取消该任务。

  3. 如果animation待处理的暂停任务,取消该任务。

  4. 应用任何待处理的播放速率animation

  5. 拒绝animation当前就绪承诺并附上名为"AbortError"的DOMException。

  6. animation当前就绪承诺的[[PromiseIsHandled]]内部槽设置为true。

  7. animation当前就绪承诺成为新解析的承诺对象,其值为animation,位于animation相关领域

4.4.15. 速度控制

可以通过设置播放速率来控制动画的播放速度。例如,将播放速率设置为2将导致动画的当前时间以其时间轴的两倍速率增加。同样,播放速率设置为-1将导致动画的当前时间以与其时间值增加相同的速率减少。

动画具有一个播放速率,它提供了从关联的时间轴时间值的变化速率到动画的当前时间的缩放因子。播放速率最初为1。

将动画的播放速率设置为零实际上会暂停动画(然而,播放状态不一定会变为暂停)。

4.4.15.1. 设置动画的播放速率

动画 animation 的播放速率设置为 new playback rate 的过程如下:

  1. 清除animation上的任何待处理的播放速率

  2. previous time为更改播放速率之前的animation当前时间

  3. previous playback rateanimation的当前有效播放速率

  4. 播放速率设置为new playback rate

  5. 根据以下第一个匹配条件执行步骤(如果有):

    如果animation单调递增时间轴关联,且previous time已解析

    将当前时间设置为animationprevious time

    如果animation与一个非空的时间轴关联,该时间轴不是单调递增的,并且animation开始时间已解析的,关联效果结束不是无限大,并且:

    • 如果previous playback rate<0,且new playback rate≥0,或者

    • 如果previous playback rate≥0,且new playback rate<0,

    animation开始时间设置为计算关联效果结束 - 开始时间的结果。

    注意: 这实际上翻转了非单调时间轴上的动画开始/结束时间,保留了相对于另一个方向的开始时间的相对偏移。

4.4.15.2. 无缝更新动画的播放速率

对于在另一个进程或线程上运行的飞行中的动画,设置播放速率的过程可能会导致动画跳跃, 如果运行动画的进程或线程当前未与执行更新的进程或线程同步。

为了对动画播放速率进行无缝更改,动画可以具有一个待处理的播放速率, 定义在进行任何必要的同步之后应用的播放速率(对于在不同线程或进程中运行的动画情况)。

最初,动画待处理的播放速率未设置。

动画的animation有效播放速率是其待处理的播放速率,如果已设置, 否则是动画的播放速率

当要应用任何待处理的播放速率动画animation时,执行以下步骤:

  1. 如果animation没有待处理的播放速率,则终止这些步骤。

  2. animation播放速率设置为其待处理的播放速率

  3. 清除animation待处理的播放速率

动画animation的播放速率无缝更新为new playback rate,并保留其当前时间的过程如下:

  1. animation播放状态记为previous play state

    注意: 在更新animation有效播放速率之前, 需要记录播放状态,因为在以下逻辑中, 如果animation当前处于完成状态,我们希望立即应用其待处理的播放速率, 无论应用后它是否仍将完成。

  2. animation待处理的播放速率设置为new playback rate

  3. 执行以下第一个匹配条件对应的步骤:

    如果animation有一个待处理的播放任务待处理的暂停任务

    终止这些步骤。

    注意: 不同类型的待处理任务将在其运行时应用待处理的播放速率,因此在这种情况下无需进一步操作。

    如果previous play state空闲暂停,或animation当前时间未解析

    应用任何待处理的播放速率animation

    注意: 第二个条件是必需的,以确保如果我们有一个运行中的动画,其当前时间未解析且没有待处理的播放任务,则我们不会在下面尝试播放它。

    如果previous play state完成

    1. unconstrained current time设为通过计算animation当前时间并将保留时间替换为未解析时间值得出的结果。

    2. animation开始时间设置为计算以下表达式的结果:

      timeline time - (unconstrained current time / 待处理的播放速率)

      其中,timeline time是与animation关联的时间轴的当前时间值

      如果待处理的播放速率为零,则将animation开始时间设置为timeline time

    3. 应用任何待处理的播放速率animation

    4. 执行更新动画完成状态的过程, 其中did seek标志设为false,synchronously notify标志设为false。

    否则,

    执行播放动画的过程, auto-rewind标志设为false。

4.4.16. 倒放动画

倒放动画animation的过程如下:

  1. 如果animation没有关联的时间轴,或关联的时间轴非活动的抛出一个 "InvalidStateError" DOMException 并终止这些步骤。

  2. original pending playback rate设为animation待处理的播放速率

  3. animation待处理的播放速率设为其有效播放速率的加法逆元(即 -有效播放速率)。

  4. 运行播放动画的步骤,auto-rewind标志设为true。

    如果播放动画的步骤抛出异常,将animation待处理的播放速率恢复为original pending playback rate并传播异常。

4.4.17. 播放状态

动画可以描述为以下播放状态之一,每个状态还提供了非规范性的描述:

空闲

动画的当前时间未解析的,动画的开始时间也是未解析的,且没有待处理的任务。在这种状态下,动画没有任何效果。

运行中

动画有一个已解析的当前时间,它会在每个动画帧上变化(前提是播放速率不为零且时间轴活动的并且单调递增)。

暂停

动画已被暂停,当前时间不再变化。

完成

动画已达到其播放范围的自然边界,当前时间不再更新。

动画播放状态animation在某个给定时刻的状态为以下条件中首先匹配的状态:

所有以下条件都为真:

空闲

以下任一条件为真:

暂停

animation来说,当前时间已解析的,并且以下任一条件为真:

完成

否则,

运行中

注意暂停状态在实际上"胜过"完成状态

然而,一个在其自然播放范围之外暂停的动画可以通过设置开始时间,从暂停状态转换为完成状态而无需重新开始,如下所示:

animation.effect.updateTiming({ duration: 5000 });
animation.currentTime = 4000; 
animation.pause(); 
animation.ready.then(function() {
  animation.effect.updateTiming({ duration: 3000 }); 
  alert(animation.playState); // 显示'paused'
  animation.startTime =
    document.timeline.currentTime - animation.currentTime * animation.playbackRate; 
  alert(animation.playState); // 显示'finished'
});

4.4.18. 动画事件

动画事件 包括本规范中定义的动画播放事件,以及来自 CSS 过渡 [CSS-TRANSITIONS-1]CSS 动画 [CSS-ANIMATIONS-1] 的事件。 未来的规范可能会通过进一步的类型扩展这组动画事件

每个文档 维护一个待处理动画事件队列,其中存储动画事件及其相应的事件目标 和计划事件时间计划事件时间是一个 相对于时间起源时间值,表示在无限高频率更新的情况下事件理想情况下的调度时间点。 它被更新动画并发送事件的过程用于按时间顺序排列队列中的 动画事件。 请注意,如果例如动画时间线生成的时间值与时间起源无关(例如跟踪滚动位置的时间线),或如果 时间线非活动的, 则该值可能是未解析

4.4.18.1. 排序动画事件

以下定义提供了排序队列事件的帮助。

将动画时间转换为时间线时间,即时间值time,相对于动画的开始时间animation,执行以下步骤:

  1. 如果time未解析的,返回time

  2. 如果time是无穷大,返回未解析时间值

  3. 如果animation播放速率为零, 返回未解析时间值

  4. 如果animation开始时间未解析的, 返回未解析时间值

  5. 返回计算结果: time × (1 / playback rate) + start time(其中 playback ratestart time分别是 animation播放速率开始时间)。

将时间线时间转换为相对于原点的时间,即一个以与时间值相同尺度表示的time,执行以下步骤,以时间线timeline为基准:

  1. time从动画时间转换为时间线时间。

  2. 如果timeline time未解析的,返回time

  3. 如果animation未关联时间线, 返回未解析的时间值。

  4. 如果animation关联的是非活动时间线, 返回未解析的时间值。

  5. 如果未定义将时间线时间转换为起源相关时间的过程, 返回未解析时间值

  6. 返回通过定义的过程,将时间线中的timeline time 转换为起源相关时间的结果,animation与此时间线关联。

4.4.18.2. 动画播放事件

动画播放时, 它们通过动画播放事件报告状态的变化。

动画播放事件 是时间模型的一个属性。因此,即使在关联效果不存在或没有可观察结果的情况下, 它们也会被调度。

4.4.18.3. 动画播放事件的类型
完成事件

每当动画进入完成播放状态时排队。

取消事件

每当动画从其他状态进入空闲播放状态时排队。 创建一个初始状态为空闲的新动画不会生成新的 取消事件

移除事件

每当动画被自动移除时排队。 参见§ 5.5 替换动画

4.5. 动画效果

动画效果是指时间层级中的一个项目的抽象术语。

4.5.1. 动画效果与动画之间的关系

如果设置了动画的关联效果,它就是一种动画效果动画的关联效果被称为与该动画相关联。 在某一时刻,一个动画效果最多只能与一个动画关联

一个动画效果, 效果,如果效果与动画相关联, 则效果是与时间线相关联的与时间线关联

4.5.2. 动画效果的类型

本规范定义了单一类型的动画效果关键帧效果。 本规范的后续版本将定义更多类型的动画效果

所有类型的动画效果定义了一些常见的属性,这些属性将在以下部分描述。

4.5.3. 活动区间

一个动画效果被安排运行的时间段称为其活动区间。 每个动画效果只有一个这样的区间。

活动区间的下限通常对应于与该动画关联的动画开始时间,但可能会因动画效果上的开始延迟而有所变动。

区间的上限由活动持续时间决定。

以下说明了动画开始时间开始延迟活动持续时间之间的关系。

开始延迟对活动区间端点的影响示例
开始延迟对活动区间端点的影响示例。
(a) 无延迟的动画效果;动画开始时间与活动区间的开始时间重合。
(b) 正延迟的动画效果;活动区间的开始时间因延迟而推迟。
(c) 负延迟的动画效果;活动区间的开始时间因延迟而提前。

还可以指定结束延迟,但主要用于动画的顺序。

动画效果定义了一个活动区间, 这是动画安排产生效果的时间段,填充模式的例外,它适用于活动区间之外。

活动区间的下限由开始延迟定义。

开始延迟是一个动画效果与其关联的动画动画开始时间之间的签名偏移量。

活动区间的长度称为活动持续时间,其计算方法定义在§ 4.8.2 计算活动持续时间

开始延迟类似,动画效果也有一个结束延迟, 主要用于基于另一个结束时间的动画排序。 尽管这通常仅在结合顺序效果时有用,这些效果在本规范的后续版本中介绍,但在此包含它是为了表示SVG中的min属性([SVG11],第19章)。

结束时间是通过评估max(开始延迟 + 活动持续时间 + 结束延迟, 0)得出的。

4.5.4. 本地时间

本地时间是指某一时刻动画效果的时间,基于以下第一个匹配条件:

如果动画效果与动画相关联

本地时间为该动画的当前时间

否则,

本地时间为未解析

4.5.5. 动画效果的阶段与状态

本节为非规范性内容

在某一时刻,动画效果可能处于三个可能的阶段之一。 如果一个动画效果具有未解析本地时间,则它将不会处于任何阶段。

不同阶段如下图所示。

描述动画效果的不同阶段和状态的示例
描述动画效果的不同阶段和状态的示例。

这些阶段如下:

之前阶段

动画效果局部时间落在效果的活动区间结束时间之前,或者发生在负启动延迟生效期间的范围内。

活动阶段

本地时间落在活动区间内, 且不在负开始延迟或负结束延迟的范围内时,动画效果处于“活动”阶段。

之后阶段

本地时间落在活动区间之后,或在由于负结束延迟而导致的更早的结束时间之后(但不在负开始延迟的范围内)时,动画效果处于“之后”阶段。

除了这些阶段外,动画效果还可以被描述为处于几个重叠的状态之一。 这些状态仅在单个动画帧的持续时间内确立,主要用于描述模型的静态部分。

这些状态及其在模型中的使用总结如下:

播放中

对应于动画效果,其活动时间在每帧上都会改变。

当前

对应于动画效果,它要么播放中,要么可能根据其动画的当前播放速率在未来进入播放中状态。

有效

对应于具有已解析活动时间动画效果。 这种情况发生在动画效果处于活动阶段或处于活动阶段之外,但在其填充模式(见§ 4.6 填充行为)生效时。 只有处于有效状态的动画效果才会对其目标产生结果。

每种状态的规范定义如下。

确定动画效果的阶段需要以下定义:

动画方向

如果效果与动画相关联且关联的动画的播放速率小于零,则为“向后”; 在所有其他情况下,动画方向为“向前”。

活动前边界时间

max(min(开始延迟, 结束时间), 0)

活动后边界时间

max(min(开始延迟 + 活动持续时间, 结束时间), 0)

如果动画效果的本地时间未解析,并且满足以下任一条件,则动画效果处于之前阶段

  1. 本地时间小于活动前边界时间,或

  2. 动画方向为“向后”,且本地时间等于活动前边界时间

如果动画效果的本地时间未解析,并且满足以下任一条件,则动画效果处于之后阶段

  1. 本地时间大于活动后边界时间,或

  2. 动画方向为“向前”,且本地时间等于活动后边界时间

如果动画效果的本地时间未解析,并且不处于之前阶段之后阶段,则动画效果处于活动阶段

此外,将动画效果处于上述任何阶段之外的情况称为处于空闲阶段

如果满足以下所有条件,则动画效果处于播放中状态:

  1. 动画效果处于活动阶段,且

  2. 动画效果与动画相关联,且动画未处于完成状态。

如果满足以下任何条件,则动画效果处于当前状态:

如果动画效果的活动时间未解析,则动画效果处于有效状态,按§ 4.8.3.1 计算活动时间中的过程计算。

4.5.6. 相关动画

我们可以根据与其关联的动画效果,将一个动画定义为相关动画。

如果满足以下条件,则一个动画相关动画:

元素或伪元素target相关动画是包含至少一个其效果目标target动画的集合。

元素、伪元素、文档影子根target的子树的相关动画是包含至少一个其效果目标target动画的集合,targettarget或伪元素的包含的后代(如果target是一个文档影子根,则为后代)的集合。

4.6. 填充行为

动画效果在播放时,其效果由其填充模式决定。

可能的填充模式有:

这些模式的规范定义包含在活动时间的计算中,详见§ 4.8.3.1 计算活动时间

建议作者避免使用填充模式来创建其效果被无限期应用的动画。填充模式的引入是为了表示CSS动画定义的animation-fill-mode属性[CSS-ANIMATIONS-1]。然而,它们会导致动画状态无限期累积,从而需要根据§ 5.5 替换动画中定义的自动移除动画。此外,无限填充动画可能会在所有动画完成后很长时间内导致指定样式无效,因为动画样式在CSS级联中优先[css-cascade-3]

在可能的情况下,作者应优先直接在指定样式中设置动画的最终状态。这可以通过等待动画完成后更新样式来实现,如下所示:

// 在以下动画完成后的第一帧中,`finished`  promise 的回调函数会在样式更新之前运行,因此不会出现闪烁。
elem.animate({ transform: 'translateY(100px)' }, 200).finished.then(() => {
  elem.style.transform = 'translateY(100px)';
});

或者,作者可以在动画开始时设置指定样式,然后从原始值开始动画,如下所示:

elem.style.transform = 'translateY(100px)';
elem.animate({ transform: 'none', offset: 0 }, 200);

涉及叠加多个动画的复杂效果可能需要临时使用向前填充模式,以捕获动画的最终值,然后取消它。例如:

elem.addEventListener('click', async evt => {
  const animation = elem.animate(
    { transform: `translate(${evt.clientX}px, ${evt.clientY}px)` },
    { duration: 800, fill: 'forwards' } 
  ); 
  await animation.finished; 
  // commitStyles 将记录包括 `animation` 在内的样式,并使用结果更新 elem 的指定样式。
  animation.commitStyles(); 
  animation.cancel(); 
});

4.6.1. 填充模式

本节为非规范性内容

每种填充模式的效果如下:

none

当动画效果不在播放时,它没有任何效果。

forwards

当动画效果处于后阶段时, 动画效果将产生与最后一次播放中时相同的迭代进度值。

在动画效果不在播放的其他所有时间内, 它将不会产生任何效果。

backwards

当动画效果处于前阶段时, 动画效果将产生与最早一次播放中时相同的迭代进度值。

在动画效果不在播放的其他所有时间内, 它将不会产生任何效果。

both

当动画效果处于前阶段时, 使用backwards填充行为。

当动画效果处于后阶段时, 使用forwards填充行为。

以下插图展示了这些填充模式的一些示例。

不同填充模式及其产生的状态示例。
不同填充模式及其产生的状态示例。
(a) 填充模式“none”。动画效果在其活动阶段之外没有效果。
(b) 填充模式“forwards”。在活动阶段结束后,迭代进度值继续保持填充值。
(c) 填充模式“backwards”。动画效果在活动阶段开始之前产生填充值。
(d) 填充模式“both”。在活动阶段之前和之后,动画效果都产生填充值。

注意:设置填充模式不会影响活动区间的端点或阶段之间的边界。 但是,填充模式确实会影响时间模型的其他属性,因为动画效果的活动时间仅在活动阶段应用填充时被定义(即不是未解决)。

4.7. 重复

4.7.1. 迭代区间

可以指定动画效果重复一定次数或无限次。 这种重复发生在活动区间之内。 单次重复发生的时间跨度称为迭代区间

活动区间不同,动画效果可以有多个迭代区间,尽管通常只有与当前迭代对应的区间是关注的重点。

单次迭代的时间长度称为迭代持续时间。 动画效果的初始迭代持续时间为零。

本节为非规范性内容

比较迭代持续时间活动持续时间

迭代持续时间

动画效果完成单次迭代所需的时间。

活动持续时间

整个动画效果完成所需的时间,包括重复部分。 这可能比迭代持续时间更长或更短。

以下插图展示了迭代持续时间活动持续时间之间的关系。

迭代持续时间与活动时间的比较。
一个将迭代持续时间活动持续时间进行比较的示例,迭代次数为2.5。 注意,最后一次迭代的迭代持续时间并没有改变,只是被活动持续时间截断了。

4.7.2. 控制迭代

动画效果重复的次数称为其迭代次数迭代次数是一个大于或等于零的实数。 迭代次数也可以是正无穷大,以表示动画效果无限重复。

除了迭代次数动画效果还具有迭代开始属性,用于指定动画效果应从一系列迭代中的哪个偏移位置开始。 迭代开始是一个大于或等于零的有限实数。

这些参数的行为在§ 4.8 核心动画效果计算中定义。

本节为非规范性内容

迭代次数迭代开始参数的效果如下图所示。

迭代次数和迭代开始参数的效果
迭代次数迭代开始参数的效果。
第一个例子中,迭代次数为2.5,导致第三次迭代在其迭代区间的一半时被截断。
第二个例子与第一个相同,但具有迭代开始0.5。 这导致动画效果从第一次迭代的一半处开始。

迭代次数参数不同,迭代开始参数不会影响活动持续时间的长度。

注意,迭代开始值大于或等于1的情况通常没有用处,除非与具有迭代复合操作累加动画效果结合使用。

4.7.3. 迭代时间空间

本节为非规范性内容

在Web动画中,所有时间都是相对于某个参考点的。 不同的参考点产生不同的时间空间

这可以与计算机图形学中使用的坐标空间进行比较。 时间空间的零时刻类似于坐标空间的原点。

我们可以描述重复的动画在每次动画重复时建立一个新的时间空间:迭代时间空间

迭代时间空间是一个时间空间,其零时刻是动画效果当前迭代的开始。

在Web动画模型中,我们还引用了活动时间,它是相对于活动区间开始的时间。 然而,这个时间空间是模型的内部内容,在编程接口或标记中并不公开。

以下图例展示了这些时间空间。

本地时间、活动时间和迭代时间的比较。
一个动画的本地时间、活动时间和迭代时间的比较,其迭代持续时间为1秒,迭代次数为2.5。

注意: 虽然时间空间本身是无限的,但Web动画定义了活动时间迭代进度,使它们被限制在如图所示的范围内。 例如,尽管-1秒的时间在活动时间空间中是有效时间,但在§ 4.8.3.1 计算活动时间中定义的计算活动时间的过程将永远不会返回负值。

除了这些时间空间,我们还可以引用文档时间空间,这是默认文档时间轴时间值的时间空间,该时间轴属于当前全局对象的Document

4.7.4. 区间定时

本节为非规范性内容

当动画效果重复时,我们必须定义迭代边界上的行为。 对于此行为,实际上对于所有区间定时,Web动画使用的是端点排他性定时模型。 这意味着虽然区间的开始时间包含在区间内,但结束时间不包含在区间内。 在区间表示法中,这可以写成[begin, end)。 这种模型在区间重复和顺序排列时提供了合理的行为,因为区间之间没有重叠。

在下图中,对于重复效果,在本地时间1秒时,迭代时间为0。 对于顺序动画,在时间轴时间1秒时,只有动画B的关联效果在播放中;边界之间没有重叠。

端点排他性定时的示例。
端点排他性定时的示例。对于重复和顺序动画效果,在区间边界之间没有重叠。

这种行为的一个例外是,当执行填充时,如果填充开始于区间端点,则使用该端点。 此行为源自于§ 4.8.3.3 计算简单迭代进度中给出的算法,并在下图中进行了说明。

迭代和填充对迭代时间的影响。
在一次迭代后,迭代进度为0,但在两次迭代后(以及之后),由于定义的动画效果填充的特殊行为,迭代进度为1。

4.8. 核心动画效果计算

4.8.1. 概述

本节为非规范性内容

Web动画定时模型的核心是将本地时间值转换为迭代进度的过程。

此过程的第一步是计算由活动持续时间决定的活动区间的边界。

此过程如下图所示。

活动持续时间的计算。
活动持续时间的计算基于将迭代持续时间乘以迭代次数

活动持续时间的计算§ 4.8.2 计算活动持续时间中有规范性定义。

在确定了活动持续时间,将动画效果本地时间转换为转换进度(即迭代进度)的过程如下图所示。

定时模型计算概览。
定时模型计算概览。
(1) 本地时间由关联的动画确定。
(2) 本地时间通过结合开始延迟转换为活动时间
(3) 活动时间通过结合迭代持续时间迭代开始属性分割以产生整体进度
(4) 然后,将整体进度时间转换为单个迭代内的偏移量:简单迭代进度
(5) 简单迭代进度通过结合播放方向转换为定向进度
(6) 最后,将定时函数应用于定向进度以产生转换进度

第一步,计算本地时间§ 4.5.4 本地时间中描述。 图中的第2至4步在以下各节中描述。 第5和第6步分别在§ 4.9.1 计算定向进度§ 4.10.1 计算转换进度中描述。

4.8.2. 计算活动持续时间

活动持续时间的计算如下:

活动持续时间 = 迭代持续时间 × 迭代次数

如果迭代持续时间迭代次数为零,那么活动持续时间也为零。

这一说明是必要的,因为根据IEEE 754-2008,零乘以无穷大的结果是未定义的。

4.8.3. 转换本地时间

4.8.3.1. 计算活动时间

活动时间基于本地时间开始延迟。 然而,它只有在动画效果应该产生输出时才定义,因此它依赖于填充模式和以下各阶段:

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

结果取决于以下第一个匹配条件:

如果填充模式backwardsboth

返回以下计算结果: max(本地时间 - 开始延迟, 0)

否则,

返回未解析的时间值

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

返回以下计算结果: 本地时间 - 开始延迟

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

结果取决于以下第一个匹配条件:

如果填充模式forwardsboth

返回以下计算结果:max(min(本地时间 - 开始延迟, 活动持续时间), 0)

否则,

返回未解析的时间值

否则(本地时间未解析),

返回未解析的时间值

4.8.3.2. 计算整体进度

整体进度 描述了已完成的迭代次数(包括部分迭代),其定义如下:

  1. 如果活动时间未解析的,则返回未解析

  2. 根据以下第一个匹配条件计算整体进度的初始值,

    如果迭代持续时间为零,

    如果动画效果处于前置阶段,则整体进度为零,否则,为迭代次数

    否则,

    整体进度等于以下计算结果 活动时间 / 迭代持续时间

  3. 返回以下计算结果 整体进度 + 迭代起始点

4.8.3.3. 计算简单迭代进度

简单迭代进度是当前迭代的进度百分比, 忽略播放方向或应用于效果的时间函数引入的时间变换,其计算方法如下:

  1. 如果整体进度未解析的, 返回未解析

  2. 如果整体进度是无穷大,令简单迭代进度等于 迭代起始点 % 1.0, 否则,令简单迭代进度等于 整体进度 % 1.0

  3. 如果所有以下条件为真,

    简单迭代进度为1.0。

    上述步骤实现了这样一种行为:当动画的活动区间恰好在迭代结束时结束时,它通过保持最后一次迭代的端点而不是下一次迭代的开始点来填充。

    最后一个条件防止在迭代次数为零的情况下应用此行为,因为动画从未播放任何迭代。

  4. 返回简单迭代进度

4.8.4. 计算当前迭代

当前迭代 可以通过以下步骤计算:

  1. 如果活动时间未解析的,返回未解析

  2. 如果动画效果处于后置阶段迭代次数 为无穷大,返回无穷大。

  3. 如果简单迭代进度为1.0, 返回 floor(整体进度) - 1

  4. 否则,返回 floor(整体进度)

4.9. 方向控制

动画效果还可以通过方向控制来配置以不同方向运行迭代。 为此,动画效果具有一个播放方向参数,该参数取以下值之一:

这些值的语义被纳入到随后对定向进度的计算中。

本节为非规范性内容

这些值的非规范性定义如下:

正常(normal)

所有迭代按指定方式播放。

反向(reverse)

所有迭代按与指定方式相反的方向播放。

交替(alternate)

偶数迭代按指定方式播放,奇数迭代按与指定方式相反的方向播放。

交替反向(alternate-reverse)

偶数迭代按与指定方式相反的方向播放,奇数迭代按指定方式播放。

4.9.1. 计算定向进度

定向进度 通过以下步骤从简单迭代进度计算得出:

  1. 如果简单迭代进度未解析的,返回未解析

  2. 使用以下列表中的第一个匹配条件计算当前方向

    如果播放方向normal

    当前方向为正向。

    如果播放方向reverse

    当前方向为反向。

    否则,
    1. d当前迭代

    2. 如果播放方向alternate-reverse,则将d加1。

    3. 如果d % 2 == 0,令当前方向为正向,否则令 当前方向为反向。 如果d为无穷大,令当前方向为正向。

  3. 如果当前方向为正向,则返回 简单迭代进度

    否则,返回 1.0 - 简单迭代进度

4.10. 时间变换

通常需要控制动画效果的进展速度。 例如,通过缓动动画速度可以创造出动量感并产生更自然的效果。 CSS 缓动函数模块[CSS-EASING-1] 定义了用于此目的的缓动函数

动画效果关联有一个 缓动函数。 默认的缓动函数线性缓动函数

4.10.1. 计算变换进度

变换进度 通过以下步骤从定向进度计算得出:

  1. 如果定向进度未解析的, 返回未解析

  2. 按以下步骤计算之前标志的值:

    1. 使用§ 4.9.1 计算定向进度中定义的过程确定当前方向

    2. 如果当前方向正向,则前进中为真,否则为假。

    3. 如果动画效果处于前置阶段前进中为真;或 如果动画效果处于后置阶段前进中为假,则设置之前标志

  3. 返回评估动画效果缓动函数的结果, 传递定向进度作为输入进度值,并传递之前标志作为之前标志

4.11. 迭代进度

动画效果迭代进度 就是其变换进度

5. 动画模型

本节为非规范性内容

对于某些类型的动画效果,Web 动画动画模型会采用时间模型生成的迭代进度当前迭代值,并使用这些值来计算相应的输出。

每个这样的动画效果的输出会通过效果栈与其他效果的输出结合,然后应用到目标属性上(参见§ 5.4 结合效果)。

5.1. 简介

一个动画效果有零个或多个与之关联的属性,这些属性会响应其时间输出的变化而受到影响。这些属性被称为效果的目标属性

给定一个迭代进度当前迭代和一个基础值,一个动画效果会通过应用适用于该属性的动画类型的程序来为每个可动画的 目标属性生成一个效果值

5.2. 动画属性

除非另有说明,否则所有 CSS 属性都是可动画的。 每个属性的属性定义表中通过动画类型行定义了属性值如何组合:

不可动画
该属性不可动画。 在动画关键帧中列出时不会处理, 并且不会受过渡效果影响。

注意:属性通常被排除在动画之外, 因为对其进行动画处理会导致过于复杂的情况。 例如,定义动画参数的属性是不可动画的, 因为这样做会导致复杂的递归行为。

注意:仅针对不可动画属性的动画效果仍然会表现出通常的动画效果行为,例如触发事件和 延迟动画当前完成的 promise的实现。

离散的
该属性的值无法有意义地组合, 因此它是不可加的插值在 50% 时从Va切换到Vb,即
V result = V start 当  p < 0.5 V end 当  p 0.5
按计算值
相应的计算值组件根据该值类型 使用指示的过程进行组合(插值、添加或累积) (参见CSS Values 4 § 3 组合值:插值、添加和累积)。 如果组件数量或相应组件的类型不匹配, 或者如果任何组件值使用离散的动画, 并且两个对应的值不匹配, 那么属性值将按离散的方式组合。
可重复列表
按计算值相同,除了当两个列表项数量不同时, 它们首先会重复到最小公倍数的项数。 然后,每一项按照按计算值进行组合。 如果一对值无法组合 或者任何组件值使用离散的动画, 那么属性值将按离散的方式组合。

注意:可重复列表概念确保了 一个概念上重复到某个长度的列表 (如background-origin会重复到 background-image列表的长度) 或无限重复的列表 将在任何值之间平滑过渡, 并且计算值 将正确表示结果 (并有可能被正确继承)。

(参见详细说明)
某些属性具有上述情况未涵盖的特定插值行为; 在这种情况下,将明确指定该属性的动画行为。

尚未包含动画类型行的属性的 动画类型在其属性定义中, 见附录 A:现有属性的动画类型中定义。

5.2.1. 自定义属性

对于使用registerProperty()方法为 当前全局对象 注册的自定义属性, 其动画类型按计算值, 派生自属性的语法定义中使用的类型。 如果没有与属性的指定语法 (例如当语法为通用语法定义)对应的计算值类型, 或者当自定义属性未注册时, 该动画类型离散的

5.3. 关键帧效果

关键帧效果是一种动画效果,它利用时间模型的输出来更新元素或伪元素(如::before::after[select])的 CSS 属性, 这些元素或伪元素称为效果目标

效果目标包括一个称为元素的对象, 称为目标元素和一个伪元素选择器, 称为目标伪选择器。 如果效果目标元素, 则目标元素是该元素, 而目标伪选择器null。 如果效果目标伪元素, 则目标元素是其源元素,而目标伪选择器则根据需要指定该伪元素。

请注意,并非所有以这种方式指定的效果目标(如::part()伪元素和不支持的伪元素)都有定义的计算属性值。

5.3.1. 关键帧

效果值是通过在一系列按分数偏移的位置插值 来为关键帧效果计算出来的。 每组按偏移索引的属性值称为关键帧

关键帧的偏移是一个范围在 [0, 1] 内的值,或特殊值 null。 关键帧列表必须按偏移松散排序, 这意味着列表中每个关键帧关键帧偏移如果不为 null, 则该偏移大于或等于列表中前一个关键帧的偏移。

关键帧重叠或具有不支持的值时的行为 定义在§ 5.3.4 关键帧效果的效果值中。

每个关键帧还有一个与之关联的缓动函数, 它应用于指定它的关键帧和列表中下一个关键帧之间的时间段。 列表中最后一个关键帧上指定的缓动函数永远不会被应用。

每个关键帧可能有一个关键帧特定的组合操作, 如果设置了,则它会应用于该关键帧中指定的所有值。 可能的操作及其含义与关键帧效果整体关联的组合操作§ 5.4.4 效果组合中定义的操作相同。 如果关键帧的关键帧特定的组合操作未设置,则使用为该关键帧中指定的值定义的关键帧效果整体上的组合操作

5.3.2. 计算属性值

计算属性值,给定属性property, 值value,和元素 element: 根据property的定义表中的"计算值"行解析value, 使用element计算值作为解析依赖项的上下文,并返回结果。

注意:element上的计算值不受 此算法影响。

此算法意味着在关键帧中指定的属性值可以建立顺序依赖。 在计算属性值时,value持有的依赖项的计算值必须 首先计算。

var animation = elem.animate([{ fontSize: '10px', width: '10em' },
                              { fontSize: '20px', width: '20em' }], 1000);
animation.currentTime = 500;
console.log(getComputedStyle(elem).fontSize); // 应为 15px
console.log(getComputedStyle(elem).width); // 应为 225px

在此示例中,为了计算属性值10em的值, 我们必须知道计算值font-size目标元素上的计算值, 而这又由font-size效果值决定, 这又要求我们计算font-size的属性值。 因此,计算属性值受制于顺序约束。

5.3.3. 计算已计算的关键帧

在计算效果值之前,关键帧效果关键帧上的属性值将先被 计算,并计算任何关键帧的空关键帧偏移的偏移量。解析这些值的结果是一组 已计算的关键帧

计算出的一组关键帧偏移,其中包含了每个空关键帧偏移的适当值,称为 已计算的关键帧偏移

要生成已计算的关键帧偏移,我们定义了一个过程来 计算缺失的关键帧偏移,该过程接受一系列 关键帧 keyframes,并包含以下步骤:

  1. 对于keyframes中的每个关键帧, 将该关键帧已计算的关键帧偏移设为其关键帧偏移值。

  2. 如果keyframes包含多个关键帧,并且keyframes中第一个 关键帧已计算的关键帧偏移为null, 则将该关键帧已计算的关键帧偏移设为0。

  3. 如果keyframes中最后一个关键帧已计算的关键帧偏移为null, 则将其已计算的关键帧偏移设为1。

  4. 对于每对AB之间的关键帧

    计算AB之间每个关键帧已计算的关键帧偏移,如下:

    1. offsetk为关键帧k已计算的关键帧偏移

    2. nAB之间包含AB的关键帧数量减去1。

    3. index表示在AB之间的关键帧序列中的位置,第一个A之后的关键帧的index为1。

    4. 将关键帧keyframe已计算的关键帧偏移设为 offsetA + (offsetBoffsetA) × index / n

已计算的关键帧是使用以下过程生成的。 注意,此过程仅对具有效果目标关键帧效果执行,其中可以计算出计算属性值。

  1. computed keyframes成为一个空的关键帧列表。

  2. 对于在此关键帧效果上指定的 关键帧列表中的每个keyframe,执行以下步骤:

    1. 将一个新的空的关键帧computed keyframe,添加到computed keyframes

    2. 对于keyframe中指定的每个属性:

      例如,如果keyframeborder-width 属性的值为“12pt”,用户代理可能会计算出每个长格式属性的“16px”值:border-bottom-widthborder-left-widthborder-right-width, 和border-top-width。 因此,computed keyframe将不会有属性 border-width的值,而是包括每个长格式属性,并且每个属性的值均为“16px”。

      如果在展开简写属性或使用物理属性替换逻辑属性时出现冲突,请按以下顺序应用规则,直到解决冲突:

      1. 长格式属性覆盖简写属性(例如border-top-color覆盖border-top)。

      2. 包含较少长格式组件的简写属性覆盖包含较多长格式组件的简写属性(例如border-top覆盖border-color)。

      3. 物理属性覆盖逻辑属性。

      4. 对于具有相同数量的长格式组件的简写属性,当按组成每个IDL名称的Unicode代码点升序排序时,IDL名称较早出现的属性将覆盖较晚出现的属性。

  3. 计算缺失的关键帧偏移过程应用于 computed keyframes

  4. 返回computed keyframes

5.3.4. 关键帧效果的效果值

单个属性的效果值关键帧效果引用,作为其 目标属性之一,对于给定的迭代进度当前迭代基础值,计算如下。

  1. 如果迭代进度未解析,则中止此过程。

  2. 目标属性成为要计算其效果值的长格式属性。

  3. 如果目标属性动画类型不可动画化,则中止此过程,因为无法应用该效果。

  4. 如果关键帧效果没有效果目标,或者如果效果目标无法计算出属性值,则中止此过程。

  5. 定义组合的中性值为一个值,当与基础值使用add 组合操作进行组合时,生成基础值

  6. 特定属性的关键帧为获取此关键帧效果已计算关键帧的结果。

  7. 特定属性的关键帧中移除没有目标属性的属性值的关键帧

  8. 如果特定属性的关键帧为空,则返回基础值

  9. 如果特定属性的关键帧中没有已计算的关键帧偏移为0的关键帧, 则创建一个新的关键帧,其已计算的关键帧偏移为0,属性值设置为组合的中性值, 并将其组合操作设置为add,并将其添加到特定属性的关键帧的开头。

  10. 同样,如果特定属性的关键帧中没有已计算的关键帧偏移为1的关键帧, 则创建一个新的关键帧,其已计算的关键帧偏移为1,属性值设置为组合的中性值, 并将其组合操作设置为add,并将其添加到特定属性的关键帧的末尾。

  11. 区间端点为一个空的关键帧序列。

  12. 通过以下条件中的第一个匹配条件来填充区间端点

    如果迭代进度 < 0,且在特定属性的关键帧中有多个关键帧已计算关键帧偏移为0,

    特定属性的关键帧中的第一个关键帧添加到区间端点中。

    如果迭代进度 ≥ 1,且在特定属性的关键帧中有多个关键帧已计算关键帧偏移为1,

    特定属性的关键帧中的最后一个关键帧添加到区间端点中。

    否则,
    1. 特定属性的关键帧已计算关键帧偏移小于等于迭代进度且小于1的最后一个关键帧添加到区间端点中。 如果没有这样的关键帧(例如,迭代进度为负),则添加已计算关键帧偏移为0的最后一个关键帧

    2. 将上一步中添加的关键帧后的下一个关键帧添加到区间端点中。

  13. 对于区间端点中的每个关键帧

    1. 如果关键帧组合操作不是replace,或关键帧没有组合操作且此关键帧效果组合操作不是replace, 则执行以下步骤:

      1. 使用的组合操作关键帧组合操作,如果没有,则为此关键帧效果组合操作

      2. 要组合的值关键帧上指定的目标属性的属性值。

      3. 目标属性的属性值替换为基础值Va)与要组合的值Vb)的组合结果,使用对应于目标属性动画类型使用的组合操作的过程。

  14. 如果区间端点中只有一个关键帧,则返回该关键帧上目标属性的属性值。

  15. 起始偏移区间端点中第一个关键帧的已计算关键帧偏移

  16. 结束偏移区间端点中最后一个关键帧的已计算关键帧偏移

  17. 区间距离为以下结果:(迭代进度 - 起始偏移) / (结束偏移 - 起始偏移)

  18. 变换后的距离为以下结果:使用区间距离作为输入进度,计算与区间端点中第一个关键帧关联的时间函数

  19. 返回以下结果:应用由目标属性动画类型定义的插值过程, 对区间端点中的两个关键帧指定的目标属性的值,使用第一个这样的值作为Vstart,使用第二个值作为Vend, 并使用变换后的距离作为插值参数p

请注意,此过程对效果指定的关键帧列表假定以下内容:

确保这些条件得到满足的责任在于模型的用户(例如,声明性标记或编程接口)。

例如,对于本规范定义的编程接口,这些条件通过生成输入此过程的已计算关键帧的过程得到满足。

注意:此过程允许关键帧重叠。 在重叠点上的行为是输出值跳转到该偏移量处定义的最后一个关键帧的值。 对于在0或1处重叠的关键帧,输出值对于迭代进度值小于0或大于或等于1的情况,分别是关键帧中的第一个关键帧或最后一个关键帧的值。

请注意,已计算的关键帧是“动态”的: 用户代理必须表现得好像它们在每次计算效果值时都会被重新创建。

例如,如果在font-size属性上存在从10px20px的进行中的过渡, 则在关键帧中指定为1em的属性值在关键帧计算期间会针对font-size属性的范围[10px, 20px]内的计算值进行解析。

在某些时间函数的存在下,动画效果的输入迭代进度不受限于范围[0, 1]。 然而,目前关键帧偏移量确实限制在范围[0, 1],并且属性值对于范围之外的输入迭代进度值只是简单地外推。

我们曾考虑删除此限制,因为存在一些情况,其中在[0, 1]范围之外指定非线性属性值变化是有用的。 一个例子是一个从绿色插值到黄色的动画,但具有一个使其暂时插值“超越”黄色到红色然后再回到黄色的超调时间函数。 虽然这种效果可以通过修改关键帧和时间函数来实现,但这种方法似乎破坏了模型中的时间问题与动画效果的分离。

目前尚不清楚应该如何实现这种效果,但我们注意到,允许关键帧偏移超出[0, 1]范围可能使当前指定的行为(必要时合成偏移为0和1的关键帧)不一致。

参见2013年东京F2F会议讨论的第4节([0, 1]范围之外的关键帧偏移)

<https://github.com/w3c/csswg-drafts/issues/2081>

5.4. 组合效果

本节是非规范性的

在为关键帧效果计算效果值后,它们将应用于动画效果目标属性

由于多个生效的关键帧效果可能会针对同一属性,因此通常需要将多个关键帧效果的结果组合在一起。这一过程称为合成,它基于为每个由生效的动画效果目标的属性建立的效果栈

在将关键帧效果的结果组合在一起后,合成的结果将与目标属性的其他指定值相结合。

该安排如下图所示:

效果值应用于目标属性的概览
效果值应用于目标属性的概览。
针对同一属性的关键帧效果的结果通过效果栈组合在一起。
该组合的结果随后在适当的位置插入 CSS 层叠。

此操作的第一部分——组合针对相同属性效果值——需要确定关键帧效果彼此结合的方式,以及它们应用的顺序,即它们的相对合成顺序。

效果值的结合方式由相应关键帧效果的合成操作决定。

效果值的相对合成顺序由为每个动画属性建立的效果栈决定。

5.4.1. 动画类

本规范提供了一个通用的动画模型,旨在供其他规范在此模型之上定义标记或编程接口。生成动画的特定标记或编程接口定义其动画类

其他规范可能会定义不同动画类之间或特定类内的复合顺序的特定行为。

本节是非规范性的

例如,为“CSS 动画”的动画被定义为比类为“CSS 过渡”的动画具有更高的复合顺序,但比没有特定类的其他动画顺序低。

在“CSS 动画”对象集中,基于animation-name属性等因素定义了特定的复合顺序。

5.4.2. 效果栈

与每个由一个或多个关键帧效果目标的属性关联的效果栈建立了关键帧效果的相对复合顺序。

在一个效果栈中,任何两个关键帧效果AB的相对复合顺序通过比较其属性来建立,具体如下:

  1. 动画效果的关联动画定义为与动画效果关联的动画

  2. 按照以下条件依次排序AB,直到确定顺序:

    1. 如果AB关联动画上不同,则按为相应类定义的任何类间复合顺序进行排序。

    2. 如果AB仍未排序,则按AB的关联动画的共同类定义的任何类特定复合顺序进行排序。

    3. 如果AB仍未排序,则按其关联动画在全局动画列表中的位置进行排序。

排序靠前的动画效果具有更低的复合顺序。

5.4.3. 计算效果栈的结果

为了计算效果栈的最终值,将栈中每个关键帧效果效果值按复合顺序依次组合。

在评估效果栈的过程中,每一步都将以基础值作为输入。

对于栈中的每个关键帧效果,从该关键帧效果中选择适当的效果值基础值结合,生成新值。这个结果值成为栈中下一个关键帧效果组合的基础值

效果栈的最终值称为合成值,它只是将栈中最后一个(复合顺序最高的)关键帧效果效果值与此时的基础值组合的结果。

5.4.4. 效果合成

效果值基础值结合使用的具体操作由产生此效果值关键帧效果合成操作决定。

本规范定义了三种合成操作,如下:

替换

效果值基础值合成的结果仅为效果值

添加

效果值添加基础值

对于动画类型而言,如果添加操作被定义为非交换的,则操作数的顺序为基础值 + 效果值

累积

效果值累积基础值

对于动画类型,如果累积操作被定义为非交换的,则操作数的顺序为基础值随后为效果值

5.4.5. 应用合成结果

合成值应用于目标属性通过将指定值添加到 CSS 层叠来实现。

添加此指定值的层叠级别取决于与效果栈中复合顺序最高的效果关联的动画的

默认情况下,指定值添加到 CSS 层叠的“动画声明”级别([css-cascade-3])。

本节是非规范性的

例如,如果复合顺序最高的效果与“CSS 过渡”类动画相关联,则合成值将添加到层叠的“过渡声明”级别。

为 CSS 目标属性计算的合成值使用以下过程进行应用。

  1. 将属性的基础值计算为在没有动画的情况下为该属性生成的计算值。

  2. 为属性建立效果栈(参见 § 5.4.2 效果栈)。

  3. 计算效果栈的合成值,将属性的基础值作为初始基础值传入(参见 § 5.4.3 计算效果栈的结果)。

  4. 合成值插入到 CSS 层叠中,在为与效果栈顶部效果关联的动画类定义的级别插入。

5.5. 替换动画

本节是非规范性的

使用本规范定义的编程接口,可以不断触发新的动画,使其无限期地对元素的动画样式产生影响。

例如,考虑以下代码:

elem.addEventListener('mousemove', evt => {
  circle.animate(
    { transform: `translate(${evt.clientX}px, ${evt.clientY}px)` },
    { duration: 500, fill: 'forwards' }
  );
});

这段代码将在每次鼠标移动时生成一个新的前进填充动画,迅速产生数百甚至数千个前进填充动画。

如果用户代理需要保留所有这些动画,动画列表将无限制增长,导致内存泄漏。

本节定义了一种机制,使被覆盖的动画自动移除,除非作者明确要求保留它们。

5.5.1. 替换状态

动画维护着一个替换状态,该状态可以是以下值之一:

动画的初始替换状态活动状态

如果动画替换状态移除状态,则其动画效果不会包括在其目标属性效果栈中。

5.5.2. 移除被替换的动画

如果满足所有以下条件,动画被认为是可替换的

当请求为文档 doc 移除被替换的动画时,对于每个动画animation,如果:

执行以下步骤:

  1. animation替换状态设置为移除状态

  2. 创建一个AnimationPlaybackEventremoveEvent

  3. removeEvent类型属性设置为remove

  4. removeEventcurrentTime属性设置为animation当前时间

  5. removeEventtimelineTime属性设置为与animation关联的时间线当前时间

  6. 如果animation用于计时的文档,则将removeEvent附加到其待处理动画事件队列中,并附上其目标animation

    否则,排队任务以在animation派发removeEvent。此任务的任务源为DOM 操作任务源

5.6. 动画的副作用

对于至少由一个动画效果且该效果为当前生效状态,并且与动画相关联的属性,且该动画的替换状态移除, 用户代理必须表现得像该元素的will-change属性 ([css-will-change-1]) 包含该属性一样。

本节是非规范性的

根据上述要求,如果动画目标为元素的transform属性,那么只要该效果目标动画处于前阶段活动阶段 或者,如果它的填充模式为“forwards”或“both”,那么它将会创建一个堆叠上下文

6. 编程接口

本节为非规范性内容

除了上面描述的抽象模型之外,Web 动画还定义了一个编程接口来操作该模型。该接口可用于检查和扩展通过声明方式生成的动画,或在过程方法更为合适时直接生成动画。

6.1. 编程接口中的时间值

时间值在编程接口中表示为double类型。未解析的时间值用null表示。

6.2. AnimationTimeline 接口

时间线在 Web 动画 API 中由AnimationTimeline接口表示。

[Exposed=Window]
interface AnimationTimeline {
    readonly attribute double? currentTime;
};
currentTime 类型为 double,只读,可为空

返回该时间线的当前时间,如果该时间线不活跃,则返回null

6.3. DocumentTimeline 接口

文档时间线,包括默认文档时间线,在 Web 动画 API 中由DocumentTimeline接口表示。

dictionary DocumentTimelineOptions {
  DOMHighResTimeStamp originTime = 0;
};

[Exposed=Window]
interface DocumentTimeline : AnimationTimeline {
  constructor(optional DocumentTimelineOptions options = {});
};
originTime 类型为 DOMHighResTimeStamp,默认为0

指定时间线的原点时间,以相对于时间原点的毫秒数表示。

DocumentTimeline (options)

创建一个新的DocumentTimeline。与该时间线关联的Document是与Document关联的Window,该当前全局对象

options

为新创建的时间线配置参数。本规范仅定义了originTime成员,但其他规范可能会扩展此集。

6.4. Animation 接口

动画在 Web 动画 API 中由Animation接口表示。

[Exposed=Window]
interface Animation : EventTarget {
    constructor(optional AnimationEffect? effect = null,
                optional AnimationTimeline? timeline);
             attribute DOMString                id;
             attribute AnimationEffect?         effect;
             attribute AnimationTimeline?       timeline;
             attribute double?                  startTime;
             attribute double?                  currentTime;
             attribute double                   playbackRate;
    readonly attribute AnimationPlayState       playState;
    readonly attribute AnimationReplaceState    replaceState;
    readonly attribute boolean                  pending;
    readonly attribute Promise<Animation>       ready;
    readonly attribute Promise<Animation>       finished;
             attribute EventHandler             onfinish;
             attribute EventHandler             oncancel;
             attribute EventHandler             onremove;
    undefined cancel();
    undefined finish();
    undefined play();
    undefined pause();
    undefined updatePlaybackRate(double playbackRate);
    undefined reverse();
    undefined persist();
    [CEReactions]
    undefined commitStyles();
};
Animation (effect, timeline)

使用以下过程创建一个新的Animation对象。

  1. animation成为一个新的Animation对象。

  2. 运行将设置动画时间轴的过程,传递animation并使用timeline作为新时间轴,或者如果timeline参数缺失,则传递与文档相关联的窗口默认文档时间轴,该窗口是当前全局对象

  3. 运行设置动画关联效果的过程,将source作为新效果传递。

effect

一个可选值,如果不为 null,则指定要分配给新创建的动画关联效果

timeline

一个可选值,如果存在,则指定要与新创建的动画关联的时间线。如果缺失,则使用与Document关联的默认文档时间线Window当前全局对象

id类型为DOMString

用于标识动画的字符串。

effect类型为AnimationEffect,可为空

此动画的关联效果。设置此属性将使用设置动画关联效果的过程更新对象的关联效果

timeline类型为AnimationTimeline,可为空

与此动画关联的时间线。设置此属性将使用设置动画时间线的过程更新对象的时间线

startTime类型为double,可为空

返回此动画的开始时间。设置此属性将使用设置开始时间的过程将对象的开始时间更新为新值。

currentTime类型为double,可为空

此动画的当前时间。设置此属性将遵循设置当前时间的过程,将对象的当前时间更新为新值。

playbackRate类型为double

此动画的播放速度。设置此属性将遵循设置播放速度的过程,将对象的播放速度更新为新值。

设置此属性会同步更新播放速度,这意味着它不会尝试与在单独进程或线程上运行的动画的播放状态同步。因此,为正在播放的动画设置playbackRate可能会导致动画跳跃。

要为正在播放的动画设置播放速度,以便它平滑更新,请使用异步updatePlaybackRate()方法。

playState类型为AnimationPlayState,只读

此动画的播放状态

replaceState类型为AnimationReplaceState,只读

此动画的替换状态

pending类型为boolean,只读

如果此动画有待处理播放任务待处理暂停任务,则返回 true。

ready类型为 Promise<Animation>,只读

返回此对象的当前 ready promise

finished类型为 Promise<Animation>,只读

返回此对象的当前 finished promise

onfinish类型为EventHandler

finish 事件的事件处理程序。

oncancel类型为EventHandler

cancel 事件的事件处理程序。

onremove类型为EventHandler

remove 事件的事件处理程序。

void cancel()

通过运行取消动画的过程来清除此动画引起的所有效果并中止其播放。

void finish()

通过运行完成动画的过程,在当前方向上将动画设置为关联效果结束

DOMException of type InvalidStateError

如果此动画的播放速度为零,或者如果此动画的播放速度 > 零且关联效果结束为无穷大,则引发。

void play()

通过运行播放动画的过程,传递 true 作为自动回放标志的值,开始或恢复动画的播放。

void pause()

通过运行暂停动画的过程暂停此动画的播放。

void updatePlaybackRate(playbackRate)

通过执行无缝更新播放速度的过程,传递playbackRate作为新播放速度,对该动画的播放速度进行异步更新。

playbackRate

指定要使用的更新后播放速度的有限实数。

void reverse()

反转此动画的播放速度并使用反转动画的过程进行播放。与play()类似,此方法会取消动画的暂停状态,并且如果动画已经在反方向播放完毕,则会将其定位到关联效果的开始。

void persist()

将此动画的替换状态设置为已持久化

void commitStyles()

使用提交计算样式的过程,将此动画的动画效果产生的当前效果值写入其对应的效果目标的内联样式中。

与此接口上定义的大多数其他方法不同,调用此方法确实会触发样式更改事件(参见§ 6.13 模型活性)。

由于提交计算样式的过程包括动画的效果值,即使它已移除,此方法对于在动画被替换后保留其效果(参见§ 5.5.2 移除已替换的动画)而不保留实际动画非常有用。

请注意,提交的值是在调用此方法时由动画效果产生的计算值。由于这些值是计算值,它们不会反映上下文的变化,例如响应 CSS 变量的更改或根据计算的 font-size 的更改重新计算 em 单位,就像实时动画产生的值一样。

为了在替换动画后保留填充动画结果的完整保真度(参见§ 5.5 替换动画),可以使用persist()方法,但请注意,这样做会意味着动画继续消耗资源。

要为动画animation提交计算样式

  1. targets为与animation关联动画效果的所有效果目标集合

  2. 对每个集合中的target

    1. 如果target不是可以拥有样式属性[CSS-STYLE-ATTR]的元素(例如,它是一个伪元素或在不定义样式属性的文档格式中的元素),则抛出"NoModificationAllowedError"DOMException并中止这些步骤。

    2. 如果在应用任何待处理的样式更改后,target被渲染,则抛出"InvalidStateError"DOMException并中止这些步骤。

      关于正在渲染的定义 [HTML] 以及 display: contents 仍在 讨论中。在本程序中,我们假设具有 display: contents 且通常会有相关布局框的元素(即它是 连接的,且不属于 display: none 子树的一部分)正在被渲染。

    3. inline style成为获取与target样式属性对应的CSS 声明块的结果。如果target没有样式属性,则令inline style成为一个新的空的CSS 声明块所有者节点设置为target

    4. targeted properties成为物理长手属性的集合,它是至少一个动画效果与动画关联目标属性animation效果目标target

    5. 对于targeted properties中的每个属性,property

      1. partialEffectStack成为targetproperty效果堆栈的副本。

      2. 如果animation替换状态移除的,则将所有动画效果与动画关联,且其效果目标target且包括property作为目标属性,添加到partialEffectStack

      3. partialEffectStack中移除任何其关联动画效果具有比animation更高的合成顺序的效果。

      4. 使用target的计算样式(参见§ 5.4.3 计算效果堆栈的结果),让effect value成为计算partialEffectStack的结果。

      5. 设置 CSS 声明propertyeffect valueinline style中。

    6. 更新inline style的样式属性。

6.4.1. AnimationPlayState 枚举

enum AnimationPlayState { "idle", "running", "paused", "finished" };
idle

对应于空闲播放状态

running

对应于运行中播放状态

paused

对应于暂停播放状态

finished

对应于完成播放状态

6.4.2. AnimationReplaceState 枚举

enum AnimationReplaceState { "active", "removed", "persisted" };
active

对应于活动替换状态

removed

对应于移除替换状态

persisted

对应于持久化替换状态

6.5. AnimationEffect 接口

动画效果在 Web 动画 API 中由抽象AnimationEffect接口表示。

[Exposed=Window]
interface AnimationEffect {
    EffectTiming getTiming();
    ComputedEffectTiming getComputedTiming();
    undefined updateTiming(optional OptionalEffectTiming timing = {});
};
未来,我们可能会公开any onupdate (double? progress, double currentIteration, Animatable? target, any underlyingValue),以便动画效果可以与时间模型分离。
getTiming()

返回此动画效果的指定时间属性。

有关返回的EffectTiming对象的成员与时间模型属性之间的对应关系,请参见EffectTiming接口。

getComputedTiming()

返回此动画效果的计算时间属性。

尽管getTiming()getComputedTiming()返回的对象的某些属性是通用的,但它们的值可能会有所不同:

注意: 未来可能会扩展其他时间成员以包括auto之类的值。进行时间计算时,鼓励作者尽可能使用getComputedTiming(),以避免在允许指定值的范围或类型发生更改时出现不兼容情况。

除了返回值可能的差异之外,与getTiming()相比,getComputedTiming()返回了ComputedEffectTiming字典定义的其他时间信息。

updateTiming(timing)

通过执行更新动画效果的时间属性的过程,传递timing参数作为输入,更新此动画效果的指定时间属性。

optional OptionalEffectTiming timing

要更新的时间属性。timing中不存在的任何成员的时间属性将不会被修改。

remove()方法可用于从其父组或动画中移除效果。我们是否应该将其保留在第一级并仅将其定义为从动画中移除动画效果?[Issue #2082]

6.5.1. EffectTimingOptionalEffectTiming 字典

EffectTiming字典表示AnimationEffect的时间属性。

OptionalEffectTiming字典是EffectTiming字典的变体,允许某些成员不存在exist。它被updateTiming()方法使用,该方法属于AnimationEffect接口,用于对动画效果的时间属性执行增量更新。

dictionary EffectTiming {
    double                             delay = 0;
    double                             endDelay = 0;
    FillMode                           fill = "auto";
    double                             iterationStart = 0.0;
    unrestricted double                iterations = 1.0;
    (unrestricted double or DOMString) duration = "auto";
    PlaybackDirection                  direction = "normal";
    DOMString                          easing = "linear";
};

dictionary OptionalEffectTiming {
    double                             delay;
    double                             endDelay;
    FillMode                           fill;
    double                             iterationStart;
    unrestricted double                iterations;
    (unrestricted double or DOMString) duration;
    PlaybackDirection                  direction;
    DOMString                          easing;
};
delay, 类型为 double,默认值为 0

开始延迟,表示从关联的开始时间活动间隔开始之间的毫秒数。

endDelay, 类型为 double,默认值为 0

结束延迟,表示从动画效果活动间隔结束到其结束时间之间的毫秒数。

fill, 类型为 FillMode,默认值为 "auto"

填充模式,定义了动画效果在其活动间隔之外的行为。

在进行时间计算时,特殊字符串值auto会扩展为时间模型所识别的填充模式之一,如下所示:

如果应用填充模式的动画效果关键帧效果

使用 none 作为填充模式

否则,

使用 both 作为填充模式

§ 4.6 填充行为所述,不建议作者使用无限期填充动画。
iterationStart, 类型为 double,默认值为 0.0

动画效果迭代开始属性,它是一个大于或等于零的有限实数,表示动画效果开始的迭代索引及其在该迭代中的进度。

例如,值为0.5表示动画效果在其第一次迭代的中途开始。值为1.2表示动画效果在其第二次迭代的20%处开始。

请注意,iterations的值实际上是添加iterationStart上的,因此一个iterationStart值为"0.5"且iterations值为"2"的动画效果仍将重复两次,但它将在迭代间隔的中途开始和结束。

iterationStart值大于或等于1通常仅在与具有迭代合成操作累积的动画效果结合使用时,或者当当前迭代索引有其他意义时才有用。

iterations, 类型为 unrestricted double,默认值为 1.0

动画效果迭代计数属性,它是一个大于或等于零的实数(包括正无穷),表示动画效果重复的次数。

可以将此值设置为+Infinity,以使动画效果永远重复(除非效果的持续时间为零,在这种情况下它会立即结束)。

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

迭代持续时间,它是一个大于或等于零的实数(包括正无穷),表示完成动画效果单次迭代所需的时间。

在本规范的这个层次上,字符串值auto在时间模型计算和从getComputedTiming()返回的duration成员的结果中被视为值为零。然而,如果作者指定了auto值,用户代理必须为从getTiming()返回的duration成员返回auto

这是一个前向兼容措施,因为预计本规范的未来版本将引入组效果,其中auto值将扩展为包含子效果的持续时间。

direction, 类型为 PlaybackDirection,默认值为 "normal"

播放方向,定义了动画效果是正向播放、反向播放还是每次迭代交替播放。

easing, 类型为 DOMString,默认值为 "linear"

缓动函数,用于缩放时间以产生缓动效果。

该字符串的语法由 <easing-function> 生成式 [CSS-EASING-1] 定义。

6.5.2. FillMode 枚举

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

无填充。

forwards

向前填充。

backwards

向后填充。

both

前后填充。

auto

无填充。在本规范的后续版本中,这可能会对其他类型的动画效果产生不同的行为。

6.5.3. PlaybackDirection 枚举

enum PlaybackDirection { "normal", "reverse", "alternate", "alternate-reverse" };
normal

所有迭代都按指定的顺序播放。

reverse

所有迭代按指定顺序的反方向播放。

alternate

偶数次迭代按指定顺序播放,奇数次迭代按指定顺序的反方向播放。

alternate-reverse

偶数次迭代按指定顺序的反方向播放,奇数次迭代按指定顺序播放。

6.5.4. 更新AnimationEffect的时间

要从EffectTimingOptionalEffectTiming对象input更新动画效果的时间属性,请执行以下步骤:

  1. 如果inputiterationStart成员存在且小于零,则抛出一个TypeError并中止此过程。

    注意: 之所以使用TypeError而不是RangeError,是为了反映WebIDL的[EnforceRange]注释的行为,假如将来可以将该注释用于浮点值。

  2. 如果inputiterations成员存在,且小于零或为NaN,则抛出一个TypeError并中止此过程。

  3. 如果inputduration成员存在,且小于零或为NaN,则抛出一个TypeError并中止此过程。

  4. 如果 inputeasing 成员 存在,但无法使用 <easing-function> 生成式 [CSS-EASING-1] 解析,则 抛出一个 TypeError 异常并中止此过程。

  5. input存在的每个成员分配给effect的相应时间属性,具体如下:

6.5.5. ComputedEffectTiming 字典

通过时间模型计算出的时间属性通过ComputedEffectTiming字典对象公开。

dictionary ComputedEffectTiming : EffectTiming {
    unrestricted double  endTime;
    unrestricted double  activeDuration;
    double?              localTime;
    double?              progress;
    unrestricted double? currentIteration;
};
endTime, 类型为 unrestricted double

以毫秒为单位表示的结束时间,从本地时间零点算起(即,如果该动画效果与动画关联,则从相关动画开始时间算起)。这对应于结束延迟加上动画效果的活动间隔结束。

activeDuration, 类型为 unrestricted double

活动时长表示此动画效果的持续时间。

localTime, 类型为 double,可为空

本地时间表示此动画效果的本地时间。

如果此动画效果未与动画相关联,则此值为null

progress, 类型为 double,可为空

迭代进度表示此动画效果的当前进度。

currentIteration, 类型为 unrestricted double,可为空

当前迭代的索引,从第一次迭代的零开始。

在大多数情况下,这将是一个正整数。然而,对于一个零持续时间且无限次重复的动画,值将为正的Infinity

与未解析的时间一样,未解析的当前迭代表示为null值。

6.6. KeyframeEffect 接口

关键帧效果KeyframeEffect接口表示。

[Exposed=Window]
interface KeyframeEffect : AnimationEffect {
    constructor(Element? target,
                object? keyframes,
                optional (unrestricted double or KeyframeEffectOptions) options = {});
    constructor(KeyframeEffect source);
    attribute Element?           target;
    attribute CSSOMString?       pseudoElement;
    attribute CompositeOperation composite;
    sequence<object> getKeyframes();
    undefined        setKeyframes(object? keyframes);
};
KeyframeEffect (target, keyframes, options)

使用以下过程创建一个新的 KeyframeEffect 对象:

  1. 创建一个新的 KeyframeEffect 对象,effect

  2. effect目标元素 设置为 target

  3. 目标伪选择器 设置为以下条件的第一个匹配结果。

    如果 options 是一个 KeyframeEffectOptions 对象,

    目标伪选择器 设置为 pseudoElement 属性的值。

    在设置此属性时,应用接口上的 pseudoElement setter 定义的错误处理。 如果 setter 需要抛出异常,则此过程必须抛出相同的异常并中止所有进一步的步骤。

    否则,

    目标伪选择器 设置为 null

  4. timing input 为以下条件的第一个匹配结果。

    如果 options 是一个 KeyframeEffectOptions 对象,

    timing input 设置为 options

    否则(如果 optionsdouble),

    timing input 设置为一个新的 EffectTiming 对象,其中所有成员设置为其默认值,并且 duration 设置为 options

  5. 调用过程 更新动画效果的时序属性,从 timing input 更新 effect

    如果该过程引发异常,则传播异常并中止此过程。

  6. 如果 options 是一个 KeyframeEffectOptions 对象,将 effectcomposite 属性分配为 options 的对应值。

    在分配此属性时,应用接口上对应 setter 定义的错误处理。 如果 setter 需要对 options 指定的值抛出异常,则此过程必须 抛出 相同的异常并中止所有进一步的步骤。

  7. 通过执行为 setKeyframes() 定义的过程,并将 keyframes 作为输入,初始化 关键帧 集。

Element? target

动画的 目标元素。 对于不针对特定元素的动画(例如使用音频 API 生成声音的动画),此值可以为 null

object? keyframes

要使用的 关键帧 集。 此参数的格式和处理在第 6.6.3 节 处理关键帧参数中定义。

optional KeyframeEffectOptions options

指定效果的 迭代时长 的数字,或指定效果的时序和行为的一组属性。

此构造函数的使用示例可在第 6.6.1 节 创建新的 KeyframeEffect 对象中找到。

KeyframeEffect (source)

使用以下过程创建一个与 source 具有相同属性的新 KeyframeEffect 对象:

  1. 创建一个新的 KeyframeEffect 对象,effect

  2. 使用 source 的相应值设置 effect 的以下属性:

    注意:KeyframeEffect(target, keyframes, options) 构造函数不同, 我们不需要重新抛出异常,因为可以假设在 source 上指定的时序属性是有效的。

KeyframeEffect source

要从中复制定义新 关键帧效果 的属性的 关键帧效果

target, 类型为 Element,可为空

此对象动画的 目标元素(如果 效果目标Element,或如果它是伪元素,则为其 源元素)。 对于不针对特定元素的动画(例如使用音频 API 生成声音的动画),此值可以为 null

pseudoElement, 类型为 CSSOMString,可为空

此对象的 目标伪选择器。 如果此效果没有 效果目标效果目标 是一个元素(即不是伪元素),则为 null。 当 效果目标 是伪元素时,此属性指定伪元素选择器(例如 ::before)。

在设置时, 将此对象的 目标伪选择器 设置为提供的值,并应用以下例外情况:

  • 如果提供的值不是 null 且为无效的 <pseudo-element-selector>, 用户代理必须 抛出 一个 DOMException,错误名称为 SyntaxError,并保持此 动画效果目标伪选择器 不变。

    注意: 在此上下文中,无效的伪元素遵循无效选择器的定义,包括语法无效的伪元素以及用户代理不支持的伪元素,均被视为无效。

  • 如果指定了选择器级别 2 中的旧版单冒号伪选择器(':before',':after',':first-letter',或 ':first-line'), 则 目标伪选择器 必须设置为等效的双冒号选择器(例如 '::before')。

composite, 类型为 CompositeOperation

用于将此 关键帧效果效果堆栈 进行组合的 复合操作,如 CompositeOperation 枚举值中指定。

设置时,将此 动画效果复合操作 属性设置为提供的值。

sequence<object> getKeyframes()

返回组成此效果的关键帧及其 计算的关键帧偏移

本节为非规范性内容

此方法的结果是以下格式的对象序列:

dictionary ComputedKeyframe {
    // ... 属性-值对 ...
    // 即 DOMString 属性名称
    double?                  offset = null;
    double                   computedOffset;
    DOMString                easing = "linear";
    CompositeOperationOrAuto composite = "auto";
};

每个成员的含义和值如下:

offset

关键帧偏移,为 关键帧 指定的介于 0.0 到 1.0 之间的数字,或 null

如果 关键帧 将自动在相邻关键帧之间进行间隔,则此值为 null

computedOffset

计算出的 关键帧偏移,为在运行 计算丢失的关键帧偏移 过程时计算的值。

offset 成员不同,computedOffset 永远不会为 null。

easing

用于从此关键帧到系列中的下一个关键帧的时间进度转换的时序函数

composite

用于将此关键帧中指定的值与底层值结合的 关键帧特定的复合操作

如果此 关键帧效果 上指定的 复合操作 正在使用,则此成员将为 auto

由于 关键帧 由部分开放的字典类型表示,目前尚无法使用 WebIDL 表达,故此方法结果的准备过程以如下文字定义:

  1. result 成为一个空的对象序列。

  2. keyframes 为以下之一:

    1. 如果此 关键帧效果CSSAnimation 关联,并且它的 关键帧 尚未被 setKeyframes() 的成功调用替换; 则此 关键帧效果计算的关键帧

    2. 否则,应用过程 计算丢失的关键帧偏移 的结果,此 关键帧效果关键帧

    注意: 我们返回 CSS 动画的 计算的关键帧, 因为并非所有在 CSS 中指定的关键帧都可以用字典表示。

  3. 对于 keyframes 中的每个 keyframe 执行以下步骤:

    1. 使用以下定义初始化一个字典对象 output keyframe

      dictionary BaseComputedKeyframe {
           double?                  offset = null;
           double                   computedOffset;
           DOMString                easing = "linear";
           CompositeOperationOrAuto composite = "auto";
      };
      
    2. offsetcomputedOffseteasingcomposite 成员设置为 keyframe 的相应 关键帧偏移计算的关键帧偏移、 关键帧特定的 时序函数关键帧特定的复合操作

    3. 对于 keyframe 中的每个动画属性-值对 declaration,执行以下步骤:

      1. property name 设置为通过将 declaration 的属性名称传递给算法将动画属性名称转换为 IDL 属性名称的结果。

      2. IDL value 设置为通过将 declaration 传递给算法序列化 CSS 值 [CSSOM]得到的属性值的序列化结果。

      3. value 设置为转换 IDL value 为 ECMAScript 字符串值的结果。

      4. 使用属性名称 property name 和属性描述符 { [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true, [[Value]]: value } 以及布尔标志 false, 调用 output keyframe[[DefineOwnProperty]] 内部方法。

    4. output keyframe 添加到 result

  4. 返回 result

void setKeyframes(object? keyframes)

替换组成此效果的一组 关键帧

object? keyframes

一系列关键帧,其格式和处理在 第 6.6.3 节 处理关键帧参数 中定义。

此效果的 关键帧 组将被执行过程 处理关键帧参数 的结果替换。 如果该过程引发异常,则此效果的 关键帧 不会被修改。

6.6.1. 创建一个新的 KeyframeEffect 对象

本节为非规范性内容

KeyframeEffect 构造函数提供了多种创建新的 KeyframeEffect 对象的方法。

在最简单的情况下,可以通过以下方式构造一个将 elem 的 "left" 属性在三秒内更改为 100px 的 KeyframeEffect 对象:

var effect = new KeyframeEffect(elem, { left: '100px' }, 3000);

第二个参数,表示关键帧列表,可以指定多个属性。(参见 第 6.6.3 节 处理关键帧参数。)

// 一次性指定多个属性
var effectA = new KeyframeEffect(elem, { left: '100px', top: '300px' }, 3000);

// 指定多个关键帧
var effectB = new KeyframeEffect(elem, [ { left: '100px' }, { left: '300px' } ], 3000);

第三个参数,表示动画的时序,可以简单地是一个表示以毫秒为单位的 迭代持续时间 的数字,如上所示,或者为了指定更多的时序属性,例如 开始延迟,可以使用 EffectTiming 对象,如下所示:

var effect =
  new KeyframeEffect(elem, { left: '100px' }, { duration: 3000, delay: 2000 });

如果未指定持续时间,则使用值 0。可以通过以下方式创建一个简单设置属性而不进行任何插值的动画:

var effect =
  new KeyframeEffect(elem, { visibility: 'hidden' }, { fill: 'forwards' });

然而,正如在 第 4.6 节 填充行为 中所描述的,这种使用无限填充动画的方式是不建议的。

创建一个 KeyframeEffect 后,可以将其添加到 Animation 中然后播放该动画。然而,对于简单效果来说,Element.animate 快捷方式更为方便,因为它会自动执行这些步骤。例如,

elem.animate({ left: '100px' }, 3000);

6.6.2. 属性名称和IDL名称

property动画属性名称到IDL属性名称 转换算法如下:

  1. 如果 property 符合 <custom-property-name> 的形式,返回 property

  2. 如果 property 指的是CSS属性 float,返回字符串 "cssFloat"。

  3. 如果 property 指的是CSS属性 offset,返回字符串 "cssOffset"。

  4. 否则,返回将 property 传入 CSS属性到IDL属性 的算法结果 [CSSOM]

attributeIDL属性名称到动画属性名称 转换算法如下:

  1. 如果 attribute 符合 <custom-property-name> 的形式,返回 attribute

  2. 如果 attribute 是字符串 "cssFloat",则返回表示CSS属性 float 的动画属性。

  3. 如果 attribute 是字符串 "cssOffset",则返回表示CSS属性 offset 的动画属性。

  4. 否则,返回将 attribute 传入 IDL属性到CSS属性 的算法结果 [CSSOM]

6.6.3. 处理 keyframes 参数

本节内容为非规范性内容

以下方法均接受一组关键帧作为参数:

此参数可以采用以下两种形式之一,如下所示。

// 以下两种表达式产生相同的结果:
elem.animate([ { color: 'blue' },
               { color: 'green' },
               { color: 'red' },
               { color: 'yellow' } ], 2000);
elem.animate({ color: [ 'blue', 'green', 'red', 'yellow' ] }, 2000);

// 同样地,对于多属性动画,以下两个表达式是等效的:
elem.animate([ { color: 'blue', left: '0px' },
               { color: 'green', left: '-20px' },
               { color: 'red', left: '100px' },
               { color: 'yellow', left: '50px'} ], 2000);
elem.animate({ color: [ 'blue', 'green', 'red', 'yellow' ],
               left: [ '0px', '-20px', '100px', '50px' ] }, 2000);

// 顺便说一句,以下三种表达式都是等效的:
elem.animate([ { color: 'red' } ], 1000);
elem.animate({ color: [ 'red' ] }, 1000);
elem.animate({ color: 'red' }, 1000);

第一种形式(数组形式)由一个关键帧数组组成,每个关键帧可以为每个动画属性指定最多一个值。第二种形式(对象形式)由一个对象组成,其中每个动画属性可以指定单个动画值或一组动画值。

第一种数组形式是规范形式,也是由 getKeyframes() 方法返回的形式。

关键帧偏移 可以通过任意一种形式来指定,如下所示:

// 没有偏移的关键帧将自动计算偏移
// 为第一个关键帧设置为 0,中间关键帧为 0.65,最后一个关键帧为 1。
elem.animate([ { color: 'blue' },
               { color: 'green', offset: 0.5 },
               { color: 'red' },
               { color: 'yellow', offset: 0.8 },
               { color: 'pink' } ], 2000);

// 以下代码产生相同的结果。请注意,不需要指定最后一个值:它将自动被视为 'null',然后像前一个情况一样应用自动分配。
elem.animate({ color: [ 'blue', 'green', 'red', 'yellow', 'pink' ],
               offset: [ null, 0.5, null, 0.8 ] }, 2000);

同样的,时间函数关键帧特定复合操作 也可以通过任意形式指定。数组形式允许为每个 关键帧 指定不同的值,而对于对象形式,值列表将根据需要重复,直到每个关键帧都分配了一个值。

// 由于时间函数应用于关键帧之间,即使我们在最后一个关键帧上指定
// 一个时间函数,它也会被忽略。
elem.animate([ { color: 'blue', easing: 'ease-in' },
               { color: 'green', easing: 'ease-out' },
               { color: 'yellow' } ], 2000);

// 以下代码产生相同的结果。
elem.animate({ color: [ 'blue', 'green', 'yellow' ],
               easing: [ 'ease-in', 'ease-out' ] }, 2000);

// 重复行为使得将相同的值分配给所有关键帧变得简单:
elem.animate({ color: [ 'blue', 'green', 'yellow' ],
               easing: 'ease-in-out' }, 2000);

请注意,easing 属性在任意形式中都设置了 关键帧特定的时间函数。这与应用于整个 迭代持续时间时间函数 是独立的,后者可以使用 KeyframeEffectOptions 对象指定(或者在使用 animate() 方法时使用 KeyframeAnimationOptions 对象)。

在以下示例中,这两条语句会产生不同的结果。

// 此处,'ease-in-out' 应用于每个颜色值之间。
elem.animate({ color: [ 'blue', 'green', 'yellow' ],
               easing: 'ease-in-out' }, 2000);

// 然而,在这种情况下,'ease-in-out' 应用于整个动画的跨度,即从 'blue' 到 'yellow'。
elem.animate({ color: [ 'blue', 'green', 'yellow' ] },
             { duration: 2000, easing: 'ease-in-out' });

keyframes 参数的类型无法在 WebIDL 中表示,因为它依赖于部分开放的字典类型。

从概念上讲,此参数的类型等同于以下类 WebIDL 定义:

dictionary Keyframe {
    // ... 属性-值对 ...
    // 即 DOMString propertyName
    double?                   offset = null;
    DOMString                 easing = "linear";
    CompositeOperationOrAuto  composite = "auto";
};

dictionary PropertyIndexedKeyframes {
    // ... 属性-值 和 属性-值列表 对 ...
    // 即 (DOMString 或 sequence&lt;DOMString&gt;) propertyName
    (double? or sequence<double?>)                         offset = [];
    (DOMString or sequence<DOMString>)                     easing = [];
    (CompositeOperationOrAuto or sequence<CompositeOperationOrAuto>) composite = [];
};

typedef (sequence<Keyframe> or PropertyIndexedKeyframes) KeyframeArgument;

每个参数的含义和允许的值如下:

offset

关键帧偏移,以介于 0.0 和 1.0 之间的数字表示,包括这两个值,或 null

null 值表示 关键帧 应在相邻关键帧之间自动间隔。

指定超出范围 [0.0, 1.0] 的偏移量会导致抛出 TypeError 异常。

指定偏移量的关键帧必须按偏移量递增的顺序提供。然而,允许相邻且相等的偏移量。

easing

从此关键帧到系列中的下一个关键帧的 时间函数

解析此字符串的语法和错误处理与为 easing 属性在 EffectTiming 接口中定义的解析方式相同。

composite

用于将此关键帧中指定的值与 基础值 结合起来的 关键帧特定复合操作

如果值为 auto,则使用在 关键帧效果 中指定的 复合操作

由于此类型无法在 WebIDL 中表示,因此其处理方式在以下文本中定义。

对于接受 keyframes 参数的每个方法,对输入运行 处理 keyframes 参数 的过程,并保留该过程的结果。

首先,我们定义两个支持定义。

指令 检查完成记录结果,其中 结果 是来自调用 ECMAScript 操作的 完成记录,相当于以下步骤:

  1. 如果 结果突然完成,则抛出 结果 的 [[value]] 字段中包含的异常并中止该过程。

    如果 [[type]] 是 breakcontinuereturn,我们应该怎么做?可能会这样吗?

  2. 结果 的 [[value]] 字段中的值替换 结果

处理类似关键帧对象 的过程分为两个参数:

并返回一个映射,将属性名称映射到 DOMString 值(如果 allow lists 为 false),或者将属性名称映射到字符串值序列,否则,将使用以下过程:

  1. 运行 将 ECMAScript 值转换为字典类型 [WEBIDL] 的过程,将 keyframe input 作为 ECMAScript 值,并根据 allow lists 标志的值选择字典类型,如下所示:

    如果 allow lists 为 true,

    使用以下字典类型:

    dictionary BasePropertyIndexedKeyframe {
        (double? or sequence<double?>)                         offset = [];
        (DOMString or sequence<DOMString>)                     easing = [];
        (CompositeOperationOrAuto or sequence<CompositeOperationOrAuto>) composite = [];
    };
    
    否则,

    使用以下字典类型,

    dictionary BaseKeyframe {
        double?                  offset = null;
        DOMString                easing = "linear";
        CompositeOperationOrAuto composite = "auto";
    };
    

    将此过程的结果存储为 keyframe output

  2. 构建一个 animatable properties 列表如下:

    1. animatable properties 成为一个属性名称列表(包括具有可动画长手子属性的简写属性),这些属性可以由实现进行动画处理。

    2. 通过应用 动画属性名称到 IDL 属性名称 算法,将 animatable properties 中的每个属性名称转换为等效的 IDL 属性。

  3. input properties 成为通过调用 EnumerableOwnNames 操作获得的结果,并将 keyframe input 作为对象。

  4. 组成一个新的列表 animation properties,该列表包含在 input propertiesanimatable properties 中的所有属性,input properties 中的属性并符合 <custom-property-name> 生产规则。

  5. 按每个属性名称定义的 Unicode 代码点对 animation properties 进行升序排序。

  6. 对于 animation properties 中的每个 property name

    1. raw value 成为通过调用 [[Get]] 内部方法,并将 property name 作为属性键,将 keyframe input 作为接收器的结果。

    2. 检查完成记录raw value

    3. raw value 转换为 DOMString 或 DOMString 序列 property values,如下所示:

      如果 allow lists 为 true,

      property values 成为通过使用为将 ECMAScript 值转换为 IDL 值而定义的 过程raw value 转换为 IDL 类型 (DOMString 或 sequence<DOMString>) 的结果。

      如果 property values 是单个 DOMString,则用一个 DOMString 序列替换 property values,该序列的原始值为 property values 的唯一元素。

      否则,

      property values 成为通过使用为将 ECMAScript 值转换为 DOMString 而定义的 过程raw value 转换为 DOMString 的结果。

    4. 计算 normalized property name,该名称是将 property name 应用到 IDL 属性名称到动画属性名称 算法的结果。

    5. 将属性添加到 keyframe output,其属性名称为 normalized property name,属性值为 property values

  7. 返回 keyframe output

处理 keyframes 参数 的过程分为可为空的 ECMAScript 对象 object 作为输入,并返回一个关键帧序列,使用以下过程:

  1. 如果 object 为空,返回一个空的关键帧序列。

  2. processed keyframes 成为一个空的 关键帧 序列。

  3. method 成为 GetMethod(object, @@iterator) 的结果。

  4. 检查完成记录method

  5. 执行与以下第一个匹配条件对应的步骤,

    如果 method 不为 undefined
    1. iter 成为 GetIterator(object, method)。

    2. 检查完成记录iter

    3. 重复:

      1. next 成为 IteratorStep(iter)。

      2. 检查完成记录next

      3. 如果 next 为 false,中止此循环。

      4. nextItem 成为 IteratorValue(next)。

      5. 检查完成记录nextItem

      6. 如果 Type(nextItem) 不是 Undefined、Null 或 Object,则抛出 TypeError 并中止这些步骤。

      7. 将结果追加到 processed keyframes,通过运行 处理类似关键帧对象 过程,传递 nextItem 作为 keyframe input,并将 allow lists 标志设置为 false。

    否则,
    1. property-indexed keyframe 成为运行 处理类似关键帧对象 过程的结果,将 object 作为 keyframe input,并将 allow lists 标志设置为 true。

    2. 对于 property-indexed keyframe 中的每个成员 m,执行以下步骤:

      1. property name 成为 m 的键。

      2. 如果 property name 是 "composite"、"easing" 或 "offset",跳过此循环中的剩余步骤,并从 property-indexed keyframe 中的下一个成员 m 继续。

      3. property values 成为 m 的值。

      4. property keyframes 成为一个空的 关键帧 序列。

      5. 对于 property values 中的每个值 v,执行以下步骤:

        1. k 成为一个新的 关键帧,其 关键帧偏移 为空。

        2. 将属性-值对 property namev 添加到 k 中。

        3. k 追加到 property keyframes 中。

      6. 计算丢失的关键帧偏移 过程应用于 property keyframes

      7. 关键帧 添加到 processed keyframes 中。

    3. 计算的关键帧偏移 升序排序 processed keyframes 中的每个 关键帧

    4. processed keyframes 中相邻的 关键帧 具有相等的 计算的关键帧偏移 时,合并它们。

    5. offsets 成为一个 可为空double 值序列,基于 property-indexed keyframe 的 "offset" 成员的类型,分配如下:

      sequence<double?>

      "offset" 的值原样保留。

      double?

      一个长度为一的序列,其值为 "offset",即 « offset »,

    6. offsets 中的每个值分配给 processed keyframes 中对应位置的 关键帧偏移,直到到达任一序列的末尾。

    7. easings 成为 DOMString 值的序列,基于 property-indexed keyframe 的 "easing" 成员的类型,分配如下:

      sequence<DOMString>

      "easing" 的值原样保留。

      DOMString

      一个长度为一的序列,其值为 "easing",即 « easing »,

    8. 如果 easings 是一个空序列,则将其设为长度为一的序列,其中包含单个值 "linear",即 « "linear" »。

    9. 如果 easings 的项数少于 processed keyframes,则从列表的开头开始,连续重复 easings 中的元素,直到 easings 的项数与 processed keyframes 相等。

      例如,如果 processed keyframes 有五个项,而 easings 是序列 « "ease-in", "ease-out" »,则 easings 将被重复,变为 « "ease-in", "ease-out", "ease-in", "ease-out", "ease-in" »。
    10. 如果 easings 的项数多于 processed keyframes,则将多余的项存储为 unused easings

    11. easings 中的每个值分配给与 processed keyframes 中对应位置的 关键帧 上的 "easing" 属性,直到到达 processed keyframes 的末尾。

    12. 如果 property-indexed keyframe 的 "composite" 成员 不是 一个空序列:

      1. composite modes 成为从 property-indexed keyframe 的 "composite" 成员中分配的 CompositeOperationOrAuto 值的序列。如果该成员是一个 CompositeOperationOrAuto 值操作的单个值,则将 composite modes 设为长度为一的序列,其中值为 "composite"。

      2. easings 类似,如果 composite modes 的项数少于 processed keyframes,则从列表的开头开始,连续重复 composite modes 中的元素,直到 composite modes 的项数与 processed keyframes 相等。

      3. composite modes 中不为 auto 的每个值分配给与 processed keyframes 中对应位置的 关键帧特定复合操作 上的 关键帧,直到到达 processed keyframes 的末尾。

  6. 如果 processed keyframes 未按 松散排序的偏移 排序,则抛出 TypeError 并中止这些步骤。

  7. 如果 processed keyframes 中存在任何 关键帧,其 关键帧偏移 非空且小于零或大于一,则抛出 TypeError 并中止这些步骤。

  8. 对于 processed keyframes 中的每个 frame,执行以下步骤:

    1. 对于 frame 中的每个属性-值对,使用为该属性定义的语法解析属性值。

      如果属性值根据该属性的语法无效,则丢弃属性-值对。提供支持诊断内容错误的用户代理应该生成适当的警告,以突出显示无效的属性值。

    2. frame时间函数 成为通过使用为 easing 属性在 EffectTiming 字典中定义的 CSS 语法解析 "easing" 属性的结果。

      如果解析 "easing" 属性失败,抛出 TypeError 并中止此过程。

      注意: 在上述两个步骤中使用 CSS 解析器意味着允许 CSS 注释和转义,但在成功解析值时不保留这些内容。

      注意: 在 "easing" 属性解析失败的情况下,重要的是在从 object 读取所有属性后 抛出 TypeError,因为未能这样做是可观察的,并且如果以后在 WebIDL 中部分支持开放式字典,则不会匹配行为。

  9. 使用在 easing 属性在 EffectTiming 接口中定义的 CSS 语法解析 unused easings 中的每个值,如果任何值解析失败,抛出 TypeError 并中止此过程。

    此最后步骤是必需的,以便提供一致的行为,使得在以下所有情况下都会抛出 TypeError

    elem.animate({ easing: 'invalid' });
    elem.animate({ easing: ['invalid'] });
    elem.animate([{ easing: 'invalid' }]);
    

6.6.4. KeyframeEffectOptions 字典

通过提供 KeyframeEffect(target, keyframes, options) 构造函数,可以传递其他参数,其中包含 KeyframeEffectOptions 对象。

dictionary KeyframeEffectOptions : EffectTiming {
    CompositeOperation composite = "replace";
    CSSOMString?       pseudoElement = null;
};
composite, 类型为 CompositeOperation,默认值为 "replace"

合成操作 用于将此动画与 效果栈 进行合成,具体由 CompositeOperation 枚举值之一指定。 适用于所有指定 auto 关键帧特定合成操作关键帧

pseudoElement, 类型为 CSSOMString,可为空,默认值为 null

用于指定 伪元素 选择器(必须有效或为 null), 以确定给定 目标元素效果目标

6.7. CompositeOperationCompositeOperationOrAuto 枚举

关键帧效果 的合成行为可能的值由 CompositeOperation 枚举表示。

enum CompositeOperation { "replace", "add", "accumulate" };
replace

对应于 替换 合成操作 值 使 动画效果 覆盖其合并的 基础值

add

对应于 添加 合成操作 值 使 动画效果 添加到其合并的 基础值 上。

accumulate

对应于 累积 合成操作 值 使 动画效果 累积到其合并的 基础值 上。

关键帧 的合成行为的可能值与 CompositeOperation 枚举共享相同的值,以及附加的 auto 值。

enum CompositeOperationOrAuto { "replace", "add", "accumulate", "auto" };
auto

指示应使用关联的 关键帧效果合成操作

6.8. Animatable 接口混入

可以作为 KeyframeEffect 对象目标的对象实现了 Animatable 接口混入。

interface mixin Animatable {
    Animation           animate(object? keyframes,
                                optional (unrestricted double or KeyframeAnimationOptions) options = {});
    sequence<Animation> getAnimations(optional GetAnimationsOptions options = {});
};

dictionary KeyframeAnimationOptions : KeyframeEffectOptions {
    DOMString id = "";
    AnimationTimeline? timeline;
};

dictionary GetAnimationsOptions {
    boolean subtree = false;
};
Animation animate(keyframes, options)

执行以下步骤:

  1. target 为调用此方法的对象。

  2. 使用与 KeyframeEffect(target, keyframes, options) 构造函数相同的过程,在 target相关 Realm 中构造一个新的 KeyframeEffect 对象 effect,传递 target 作为 target 参数,keyframesoptions 参数如所提供的那样。

    如果上述过程引发异常,则传播该异常并中止此过程。

  3. 如果 optionsKeyframeAnimationOptions 对象,令 timelineoptionstimeline 成员,或者,如果 optionstimeline 成员缺失,则为调用此方法的元素的 默认文档时间线

  4. 使用与 Animation() 构造函数相同的过程,在 target相关 Realm 中构造一个新的 Animation 对象 animation,传递 effecttimeline 作为同名参数。

  5. 如果 optionsKeyframeAnimationOptions 对象,将 optionsid 成员的值分配给 animationid 属性。

  6. 运行 播放动画 的过程,animationauto-rewind 标志设置为 true。

  7. 返回 animation

本节为非规范性内容

以下代码片段:

var animation = elem.animate({ opacity: 0 }, 2000);

大致相当于:

var effect = new KeyframeEffect(elem, { opacity: 0 }, 2000);
var animation = new Animation(effect, elem.ownerDocument.timeline);
animation.play();
keyframes

要使用的 关键帧。 此值将作为 keyframes 参数传递给 KeyframeEffect(target, keyframes, options) 构造函数,并且具有与该构造函数中定义的相同解释。

options

为创建的 KeyframeEffectAnimation 提供的时间和动画选项。

sequence<Animation> getAnimations(options)

返回该对象的 相关动画 集合,或者,如果传递了 options 参数,并且将 subtree 设置为 true,则返回该对象的 子树相关动画 集合。

返回的列表按照 动画 相关的效果在 § 5.4.2 效果栈 中描述的组合顺序进行排序。

调用此方法会触发 样式更改事件,适用于 目标元素。 因此,返回的列表反映了应用任何未处理的样式更改后的状态,例如尚未处理的动画相关样式属性的更改。

options

管理 getAnimations() 返回的动画集合的参数。

id, 类型为 DOMString,默认值为 ""

要分配给生成的 Animationid 属性的字符串。

timeline, 类型为 AnimationTimeline,可为空

一个可选值,如果存在,则指定要与新创建的 动画 关联的 时间线

subtree, 类型为 boolean,默认值为 false

如果为 true,则表示应将与其 动画效果 关联的 动画 的目标元素是调用 getAnimations() 方法的对象的 后代 时,也应将其包含在结果中。

6.9. Document 接口的扩展

以下扩展应用于 Document 接口,该接口在 [DOM] 中定义。

partial interface Document {
    readonly attribute DocumentTimeline timeline;
};
timeline, 类型为 DocumentTimeline,只读

表示 默认文档时间线DocumentTimeline 对象。

6.10. DocumentOrShadowRoot 接口混入的扩展

以下扩展应用于 DocumentOrShadowRoot 接口 混入,该接口混入在 [DOM] 中定义。

partial interface mixin DocumentOrShadowRoot {
    sequence<Animation> getAnimations();
};
sequence<Animation> getAnimations()

返回调用此方法的 文档影子根子树相关动画 集合。

返回的列表按照 动画 相关的效果在 § 5.4.2 效果栈 中描述的组合顺序进行排序。

调用此方法会触发文档的 样式更改事件。 因此,返回的列表反映了应用任何未处理的样式更改后的状态,例如尚未处理的动画相关样式属性的更改。

6.11. Element 接口的扩展

由于 DOM 元素可以作为动画的目标,Element 接口在 [DOM] 中扩展如下:

Element includes Animatable;

这允许以下用法。

elem.animate({ color: 'red' }, 2000);

6.12. AnimationPlaybackEvent 接口

动画播放事件 使用 AnimationPlaybackEvent 接口表示。

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

使用在 构造事件 中定义的过程构造一个新的 AnimationPlaybackEvent 对象 [DOM]

currentTime, 类型为 double,只读,可为空

事件排队时生成事件的 动画的当前时间。 如果事件生成时动画处于空闲状态,此值将为 null

timelineTime, 类型为 double,只读,可为空

事件排队时与生成事件的时间线关联的时间值。 如果事件排队时动画没有关联的活动时间线,此值将为 null

currentTime, 类型为 double,可为空,默认值为 null

请参见 currentTime 属性的描述。

timelineTime, 类型为 double,可为空,默认值为 null

请参见 timelineTime 属性的描述。

6.13. 模型活跃性

对模型的任何部分进行更改,都会导致整个时间模型以及任何依赖的样式更新。

除非另有说明, 调用此规范编程接口部分定义的方法或构造函数, 或者获取或设置成员, 不会触发样式更改事件

注意: 扩展此规范的其他规范预计将通过引入触发 这种事件的情况来细化样式更改事件的要求。 例如, 当此规范中的接口表示 由 CSS 标记定义的动画时, 其中的许多方法将需要触发样式更改事件 以反映对指定样式的更改。

本节为非规范性内容

基于上述要求和本规范中其他地方的规范性要求,可以观察到以下不变量:

对 Web 动画模型所做的更改立即生效

例如,如果与 KeyframeEffect 相关联的 Animation 通过编程接口进行定位(参见 § 4.4.4 设置动画的当前时间), 则查询动画的 startTime 时返回的值将立即反映模型的更新状态。

// 最初 animation.effect.getComputedTiming().localTime 为 3000
animation.currentTime += 2000;
alert(animation.effect.getComputedTiming().localTime); // 显示 "5000"
查询受动画影响的属性的计算样式将返回动画的完全最新状态

例如,如果在将新的 Animation 应用于元素后立即查询该元素的已使用样式, 则新动画的结果将包含在返回的值中。

// 立即将不透明度设置为 0
elem.animate({ opacity: 0 }, { fill: 'forwards' });
alert(window.getComputedStyle(elem).opacity); // 显示 "0"
在同一任务内进行的更改被同步,以便整个更改集一起呈现

由于对模型的更改立即生效并结合 ECMAScript 的运行到完成语义, 因此永远不会出现例如仅应用指定样式的更改而不应用动画的情况。

// 使用回退淡化不支持 Element.animate 的浏览器的透明度
// 设置过渡起点
elem.style.opacity = '0';
elem.animate([ { opacity: 1 }, { opacity: 0 } ], 500);

注意,然而,在上述示例中,用户代理可能会呈现 应用上述任何更改的帧。 例如,如果渲染发生在一个单独的进程中,并且该进程计划在 上述任务完成后不久运行,但在更改 可以传达给该进程之前。

通过 文档时间线currentTime 属性返回的值在任务内不会更改

由于要求时间线每次运行 更新动画并发送事件 过程时更新其当前时间, 在同一个脚本块中执行的长代码块中两次查询 currentTime 将返回相同的值,如以下示例所示。

var a = document.timeline.currentTime;
// ... 许多行代码 ...
var b = document.timeline.currentTime;
alert(b - a); // 显示 0
传递给 requestAnimationFrame 回调的时间将等于 document.timeline.currentTime

由于 HTML 的 事件循环处理模型 定义了 更新动画并发送事件 的过程在运行动画帧回调之前执行, 并且由于传递给这些回调的时间与传递给这两个过程的 now 时间戳相同,默认文档时间线的 当前时间应该 与传递给 requestAnimationFrame 的时间一致。

window.requestAnimationFrame(function(now) {
  // 显示 0
  alert(now - document.timeline.currentTime);
});
从此编程接口调用方法通常不会触发过渡

请考虑以下示例:

// 设置过渡起点
div.style.opacity = '1';
getComputedStyle(div).opacity;

// 设置过渡终点
div.style.transition = 'opacity 1s';
div.style.opacity = '0';

// 触发动画
div.animate({ opacity: [0.5, 1] }, 500);

// 等待过渡结束 —— 以下内容永远不会被调用!
div.addEventListener('transitionend', () => {
  console.log('transitionend');
});

在这种情况下,调用 animate()不会触发 样式更改事件。 因此,待处理的样式更改将与 由新动画导致的样式更改同时处理。 由于动画样式将覆盖 更改前样式更改后样式, 因此不会生成过渡, 并且transitionend事件的事件处理程序永远不会被调用。

7. 与媒体片段的集成

媒体片段规范 [MEDIA-FRAGS] 定义了一种用于定位媒体资源时间范围的方法。 媒体片段的应用取决于其所指定资源的 MIME 类型。 对于具有 SVG MIME 类型 的资源,时间参数的应用在 动画元素 规范中定义。

注意: 媒体片段的定义是基于资源的 MIME 类型进行操作。 因此,在使用 Web 动画内容的所有情况下,可能不支持时间定位。

8. 与页面显示的交互

HTML 允许用户代理与用户代理定义的状态一起存储 会话历史条目,以便用户在页面之间导航时, 可以恢复页面的先前状态,包括滚动位置等状态 [HTML]

当引用的文档被卸载和遍历时,暂停和恢复媒体元素的用户代理, 应当对包含 Web 动画内容的文档应用一致的处理。 如果提供了这种行为,则应通过调整跟踪挂钟时间的时间值的任何时间线来实现。

这是否与这些 时间值相对于 navigationStart 并且 requestAnimationFrame 使用与 document.timeline.currentTime 相同的时间相矛盾? [问题 #2083]

9. 实现要求

9.1. 时间值的精度

时间值的内部表示是实现依赖的, 但是,建议用户代理能够以微秒精度表示输入时间值, 以便 0.001 的时间值(通常表示毫秒)与 0.0 区分开来。

9.2. 一致性标准

本规范定义了动画的抽象模型, 因此,对于不支持脚本的用户代理, 由于没有可测试的表面区域,故没有一致性标准。

然而,不支持脚本的用户代理可能会实现基于本规范定义的其他技术, 在这种情况下,本规范中提供的定义将构成额外技术的一部分一致性标准。

符合规范的脚本化 Web 动画用户代理是实现了§ 6 编程接口中定义的API的用户代理。

10. 致谢

感谢 Steve Block、Michael Giuffrida、Ryan Seys 和 Eric Willigers 对本规范的贡献。

还要感谢 Michiel "Pomax" Kamermans 帮助拟定了平滑时间函数的方程, 尽管此功能已推迟到后续规范。

我们深深感谢 Southern Star Animation 的慷慨和耐心,他们向编辑介绍了制作广播动画时使用的过程和技术。

11. 自上次发布以来的更改

2022 年 9 月 8 日工作草案 以来,进行了以下更改:

更新日志提供了更详细的历史记录。

附录 A: 现有属性的动画类型

通常,属性的动画类型会与其定义一起包含。 然而,对于一些在较旧或非常成熟的规范中定义的属性, 动画类型信息未被包含。 所有这些属性都被假定为具有 通过计算值动画类型, 除非它们是下面列出的例外之一。

font-weight 的动画

font-weight 属性值在第 4 级之前按如下方式组合

注意: 此定义已被[CSS-FONTS-4]废弃, 其中关于font-weight值必须是 100 的倍数的要求被取消。 此时,font-weight动画类型仅为通过计算值

visibility 的动画

对于 visibility 属性,visible插值方式为 离散步长, 其中 p 的值介于 0 和 1 之间时 映射到 visiblep 的其他值映射到更接近的端点; 如果两个值都不是 visible,则使用离散动画。

box-shadowtext-shadow 的动画

box-shadowtext-shadow 属性的动画遵循组合 阴影列表的以下程序:

列表中的每个阴影 (将 none 视为长度为 0 的列表) 都按组件插值,类似于 通过计算值的行为。 但是,如果两个输入阴影都是 inset 或两个输入阴影都不是 inset, 则插值阴影在这方面必须与输入阴影匹配。 如果任何一对输入阴影有一个 inset 而另一个不是 inset, 则整个阴影列表使用离散动画。 如果阴影列表的长度不同, 则通过在末尾填充阴影来扩展较短的列表, 这些阴影的颜色为 透明, 所有长度为 0, 并且其 inset(或不是)与较长的列表匹配。

两个 VaVb 的加法定义为阴影列表列表连接,使得 Vresult 等于 Va 扩展Vb

阴影列表的累积遵循 上述插值的匹配规则,根据每个组件的类型执行加法, 或者如果 inset 值不匹配,则回退到离散动画。

一致性

文档约定

一致性要求以描述性断言和 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"> 与其他规范性文本区分开来,如下所示: 用户代理必须提供可访问的替代方案。

一致性类别

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

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

如果样式表的所有语句 使用此模块中定义的语法均符合通用 CSS 语法以及此模块中定义的每个功能的单独语法,则样式表符合本规范。

如果渲染器除了按 适当规范定义的方式解释样式表之外,还通过正确解析 并相应地渲染文档,支持本规范定义的所有功能,则渲染器符合本规范。 但是,UA 因设备限制无法正确渲染文档并不会导致 UA 不符合规范。 (例如,UA 不要求在单色显示器上渲染颜色。)

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

部分实现

为了便于作者利用向前兼容的解析规则来分配后备值,CSS 渲染器 必须 将任何 at 规则、属性、属性值、关键字 以及其他语法结构视为无效(并忽略 适当),如果它们没有可用的支持级别。 特别是,用户代理不得在单个 多值属性声明中选择性忽略不支持的组件值并使用支持的值:如果任何值被视为无效 (不支持的值必须如此),CSS 要求整个声明 被忽略。

不稳定和专有功能的实现

为了避免与未来的稳定 CSS 功能发生冲突, CSS 工作组建议遵循最佳实践来 实现 不稳定功能和 专有扩展到 CSS。

非实验性实现

一旦规范达到候选推荐阶段, 就可以进行非实验性实现,并且实现者应 发布任何 CR 级别功能的未加前缀的实现, 只要他们能够证明这些功能已根据规范正确实现。

为了建立和维护 CSS 在各个 实现之间的互操作性,CSS 工作组要求非实验性 CSS 渲染器在发布任何 CSS 功能的未加前缀实现之前,向 W3C 提交实施报告(如有必要,还提交实施报告中使用的 测试用例)。 提交给 W3C 的测试用例 需由 CSS 工作组审查和修正。

有关提交测试用例和实施报告的更多信息, 可以在 CSS 工作组的网站上找到,网址为 https://www.w3.org/Style/CSS/Test/。 应将问题发送到 public-css-testsuite@w3.org 邮件列表。

索引

本规范定义的术语

引用定义的术语

参考文献

规范性引用

[CSS-ANIMATIONS-1]
David Baron 等人. CSS 动画 第 1 级. 2023年3月2日. WD. URL: https://www.w3.org/TR/css-animations-1/
[CSS-ANIMATIONS-2]
David Baron; Brian Birtles. CSS 动画 第 2 级. 2023年3月2日. WD. URL: https://www.w3.org/TR/css-animations-2/
[CSS-BACKGROUNDS-3]
Bert Bos; Elika Etemad; Brad Kemper. CSS 背景和边框模块 第 3 级. 2023年2月14日. CR. URL: https://www.w3.org/TR/css-backgrounds-3/
[CSS-CASCADE-3]
Elika Etemad; Tab Atkins Jr.. CSS 层叠与继承 第 3 级. 2021年2月11日. REC. URL: https://www.w3.org/TR/css-cascade-3/
[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS 层叠与继承 第 5 级. 2022年1月13日. CR. URL: https://www.w3.org/TR/css-cascade-5/
[CSS-COLOR-4]
Tab Atkins Jr.; Chris Lilley; Lea Verou. CSS 色彩模块 第 4 级. 2022年11月1日. CR. URL: https://www.w3.org/TR/css-color-4/
[CSS-DISPLAY-3]
Elika Etemad; Tab Atkins Jr.. CSS 显示模块 第 3 级. 2023年3月30日. CR. URL: https://www.w3.org/TR/css-display-3/
[CSS-EASING-1]
Brian Birtles; Dean Jackson; Matt Rakow. CSS 缓动函数 第 1 级. 2023年2月13日. CR. URL: https://www.w3.org/TR/css-easing-1/
[CSS-FONTS-4]
John Daggett; Myles Maxfield; Chris Lilley. CSS 字体模块 第 4 级. 2021年12月21日. WD. URL: https://www.w3.org/TR/css-fonts-4/
[CSS-LOGICAL-1]
Rossen Atanassov; Elika Etemad. CSS 逻辑属性与值 第 1 级. 2018年8月27日. WD. URL: https://www.w3.org/TR/css-logical-1/
[CSS-PROPERTIES-VALUES-API-1]
Tab Atkins Jr. 等人. CSS 属性与值 API 第 1 级. 2020年10月13日. WD. URL: https://www.w3.org/TR/css-properties-values-api-1/
[CSS-SHADOW-PARTS-1]
Tab Atkins Jr.; Fergal Daly. CSS Shadow Parts. 2018年11月15日. WD. URL: https://www.w3.org/TR/css-shadow-parts-1/
[CSS-STYLE-ATTR]
Tantek Çelik; Elika Etemad. CSS 样式属性. 2013年11月7日. REC. URL: https://www.w3.org/TR/css-style-attr/
[CSS-TEXT-DECOR-4]
Elika Etemad; Koji Ishii. CSS 文本装饰模块 第 4 级. 2022年5月4日. WD. URL: https://www.w3.org/TR/css-text-decor-4/
[CSS-TRANSFORMS-1]
Simon Fraser 等人. CSS 变换模块 第 1 级. 2019年2月14日. CR. URL: https://www.w3.org/TR/css-transforms-1/
[CSS-TRANSITIONS-1]
David Baron 等人. CSS 过渡. 2018年10月11日. WD. URL: https://www.w3.org/TR/css-transitions-1/
[CSS-TRANSITIONS-2]
CSS 过渡 第 2 级 URL: https://drafts.csswg.org/css-transitions-2/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS 值与单位模块 第 4 级. 2023年4月6日. WD. URL: https://www.w3.org/TR/css-values-4/
[CSS-VARIABLES-1]
Tab Atkins Jr.. CSS 自定义属性级联变量模块 第 1 级. 2022年6月16日. CR. URL: https://www.w3.org/TR/css-variables-1/
[CSS-WILL-CHANGE-1]
Tab Atkins Jr.. CSS Will Change 模块 第 1 级. 2022年5月5日. CR. URL: https://www.w3.org/TR/css-will-change-1/
[CSS-WRITING-MODES-3]
Elika Etemad; Koji Ishii. CSS 书写模式 第 3 级. 2019年12月10日. REC. URL: https://www.w3.org/TR/css-writing-modes-3/
[CSS-WRITING-MODES-4]
Elika Etemad; Koji Ishii. CSS 书写模式 第 4 级. 2019年7月30日. CR. URL: https://www.w3.org/TR/css-writing-modes-4/
[CSS21]
Bert Bos 等人. 层叠样式表 第 2 级修订 1 (CSS 2.1) 规范. 2011年6月7日. REC. URL: https://www.w3.org/TR/CSS21/
[CSS22]
Bert Bos. 层叠样式表 第 2 级修订 2 (CSS 2.2) 规范. 2016年4月12日. WD. URL: https://www.w3.org/TR/CSS22/
[CSSOM]
Daniel Glazman; Emilio Cobos Álvarez. CSS 对象模型 (CSSOM). 2021年8月26日. WD. URL: https://www.w3.org/TR/cssom-1/
[DOM]
Anne van Kesteren. DOM 标准. 现行标准. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript 语言规范. URL: https://tc39.es/ecma262/multipage/
[HR-TIME]
Yoav Weiss. 高分辨率时间. 2023年4月25日. WD. URL: https://www.w3.org/TR/hr-time-3/
[HTML]
Anne van Kesteren 等人. HTML 标准. 现行标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 现行标准. URL: https://infra.spec.whatwg.org/
[MEDIA-FRAGS]
Raphaël Troncy 等人. 媒体片段 URI 1.0 (基础). 2012年9月25日. REC. URL: https://www.w3.org/TR/media-frags/
[MOTION-1]
Dirk Schulze 等人. 运动路径模块 第 1 级. 2018年12月18日. WD. URL: https://www.w3.org/TR/motion-1/
[RFC2119]
S. Bradner. RFC 中用于表示要求级别的关键词. 1997年3月. 最佳现行做法. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SELECT]
Tantek Çelik 等人. 选择器 第 3 级. 2018年11月6日. REC. URL: https://www.w3.org/TR/selectors-3/
[SELECTORS-4]
Elika Etemad; Tab Atkins Jr.. 选择器 第 4 级. 2022年11月11日. WD. URL: https://www.w3.org/TR/selectors-4/
[SVG11]
Erik Dahlström 等人. 可缩放矢量图形 (SVG) 1.1 (第二版). 2011年8月16日. REC. URL: https://www.w3.org/TR/SVG11/
[SVG2]
Amelia Bellamy-Royds 等人. 可缩放矢量图形 (SVG) 2. 2018年10月4日. CR. URL: https://www.w3.org/TR/SVG2/
[WEB-ANIMATIONS-2]
Brian Birtles; Robert Flack. Web 动画 第 2 级. 2023年2月21日. WD. URL: https://www.w3.org/TR/web-animations-2/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. 现行标准. URL: https://webidl.spec.whatwg.org/

参考资料

[SMIL-ANIMATION]
Patrick Schmitz; Aaron Cohen. SMIL 动画. 2001年9月4日. REC. URL: https://www.w3.org/TR/smil-animation/

IDL 索引

[Exposed=Window]
interface AnimationTimeline {
    readonly attribute double? currentTime;
};

dictionary DocumentTimelineOptions {
  DOMHighResTimeStamp originTime = 0;
};

[Exposed=Window]
interface DocumentTimeline : AnimationTimeline {
  constructor(optional DocumentTimelineOptions options = {});
};

[Exposed=Window]
interface Animation : EventTarget {
    constructor(optional AnimationEffect? effect = null,
                optional AnimationTimeline? timeline);
             attribute DOMString                id;
             attribute AnimationEffect?         effect;
             attribute AnimationTimeline?       timeline;
             attribute double?                  startTime;
             attribute double?                  currentTime;
             attribute double                   playbackRate;
    readonly attribute AnimationPlayState       playState;
    readonly attribute AnimationReplaceState    replaceState;
    readonly attribute boolean                  pending;
    readonly attribute Promise<Animation>       ready;
    readonly attribute Promise<Animation>       finished;
             attribute EventHandler             onfinish;
             attribute EventHandler             oncancel;
             attribute EventHandler             onremove;
    undefined cancel();
    undefined finish();
    undefined play();
    undefined pause();
    undefined updatePlaybackRate(double playbackRate);
    undefined reverse();
    undefined persist();
    [CEReactions]
    undefined commitStyles();
};

enum AnimationPlayState { "idle", "running", "paused", "finished" };

enum AnimationReplaceState { "active", "removed", "persisted" };

[Exposed=Window]
interface AnimationEffect {
    EffectTiming         getTiming();
    ComputedEffectTiming getComputedTiming();
    undefined            updateTiming(optional OptionalEffectTiming timing = {});
};

dictionary EffectTiming {
    double                             delay = 0;
    double                             endDelay = 0;
    FillMode                           fill = "auto";
    double                             iterationStart = 0.0;
    unrestricted double                iterations = 1.0;
    (unrestricted double or DOMString) duration = "auto";
    PlaybackDirection                  direction = "normal";
    DOMString                          easing = "linear";
};

dictionary OptionalEffectTiming {
    double                             delay;
    double                             endDelay;
    FillMode                           fill;
    double                             iterationStart;
    unrestricted double                iterations;
    (unrestricted double or DOMString) duration;
    PlaybackDirection                  direction;
    DOMString                          easing;
};

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

enum PlaybackDirection { "normal", "reverse", "alternate", "alternate-reverse" };

dictionary ComputedEffectTiming : EffectTiming {
    unrestricted double  endTime;
    unrestricted double  activeDuration;
    double?              localTime;
    double?              progress;
    unrestricted double? currentIteration;
};

[Exposed=Window]
interface KeyframeEffect : AnimationEffect {
    constructor(Element? target,
                object? keyframes,
                optional (unrestricted double or KeyframeEffectOptions) options = {});
    constructor(KeyframeEffect source);
    attribute Element?           target;
    attribute CSSOMString?       pseudoElement;
    attribute CompositeOperation composite;
    sequence<object> getKeyframes();
    undefined        setKeyframes(object? keyframes);
};

dictionary BaseComputedKeyframe {
     double?                  offset = null;
     double                   computedOffset;
     DOMString                easing = "linear";
     CompositeOperationOrAuto composite = "auto";
};

dictionary BasePropertyIndexedKeyframe {
    (double? or sequence<double?>)                         offset = [];
    (DOMString or sequence<DOMString>)                     easing = [];
    (CompositeOperationOrAuto or sequence<CompositeOperationOrAuto>) composite = [];
};

dictionary BaseKeyframe {
    double?                  offset = null;
    DOMString                easing = "linear";
    CompositeOperationOrAuto composite = "auto";
};

dictionary KeyframeEffectOptions : EffectTiming {
    CompositeOperation composite = "replace";
    CSSOMString?       pseudoElement = null;
};

enum CompositeOperation { "replace", "add", "accumulate" };

enum CompositeOperationOrAuto { "replace", "add", "accumulate", "auto" };

interface mixin Animatable {
    Animation           animate(object? keyframes,
                                optional (unrestricted double or KeyframeAnimationOptions) options = {});
    sequence<Animation> getAnimations(optional GetAnimationsOptions options = {});
};

dictionary KeyframeAnimationOptions : KeyframeEffectOptions {
    DOMString id = "";
    AnimationTimeline? timeline;
};

dictionary GetAnimationsOptions {
    boolean subtree = false;
};

partial interface Document {
    readonly attribute DocumentTimeline timeline;
};

partial interface mixin DocumentOrShadowRoot {
    sequence<Animation> getAnimations();
};

Element includes Animatable;

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

问题索引

应该有一个比“origin time”更好的术语—— 它与“time origin”太相似了。[问题 #2079]
在某些计时函数存在的情况下,动画效果的输入迭代进度并不限于[0, 1]范围。 但是,目前关键帧偏移确实限于[0, 1]范围,并且属性值只是为超出此范围的输入迭代进度值外推。

我们曾考虑取消此限制,因为在某些情况下,能够在[0, 1]范围外指定非线性属性值变化是有用的。 一个例子是一个从绿色到黄色插值的动画,但有一个过冲计时函数,使得它暂时插值“超出”黄色到红色,然后再回到黄色。

虽然可以通过修改关键帧和计时函数来实现此效果,但这种方法似乎打破了模型将计时问题与动画效果分离的原则。

目前尚不清楚应该如何实现这种效果,但我们注意到允许关键帧偏移超出[0, 1]范围可能会使当前指定的行为(在必要时综合偏移为0和1的关键帧)不一致。

请参阅2013年东京F2F会议中讨论的第4节([0, 1]范围外的关键帧偏移)

<https://github.com/w3c/csswg-drafts/issues/2081>

关于being rendered [HTML]对于display: contents的定义仍然在讨论中。 在此过程的目的下,我们假设一个具有display: contents的元素如果在其他情况下会有相关布局框(即它是连接的且不属于 display: none子树)正在渲染

remove() 方法可以用于从其父组或动画中移除效果。 我们是否应该保留它在第1级中并将其简单定义为从其动画中移除动画效果?[问题 #2082]
如果 [[type]] 是 breakcontinuereturn,我们该怎么办?可能吗?
这是否与这些时间值相冲突,因为它们相对于 navigationStart,并且 requestAnimationFrame 使用的时间与 document.timeline.currentTime 相同?[问题 #2083]