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()); 或者,应用程序可能希望查询动画的播放状态,而无需等待。
- 控制运行中的动画
-
有时需要对动画进行播放控制,以便它们能够响应外部输入。 例如,可能需要在显示模态对话框之前暂停所有现有动画,以免它们分散用户的注意力。
- 通过脚本创建动画
-
虽然可以使用 ECMAScript 通过
requestAnimationFrame
[HTML]执行动画, 但在表示方式和可能的性能优化(如在单独线程上执行动画)方面,此类动画的行为与声明性动画有所不同。 使用 Web Animations 编程接口,可以通过脚本创建与声明性动画具有相同行为和性能特征的动画。 - 动画调试
-
在复杂的应用程序中,可能很难确定元素如何达到其当前状态。 可以使用 Web Animations 编程接口检查正在运行的动画,以回答诸如“为什么这个元素的不透明度在变化?”等问题。
// 打印 elem 上任何不透明度动画的 id for ( const animationof elem. getAnimations()) { if ( animation. effectinstanceof KeyframeEffect&& animation. effect. getKeyframes() . some( frame=> frame. hasOwnProperty( 'opacity' )) ) { console. log( animation. id); } } 同样,为了微调动画,通常需要降低它们的播放速度并重放它们。
// 减慢并重放任何变换动画 const transformAnimations= elem. getAnimations(). filter( animation=> animation. effectinstanceof KeyframeEffect&& animation. effect. getKeyframes(). some( frame=> frame. hasOwnProperty( 'transform' ) ) ); for ( const animationof transformAnimations) { animation. currentTime= 0 ; animation. updatePlaybackRate( 0.5 ); } - 测试动画
-
为了测试使用动画的应用程序,通常不实际等待这些动画运行完毕。 而是希望将动画调整到特定时间点。
// 将动画调整到中途并检查不透明度是否为50% for ( const animationof elem. getAnimations()) { const { delay, activeDuration} = animation. effect. getComputedTiming(); animation. currentTime= delay+ activeDuration/ 2 ; } assert. strictEqual( getComputedStyle( elem). opacity, '0.5' ); // 检查动画完成后加载屏幕是否隐藏 for ( const animationof 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 模型由两个主要独立的部分组成,即时间模型和动画模型。这些部分的作用如下:
- 时间模型
-
获取某一时刻,并将其转换为动画单次迭代中的比例距离,称为迭代进度。 同时记录迭代索引,因为有些动画每次重复时会有所不同。
- 动画模型
-
获取时间模型生成的迭代进度值和迭代索引,并将其转换为要应用于目标属性的一系列值。
从图形上看,这个流程可以表示如下:
当前时间作为输入进入时间模型,生成迭代进度值和迭代索引。
这些参数作为输入用于动画模型,生成要应用的值。
例如,考虑一个动画:
-
在3秒后开始,
-
运行两次,
-
每次运行2秒,
-
将一个矩形的宽度从50像素更改为100像素。
前三点适用于时间模型。 在6秒时,它会计算出动画应处于第二次迭代的中途,并生成结果0.5。 然后动画模型使用该信息来计算宽度。
本规范从时间模型开始,然后介绍动画模型。
4. 时间模型
本节描述并定义了 Web Animations 时间模型的行为。
4.1. 时间模型概述
本节是非规范性的
Web Animations 时间模型的两个特征是:无状态和层次化。
4.1.1. 无状态
Web Animations 时间模型通过接收输入时间并生成输出迭代进度来运行。 由于输出仅基于输入时间,并且与先前的输入无关,因此该模型可以被描述为无状态的。 这使得模型具有以下特性:
- 与帧率无关
-
由于输出与先前的输入无关,更新模型的速度不会影响其进度。 只要输入时间与现实世界时间的进展成比例,动画将以相同的速度进行, 而不受运行它们的设备能力的影响。
- 方向不可知
-
由于输入序列无关紧要,模型是无方向的。 这意味着模型可以更新到任意时刻,而无需任何特殊处理。
- 常时间寻址
-
由于每个输入都与先前的输入无关,执行寻址操作所需的处理,即使是很远的未来,也至少有可能是恒定的。
时间模型的无状态行为有一些例外情况。
首先,模型的编程接口中定义了一些方法, 提供播放控制功能,如暂停动画。 这些方法是根据调用它们的时间来定义的,因此是有状态的。 这些方法主要是为了方便而提供的,不属于核心时间模型,而是叠加在其上。
同样,动画的完成行为意味着 动画的媒体(关联效果)的结束时间的动态变化 可能会根据变化发生的时间产生不同的结果。 这种行为虽然有些不尽如人意,但被认为是直观的,并与 HTML 保持一致。 因此,模型只能在时间属性没有动态变化的情况下真正被描述为无状态的。
最后,每次更新模型时,都可以认为它建立了一个临时状态。 虽然这个临时状态会影响编程接口返回的值, 但它对后续更新没有影响,因此与上述无状态特性不冲突。
4.1.2. 层次化
时间模型的另一个特征是时间是继承的。 时间从时间线开始,逐步向下传递到每个动画效果。 在每个步骤中,时间可能被向前或向后移动、缩放、反转、暂停和重复。
在本规范的这一层次中,层次结构是浅显的。 规范的后续层次将引入组效果的概念,从而允许更深的时间层次结构。
4.2. 时间值
时间的基础是时间节点之间的时间关系层次结构。 父节点以时间值的形式向其子节点提供时间信息。
时间值是一个实数,通常表示 从某个时刻起的毫秒数。 时间值与挂钟时间毫秒数之间的联系 可能会因值在时间层次结构中传递时应用的任何数量的变换而模糊。
将来可能会有基于滚动位置或 UI 手势的时间线, 在这种情况下,时间值与毫秒之间的联系将进一步减弱。
如果时间节点没有处于生成时间值的状态, 则时间值也可能是未解析的。
4.3. 时间线
时间线提供 时间值来源, 以便进行同步。
在任何给定时刻,时间线都有一个 单一的当前时间值, 简单地称为时间线的当前时间。
时间线可能并不总能返回有意义的 时间值,而是仅返回 未解析的时间值。 例如,它可能是相对于尚未发生的时刻定义的, 如文档的加载事件触发的时间。 当时间线的时间值为未解析时, 该时间线被视为非活动。
如果时间线报告的当前时间 始终大于或等于其先前报告的当前时间,则该 时间线是单调递增的。
特定类型的时间线可以定义一个过程,将 时间线时间转换为起源相对时间, 以便可以比较由基于挂钟的时间线生成的时间值。
时间线可以与文档相关联。
当被要求为时间戳now的Document
doc更新动画并发送事件时,请执行以下步骤:
-
为doc移除被替换的动画。
-
注意: 这样做是为了确保在上一步骤中更新时间线时排队的任何微任务 在分发动画事件之前运行其回调。
-
将doc的待处理的动画事件队列复制为events to dispatch。
-
清除doc的待处理的动画事件队列。
-
按以下方式对events to dispatch中的动画事件执行稳定排序:
注意: 对事件进行排序的目的是为了尽可能确保,即使在设备性能不同从而帧率不同的情况下, 事件也能以一致的顺序分发。
注意: 稳定排序的要求是因为有时可能会有相同预定事件时间的事件排队。 例如,持续时间为零的 CSS 动画可能会同时分发
animationstart
和animationend
事件, 这些事件的顺序应该保持不变。 -
按照上一步确定的顺序,在其对应的目标上分发events to dispatch中的每个事件。
通常描述每次调用此过程时,都会建立一个新的动画帧是很方便的。 对动画或动画效果的时间属性的更改,或对象的添加和删除可能会导致时间模型或动画模型的输出发生变化, 但这些操作本身并不会创建新的动画帧,它们只是更新当前的动画帧。
4.3.1. 文档时间线
文档时间线是一种时间线,它与文档相关联,其 当前时间计算为每次 更新动画并发送事件过程运行时提供的now时间戳的固定偏移量。 此固定偏移量称为文档时间线的起始时间。
“起始时间”这个术语可能有更好的替代方案—— 它与“时间起源”过于相似。[Issue #2079]
与不是活动文档的Document
相关联的文档时间线也被视为非活动的。
要将文档时间线timeline的时间线时间timeline time转换为起源相对时间, 返回timeline time与timeline的起始时间之和。如果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)的过程如下:
-
将old timeline设为当前的animation的时间线(如果有)。
-
如果new timeline与old timeline是同一对象,则终止此过程。
-
将animation的时间线设置为 new timeline。
-
运行更新动画完成状态的过程, 对animation将did seek标志设置为false,并将synchronously notify标志设置为false。
4.4.2. 设置动画的关联效果
将animation的关联效果设置为new effect(可能为null)的过程如下:
-
将old effect设为当前的animation的关联效果(如果有)。
-
如果new effect与old effect是同一对象,则终止此过程。
-
如果animation有待处理的播放任务,则重新安排该任务,以便在animation 就绪播放new effect时尽快运行。
-
如果new effect不为
null
且new effect是另一个动画的关联效果,则运行 设置动画的关联效果 (此过程)在previous animation上传递null作为new effect。 -
将animation的关联效果设为new effect。
-
运行更新动画完成状态的过程, 对animation将did seek标志设置为false,并将synchronously notify标志设置为false。
4.4.3. 动画的当前时间
当前时间根据以下条件计算:
4.4.4. 设置动画的当前时间
动画的当前时间可以设置为一个新值,以定位动画。设置当前时间的过程分为两部分。
将动画animation的当前时间静默设置为seek time的过程如下:
将动画animation的当前时间设置为seek time的过程如下:
-
运行静默设置当前时间的步骤,将animation的时间设置为seek time。
-
如果animation有待处理的暂停任务,则通过执行以下步骤同步完成暂停操作:
-
运行更新动画完成状态的过程,对animation将did seek标志设置为true,并将synchronously notify标志设置为false。
4.4.5. 设置动画的开始时间
将动画animation的开始时间设置为new start time的过程如下:
-
令timeline time为animation所关联的时间线的当前时间值。如果没有与animation关联的时间线或关联的时间线是非活动的,则将timeline time设为未解析。
-
如果timeline time是未解析的,而new start time是已解析的,则将animation的保持时间设为未解析。
这保留了一个不变性,即当我们没有活动时间线时,只能设置开始时间或动画的当前时间之一。
-
令previous current time为animation的当前时间。
注意:这是应用上一步中的更改后animation的当前时间,这些更改可能导致当前时间变为未解析。
-
应用任何待处理的播放速率到animation。
-
将animation的开始时间设为new start time。
-
根据以下匹配条件之一更新animation的保持时间:
-
如果animation有待处理的播放任务或待处理的暂停任务,则取消该任务,并解决animation的当前准备就绪的承诺,传递animation。
-
运行更新动画完成状态的过程,设置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被重用。
4.4.8. 播放动画
用于播放动画的过程,animation,给定一个标志auto-rewind,如下:
-
让aborted pause为布尔标志,如果animation有一个挂起的暂停任务,则为 true,否则为 false。
-
让has pending ready promise为布尔标志,初始值为 false。
-
如果auto-rewind标志为 true,执行以下步骤中第一个匹配条件对应的步骤:
-
如果以下三个条件都满足:
将seek time设置为零。
注意: 上述步骤确保无论auto-rewind标志如何设置,此过程都将播放一个闲置动画。
-
让has finite timeline为 true,如果animation有一个关联的时间轴,该时间轴不是单调递增的。
-
如果seek time是已解决的,
- 如果has finite timeline为 true,
-
-
将animation的开始时间设置为seek time。
-
应用任何挂起的播放速率到animation。
-
- 否则,
-
将animation的保持时间设置为seek time。
-
如果animation有一个挂起的播放任务或挂起的暂停任务,
-
取消该任务。
-
将has pending ready promise设置为 true。
-
-
如果以下四个条件都满足:
中止此过程。
-
如果has pending ready promise为 false,让animation的当前准备就绪的 Promise为一个新的 promise,位于animation的相关领域。
-
安排一个任务,在animation准备就绪时立即运行。 该任务应执行以下步骤:
-
执行以下条件中第一个匹配条件对应的步骤(如果有):
-
解决animation的当前准备就绪的 Promise与animation。
-
运行用于更新动画的完成状态的过程,将did seek标志设置为 false,并将synchronously notify标志设置为 false。
只要上述任务已安排但尚未运行,animation就被描述为具有一个挂起的播放任务。然而,当任务正在运行时,animation不具有挂起的播放任务。
如果用户代理确定animation立即准备就绪,它可能会将上述任务安排为微任务,使其在下一个微任务检查点运行,但不得同步执行任务。
上述要求异步运行挂起的播放任务,确保如下代码在不同实现之间行为一致:
animation
. play(); animation. ready. then( () => { console. log( 'Playback commenced' ); }, () => { console. log( 'Playback was canceled' ); } ); // 假设某种情况需要取消播放... animation. cancel(); // "播放已取消" 将打印到控制台。 在上述代码中,如果挂起的播放任务是同步运行的,则不会拒绝当前准备就绪的 Promise。
-
运行用于更新动画的完成状态的过程,将did seek标志设置为 false,并将synchronously notify标志设置为 false。
4.4.9. 暂停动画
与播放动画一样,暂停操作可能不会立即发生(参见§ 4.4.6 等待关联效果)。例如,如果动画由单独的进程执行,可能需要同步当前时间,以确保其反映动画进程绘制的状态。
将动画暂停,动画,的过程如下:
-
如果动画有待处理的暂停任务,中止这些步骤。
-
如果动画的当前时间是未解析,根据以下第一个匹配条件执行步骤:
- 如果动画的播放速度≥ 0,
-
将寻找时间设置为零。
- 否则,
-
- 如果动画的关联效果结束为正无穷,
-
抛出“
InvalidStateError
”DOMException
并中止这些步骤。 - 否则,
-
将寻找时间设置为动画的关联效果结束。
-
如果寻找时间是已解析,
-
设具有待处理的准备承诺为一个布尔标志,初始为假。
-
如果动画有待处理的播放任务,取消该任务并将具有待处理的准备承诺设为真。
-
调度一个任务,在首次满足以下两个条件时执行:
任务应执行以下步骤:
-
让准备时间为完成暂停动画的关联效果播放时,与动画关联的时间轴的时间值。
-
应用任何待处理的播放速度在动画上。
-
使动画的开始时间未解析。
-
运行程序更新动画的完成状态,did seek标志设置为假,synchronously notify标志设置为假。
只要上述任务已调度但尚未运行,动画即被描述为有一个待处理的暂停任务。然而,在任务运行时,动画不会有待处理的暂停任务。
-
-
运行程序更新动画的完成状态,did seek标志设置为假,synchronously notify标志设置为假。
4.4.10. 到达结束
本节是非规范性的
DVD播放器或磁带播放器通常会继续播放,直到它们到达媒体的末尾,此时它们停止播放。如果这些播放器能够反向播放,它们通常会在到达媒体的开头时停止播放。为了模拟这种行为并与HTML的媒体元素 [HTML]保持一致,Web Animations的动画不会在关联效果的结束时间之后继续向前播放,或在时间为零时向后播放。
达到其播放范围的自然边界的动画被称为已完成。
限制当前时间的效果如下图所示。
然而,可以将动画当前时间“寻找到”超出关联效果结束后的时间。在这样做时,当前时间将不会继续推进,但动画将表现得像是已在寻找到的时间暂停。
这使得,例如,可以将没有关联效果的动画的当前时间寻找到5秒。如果稍后将一个具有晚于5秒的结束时间的关联效果与动画相关联,则播放将从5秒标记处开始。
当动画的关联效果长度发生变化时,可能会出现类似的行为。
4.4.11. 当前的已完成 Promise
每个动画都有一个当前的已完成 Promise。 这个当前的已完成 Promise最初是一个待定的Promise对象。
每次动画离开已完成播放状态时,该对象将被替换为一个新的Promise。
4.4.12. 更新已完成状态
对于具有正播放速率的动画,当前时间会不断增加,直到到达关联效果的结束时间。
关联效果的结束时间等于动画的结束时间。如果动画没有关联效果,则关联效果的结束时间为零。
一个正在运行的动画,当达到这个边界(或超越它)并且其开始时间已解析时,即被称为已完成。
在每次修改动画对象时,都会通过以下定义的更新动画的已完成状态过程检查是否越过了这个边界。这个过程还会作为更新动画并发送事件过程的一部分运行。在这两种情况下,did seek标志被设置为false。
对于每个动画,用户代理维护一个先前的当前时间 时间值,该值最初为未解析的。
在正常播放期间,动画的当前时间被限制在上述边界内,然而,仍可以使用设置动画当前时间的过程将动画的当前时间设置为超出这些边界的时间。
用于更新动画的已完成状态的过程如下,参数包括did seek标志(指示是否在设置当前时间后进行的更新),以及synchronously notify标志(指示更新是否在我们期望立即完成事件队列和完成Promise解析的上下文中调用)。
-
计算unconstrained current time,即当前时间,若did seek为false,则使用保持时间代替未解析的时间值。如果did seek为true,则unconstrained current time等于current time。
注意:此定义是为了适应可能改变方向的时间轴。如果没有这个定义,即使时间轴在相反方向上进展,一旦完成的动画也将保持完成状态。
-
如果下列三个条件全部为真:
则根据下列条件更新animation的保持时间:
- 如果播放速率 > 0 并且unconstrained current time大于或等于关联效果结束时间,
-
如果did seek为true,则保持时间为unconstrained current time的值。
如果did seek为false,则保持时间为previous current time和associated 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与活动时间轴关联,
-
执行以下步骤:
-
将animation的previous current time设置为计算出的当前时间。
-
如果current finished state为true并且当前完成的promise尚未解决,则执行以下步骤:
-
让完成通知步骤指代以下过程:
-
解析animation的当前已完成的promise对象,并传递animation。
-
创建一个
AnimationPlaybackEvent
,即finishEvent。 -
将finishEvent的
currentTime
属性设置为animation的当前时间。 -
将finishEvent的
timelineTime
属性设置为与animation关联的时间轴的当前时间。如果animation未与时间轴关联,或时间轴为非活动状态,则将timelineTime
设置为null
。 -
如果animation具有时间文档,则将finishEvent及其目标animation附加到其待处理动画事件队列中。对于预定事件时间,使用将animation的关联效果结束时间转换为与原点相关的时间的结果。
-
如果synchronously notify为true,则取消任何已排队的微任务来运行animation的完成通知步骤,并立即执行finish notification steps。
否则,如果synchronously notify为false,则排队一个微任务来运行animation的完成通知步骤,除非已为animation排队了一个微任务来运行这些步骤。
-
-
如果current finished state为false并且animation的当前已完成的promise已解析,则将animation的current 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推进到其当前播放方向的自然结束点:
-
如果animation的有效播放速率为零,或者animation的有效播放速率 > 0 并且关联效果结束时间为无限大,抛出一个 "
InvalidStateError
"DOMException
并中止这些步骤。 -
应用任何待处理的播放速率到animation。
-
设置limit如下:
-
静默设置当前时间为limit。
-
如果animation的开始时间是未解析的,并且animation有一个关联的活动的时间轴,则将start time设置为以下表达式的结果:
timeline time - (limit / 播放速率)
,其中timeline time是关联的时间值。 -
如果存在待处理的播放任务,并且开始时间是已解析的,则取消该任务并解析animation的当前就绪承诺并将animation作为其值。
-
执行更新动画完成状态的过程,针对animation并将did seek标志设置为true,将synchronously notify标志设置为true。
4.4.14. 取消动画
可以取消动画,这将导致当前时间变为未解析的,从而移除由关联效果引起的任何效果。
取消动画的过程如下:
-
-
运行重置动画的待处理任务的过程,针对animation。
-
将当前完成承诺的[[PromiseIsHandled]]内部槽设置为true。
-
创建一个
AnimationPlaybackEvent
,命名为cancelEvent。 -
将cancelEvent的
currentTime
设置为null
。 -
让timeline time为与animation关联的时间轴的当前时间。如果animation未关联到一个活动时间轴,则让timeline time为未解析的时间值。
-
将cancelEvent的
timelineTime
设置为timeline time。如果timeline time是未解析的,则将其设置为null
。 -
如果animation有一个时间控制文档,则将cancelEvent及其目标animation追加到该文档的待处理动画事件队列中。如果animation与定义了将时间轴时间转换为原点相对时间的过程的活动时间轴关联,则计划事件时间为应用该过程后得到的结果。否则,计划事件时间是一个未解析的时间值。
-
重置动画的待处理任务的过程如下:
-
如果animation有待处理的播放任务,取消该任务。
-
如果animation有待处理的暂停任务,取消该任务。
-
应用任何待处理的播放速率到animation。
-
将animation的当前就绪承诺的[[PromiseIsHandled]]内部槽设置为true。
4.4.15. 速度控制
动画具有一个播放速率,它提供了从关联的时间轴的时间值的变化速率到动画的当前时间的缩放因子。播放速率最初为1。
将动画的播放速率设置为零实际上会暂停动画(然而,播放状态不一定会变为暂停)。
4.4.15.1. 设置动画的播放速率
将动画 animation 的播放速率设置为 new playback rate 的过程如下:
-
清除animation上的任何待处理的播放速率。
-
让previous playback rate为animation的当前有效播放速率。
-
将播放速率设置为new playback rate。
-
根据以下第一个匹配条件执行步骤(如果有):
- 如果animation与单调递增时间轴关联,且previous time为已解析,
-
将当前时间设置为animation的previous 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时,执行以下步骤:
将动画animation的播放速率无缝更新为new playback rate,并保留其当前时间的过程如下:
-
将animation的播放状态记为previous play state。
注意: 在更新animation的有效播放速率之前, 需要记录播放状态,因为在以下逻辑中, 如果animation当前处于完成状态,我们希望立即应用其待处理的播放速率, 无论应用后它是否仍将完成。
-
将animation的待处理的播放速率设置为new playback rate。
-
执行以下第一个匹配条件对应的步骤:
- 如果animation有一个待处理的播放任务或待处理的暂停任务,
-
终止这些步骤。
注意: 不同类型的待处理任务将在其运行时应用待处理的播放速率,因此在这种情况下无需进一步操作。
-
应用任何待处理的播放速率于animation。
注意: 第二个条件是必需的,以确保如果我们有一个运行中的动画,其当前时间未解析且没有待处理的播放任务,则我们不会在下面尝试播放它。
-
如果previous play state为完成,
- 否则,
-
执行播放动画的过程, auto-rewind标志设为false。
4.4.16. 倒放动画
倒放动画animation的过程如下:
-
如果animation没有关联的时间轴,或关联的时间轴是非活动的,抛出一个 "
InvalidStateError
"DOMException
并终止这些步骤。 -
将original pending playback rate设为animation的待处理的播放速率。
-
运行播放动画的步骤,auto-rewind标志设为true。
如果播放动画的步骤抛出异常,将animation的待处理的播放速率恢复为original pending playback rate并传播异常。
4.4.17. 播放状态
动画可以描述为以下播放状态之一,每个状态还提供了非规范性的描述:
动画的播放状态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,执行以下步骤:
-
如果time是未解析的,返回time。
-
返回计算结果:
time × (1 / playback rate) + start time
(其中 playback rate 和start time分别是 animation的播放速率 和开始时间)。
要将时间线时间转换为相对于原点的时间,即一个以与时间值相同尺度表示的time,执行以下步骤,以时间线的timeline为基准:
-
将time从动画时间转换为时间线时间。
-
如果timeline time是未解析的,返回time。
-
返回通过定义的过程,将时间线中的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 填充行为)生效时。 只有处于有效状态的动画效果才会对其目标产生结果。
每种状态的规范定义如下。
确定动画效果的阶段需要以下定义:
- 动画方向
-
如果效果与动画相关联且关联的动画的播放速率小于零,则为“向后”; 在所有其他情况下,动画方向为“向前”。
- 活动前边界时间
- 活动后边界时间
如果动画效果的本地时间未解析,并且满足以下任一条件,则动画效果处于之前阶段:
如果动画效果的本地时间未解析,并且满足以下任一条件,则动画效果处于之后阶段:
如果动画效果的本地时间未解析,并且不处于之前阶段或之后阶段,则动画效果处于活动阶段。
此外,将动画效果处于上述任何阶段之外的情况称为处于空闲阶段。
如果满足以下所有条件,则动画效果处于播放中状态:
如果满足以下任何条件,则动画效果处于当前状态:
如果动画效果的活动时间未解析,则动画效果处于有效状态,按§ 4.8.3.1 计算活动时间中的过程计算。
4.5.6. 相关动画
如果满足以下条件,则一个动画是相关动画:
元素、伪元素、文档或影子根target的子树的相关动画是包含至少一个其效果目标为target的动画的集合,target是target或伪元素的包含的后代(如果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. 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. 迭代区间
可以指定动画效果重复一定次数或无限次。 这种重复发生在活动区间之内。 单次重复发生的时间跨度称为迭代区间。
与活动区间不同,动画效果可以有多个迭代区间,尽管通常只有与当前迭代对应的区间是关注的重点。
单次迭代的时间长度称为迭代持续时间。 动画效果的初始迭代持续时间为零。
- 迭代持续时间
-
动画效果完成单次迭代所需的时间。
- 活动持续时间
-
整个动画效果完成所需的时间,包括重复部分。 这可能比迭代持续时间更长或更短。
4.7.2. 控制迭代
动画效果重复的次数称为其迭代次数。 迭代次数是一个大于或等于零的实数。 迭代次数也可以是正无穷大,以表示动画效果无限重复。
除了迭代次数,动画效果还具有迭代开始属性,用于指定动画效果应从一系列迭代中的哪个偏移位置开始。 迭代开始是一个大于或等于零的有限实数。
这些参数的行为在§ 4.8 核心动画效果计算中定义。
第一个例子中,迭代次数为2.5,导致第三次迭代在其迭代区间的一半时被截断。
第二个例子与第一个相同,但具有迭代开始0.5。 这导致动画效果从第一次迭代的一半处开始。
4.7.3. 迭代时间空间
在Web动画中,所有时间都是相对于某个参考点的。 不同的参考点产生不同的时间空间。
这可以与计算机图形学中使用的坐标空间进行比较。 时间空间的零时刻类似于坐标空间的原点。
我们可以描述重复的动画在每次动画重复时建立一个新的时间空间:迭代时间空间。
迭代时间空间是一个时间空间,其零时刻是动画效果当前迭代的开始。
在Web动画模型中,我们还引用了活动时间,它是相对于活动区间开始的时间。 然而,这个时间空间是模型的内部内容,在编程接口或标记中并不公开。
以下图例展示了这些时间空间。
注意: 虽然时间空间本身是无限的,但Web动画定义了活动时间和迭代进度,使它们被限制在如图所示的范围内。 例如,尽管-1秒的时间在活动时间空间中是有效时间,但在§ 4.8.3.1 计算活动时间中定义的计算活动时间的过程将永远不会返回负值。
除了这些时间空间,我们还可以引用文档时间空间,这是默认文档时间轴的时间值的时间空间,该时间轴属于当前全局对象的Document。
4.7.4. 区间定时
当动画效果重复时,我们必须定义迭代边界上的行为。
对于此行为,实际上对于所有区间定时,Web动画使用的是端点排他性定时模型。
这意味着虽然区间的开始时间包含在区间内,但结束时间不包含在区间内。
在区间表示法中,这可以写成[begin, end)
。
这种模型在区间重复和顺序排列时提供了合理的行为,因为区间之间没有重叠。
在下图中,对于重复效果,在本地时间1秒时,迭代时间为0。 对于顺序动画,在时间轴时间1秒时,只有动画B的关联效果会在播放中;边界之间没有重叠。
这种行为的一个例外是,当执行填充时,如果填充开始于区间端点,则使用该端点。 此行为源自于§ 4.8.3.3 计算简单迭代进度中给出的算法,并在下图中进行了说明。
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. 计算活动时间
活动时间基于本地时间和开始延迟。 然而,它只有在动画效果应该产生输出时才定义,因此它依赖于填充模式和以下各阶段:
4.8.3.2. 计算整体进度
整体进度 描述了已完成的迭代次数(包括部分迭代),其定义如下:
4.8.3.3. 计算简单迭代进度
简单迭代进度是当前迭代的进度百分比, 忽略播放方向或应用于效果的时间函数引入的时间变换,其计算方法如下:
-
如果所有以下条件为真,
令简单迭代进度为1.0。
上述步骤实现了这样一种行为:当动画的活动区间恰好在迭代结束时结束时,它通过保持最后一次迭代的端点而不是下一次迭代的开始点来填充。
最后一个条件防止在迭代次数为零的情况下应用此行为,因为动画从未播放任何迭代。
-
返回简单迭代进度。
4.8.4. 计算当前迭代
当前迭代 可以通过以下步骤计算:
-
否则,返回
floor(整体进度)
。
4.9. 方向控制
动画效果还可以通过方向控制来配置以不同方向运行迭代。 为此,动画效果具有一个播放方向参数,该参数取以下值之一:
-
正常(normal),
-
反向(reverse),
-
交替(alternate),或
-
交替反向(alternate-reverse)。
这些值的语义被纳入到随后对定向进度的计算中。
这些值的非规范性定义如下:
- 正常(normal)
-
所有迭代按指定方式播放。
- 反向(reverse)
-
所有迭代按与指定方式相反的方向播放。
- 交替(alternate)
-
偶数迭代按指定方式播放,奇数迭代按与指定方式相反的方向播放。
- 交替反向(alternate-reverse)
-
偶数迭代按与指定方式相反的方向播放,奇数迭代按指定方式播放。
4.9.1. 计算定向进度
定向进度 通过以下步骤从简单迭代进度计算得出:
4.10. 时间变换
通常需要控制动画效果的进展速度。 例如,通过缓动动画速度可以创造出动量感并产生更自然的效果。 CSS 缓动函数模块[CSS-EASING-1] 定义了用于此目的的缓动函数。
动画效果关联有一个 缓动函数。 默认的缓动函数是线性缓动函数。
4.10.1. 计算变换进度
变换进度 通过以下步骤从定向进度计算得出:
-
按以下步骤计算之前标志的值:
-
使用§ 4.9.1 计算定向进度中定义的过程确定当前方向。
-
如果当前方向为正向,则前进中为真,否则为假。
-
4.11. 迭代进度
5. 动画模型
对于某些类型的动画效果,Web 动画动画模型会采用时间模型生成的迭代进度和当前迭代值,并使用这些值来计算相应的输出。
每个这样的动画效果的输出会通过效果栈与其他效果的输出结合,然后应用到目标属性上(参见§ 5.4 结合效果)。
5.1. 简介
一个动画效果有零个或多个与之关联的属性,这些属性会响应其时间输出的变化而受到影响。这些属性被称为效果的目标属性。
给定一个迭代进度、当前迭代和一个基础值,一个动画效果会通过应用适用于该属性的动画类型的程序来为每个可动画的 目标属性生成一个效果值。
5.2. 动画属性
除非另有说明,否则所有 CSS 属性都是可动画的。 每个属性的属性定义表中通过动画类型行定义了属性值如何组合:
- 不可动画
-
该属性不可动画。
在动画关键帧中列出时不会处理,
并且不会受过渡效果影响。
注意:属性通常被排除在动画之外, 因为对其进行动画处理会导致过于复杂的情况。 例如,定义动画参数的属性是不可动画的, 因为这样做会导致复杂的递归行为。
注意:仅针对不可动画属性的动画效果仍然会表现出通常的动画效果行为,例如触发事件和 延迟动画的当前完成的 promise的实现。
- 离散的
-
该属性的值无法有意义地组合,
因此它是不可加的,插值在 50%
时从Va切换到Vb,即
- 按计算值
- 相应的计算值组件根据该值类型 使用指示的过程进行组合(插值、添加或累积) (参见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. 计算属性值
元素
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,并包含以下步骤:
已计算的关键帧是使用以下过程生成的。 注意,此过程仅对具有效果目标的 关键帧效果执行,其中可以计算出计算属性值。
-
让computed keyframes成为一个空的关键帧列表。
-
对于在此关键帧效果上指定的 关键帧列表中的每个keyframe,执行以下步骤:
-
将一个新的空的关键帧, computed keyframe,添加到computed keyframes。
-
对于keyframe中指定的每个属性:
-
使用keyframe上指定的值作为值,使用目标元素作为元素 计算属性值;然后将该属性和值添加到computed keyframe。
-
对于简写属性,添加等效的长格式属性。
-
对于逻辑属性[CSS-LOGICAL-1],根据 writing-mode 和/或direction的计算值, 添加等效的物理属性 [CSS-WRITING-MODES-4]至效果目标。
例如,如果keyframe的border-width 属性的值为“12pt”,用户代理可能会计算出每个长格式属性的“16px”值:border-bottom-width, border-left-width, border-right-width, 和border-top-width。 因此,computed keyframe将不会有属性 border-width的值,而是包括每个长格式属性,并且每个属性的值均为“16px”。
如果在展开简写属性或使用物理属性替换逻辑属性时出现冲突,请按以下顺序应用规则,直到解决冲突:
-
长格式属性覆盖简写属性(例如border-top-color覆盖border-top)。
-
包含较少长格式组件的简写属性覆盖包含较多长格式组件的简写属性(例如border-top覆盖border-color)。
-
物理属性覆盖逻辑属性。
-
对于具有相同数量的长格式组件的简写属性,当按组成每个IDL名称的Unicode代码点升序排序时,IDL名称较早出现的属性将覆盖较晚出现的属性。
-
-
-
将计算缺失的关键帧偏移过程应用于 computed keyframes。
-
返回computed keyframes。
5.3.4. 关键帧效果的效果值
单个属性的效果值由关键帧效果引用,作为其 目标属性之一,对于给定的迭代进度、当前迭代和基础值,计算如下。
-
如果迭代进度为未解析,则中止此过程。
-
让目标属性成为要计算其效果值的长格式属性。
-
从特定属性的关键帧中移除没有目标属性的属性值的关键帧。
-
如果特定属性的关键帧为空,则返回基础值。
-
如果特定属性的关键帧中没有已计算的关键帧偏移为0的关键帧, 则创建一个新的关键帧,其已计算的关键帧偏移为0,属性值设置为组合的中性值, 并将其组合操作设置为add,并将其添加到特定属性的关键帧的开头。
-
同样,如果特定属性的关键帧中没有已计算的关键帧偏移为1的关键帧, 则创建一个新的关键帧,其已计算的关键帧偏移为1,属性值设置为组合的中性值, 并将其组合操作设置为add,并将其添加到特定属性的关键帧的末尾。
-
让区间端点为一个空的关键帧序列。
-
通过以下条件中的第一个匹配条件来填充区间端点:
-
对于区间端点中的每个关键帧:
-
如果区间端点中只有一个关键帧,则返回该关键帧上目标属性的属性值。
-
让起始偏移为区间端点中第一个关键帧的已计算关键帧偏移。
-
让结束偏移为区间端点中最后一个关键帧的已计算关键帧偏移。
-
让区间距离为以下结果:
(迭代进度 - 起始偏移) / (结束偏移 - 起始偏移)
。 -
让变换后的距离为以下结果:使用区间距离作为输入进度,计算与区间端点中第一个关键帧关联的时间函数。
-
返回以下结果:应用由目标属性的动画类型定义的插值过程, 对区间端点中的两个关键帧指定的目标属性的值,使用第一个这样的值作为Vstart,使用第二个值作为Vend, 并使用变换后的距离作为插值参数p。
请注意,此过程对效果指定的关键帧列表假定以下内容:
确保这些条件得到满足的责任在于模型的用户(例如,声明性标记或编程接口)。
注意:此过程允许关键帧重叠。 在重叠点上的行为是输出值跳转到该偏移量处定义的最后一个关键帧的值。 对于在0或1处重叠的关键帧,输出值对于迭代进度值小于0或大于或等于1的情况,分别是关键帧中的第一个关键帧或最后一个关键帧的值。
例如,如果在font-size属性上存在从10px
到20px
的进行中的过渡,
则在关键帧中指定为1em
的属性值在关键帧计算期间会针对font-size属性的范围[10px
,
20px
]内的计算值进行解析。
我们曾考虑删除此限制,因为存在一些情况,其中在[0, 1]范围之外指定非线性属性值变化是有用的。 一个例子是一个从绿色插值到黄色的动画,但具有一个使其暂时插值“超越”黄色到红色然后再回到黄色的超调时间函数。 虽然这种效果可以通过修改关键帧和时间函数来实现,但这种方法似乎破坏了模型中的时间问题与动画效果的分离。
目前尚不清楚应该如何实现这种效果,但我们注意到,允许关键帧偏移超出[0, 1]范围可能使当前指定的行为(必要时合成偏移为0和1的关键帧)不一致。
5.4. 组合效果
在为关键帧效果计算效果值后,它们将应用于动画效果的目标属性。
由于多个生效的关键帧效果可能会针对同一属性,因此通常需要将多个关键帧效果的结果组合在一起。这一过程称为合成,它基于为每个由生效的动画效果目标的属性建立的效果栈。
在将关键帧效果的结果组合在一起后,合成的结果将与目标属性的其他指定值相结合。
该安排如下图所示:
针对同一属性的关键帧效果的结果通过效果栈组合在一起。
该组合的结果随后在适当的位置插入 CSS 层叠。
此操作的第一部分——组合针对相同属性的效果值——需要确定关键帧效果彼此结合的方式,以及它们应用的顺序,即它们的相对合成顺序。
效果值的结合方式由相应关键帧效果的合成操作决定。
效果值的相对合成顺序由为每个动画属性建立的效果栈决定。
5.4.1. 动画类
本规范提供了一个通用的动画模型,旨在供其他规范在此模型之上定义标记或编程接口。生成动画的特定标记或编程接口定义其动画类。
其他规范可能会定义不同动画类之间或特定类内的复合顺序的特定行为。
例如,类为“CSS 动画”的动画被定义为比类为“CSS 过渡”的动画具有更高的复合顺序,但比没有特定类的其他动画顺序低。
在“CSS 动画”对象集中,基于animation-name属性等因素定义了特定的复合顺序。
5.4.2. 效果栈
与每个由一个或多个关键帧效果目标的属性关联的效果栈建立了关键帧效果的相对复合顺序。
在一个效果栈中,任何两个关键帧效果A和B的相对复合顺序通过比较其属性来建立,具体如下:
-
按照以下条件依次排序A和B,直到确定顺序:
排序靠前的动画效果具有更低的复合顺序。
5.4.3. 计算效果栈的结果
为了计算效果栈的最终值,将栈中每个关键帧效果的效果值按复合顺序依次组合。
对于栈中的每个关键帧效果,从该关键帧效果中选择适当的效果值与基础值结合,生成新值。这个结果值成为栈中下一个关键帧效果组合的基础值。
效果栈的最终值称为合成值,它只是将栈中最后一个(复合顺序最高的)关键帧效果的效果值与此时的基础值组合的结果。
5.4.4. 效果合成
将效果值与基础值结合使用的具体操作由产生此效果值的关键帧效果的合成操作决定。
本规范定义了三种合成操作,如下:
- 替换
- 添加
- 累积
-
对于动画类型,如果累积操作被定义为非交换的,则操作数的顺序为基础值随后为效果值。
5.4.5. 应用合成结果
将合成值应用于目标属性通过将指定值添加到 CSS 层叠来实现。
添加此指定值的层叠级别取决于与效果栈中复合顺序最高的效果关联的动画的类。
默认情况下,指定值添加到 CSS 层叠的“动画声明”级别([css-cascade-3])。
例如,如果复合顺序最高的效果与“CSS 过渡”类动画相关联,则合成值将添加到层叠的“过渡声明”级别。
-
将属性的基础值计算为在没有动画的情况下为该属性生成的计算值。
-
为属性建立效果栈(参见 § 5.4.2 效果栈)。
-
计算效果栈的合成值,将属性的基础值作为初始基础值传入(参见 § 5.4.3 计算效果栈的结果)。
-
将合成值插入到 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,如果:
执行以下步骤:
-
创建一个
AnimationPlaybackEvent
,removeEvent。 -
将removeEvent的
currentTime
属性设置为animation的当前时间。 -
将removeEvent的
timelineTime
属性设置为与animation关联的时间线的当前时间。 -
如果animation有用于计时的文档,则将removeEvent附加到其待处理动画事件队列中,并附上其目标animation。
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 ; };
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 (); };
id
,类型为DOMString-
用于标识动画的字符串。
effect
,类型为AnimationEffect,可为空timeline
,类型为AnimationTimeline,可为空startTime
,类型为double,可为空currentTime
,类型为double,可为空playbackRate
,类型为double-
此动画的播放速度。设置此属性将遵循设置播放速度的过程,将对象的播放速度更新为新值。
设置此属性会同步更新播放速度,这意味着它不会尝试与在单独进程或线程上运行的动画的播放状态同步。因此,为正在播放的动画设置
playbackRate
可能会导致动画跳跃。要为正在播放的动画设置播放速度,以便它平滑更新,请使用异步
updatePlaybackRate()
方法。 playState
,类型为AnimationPlayState,只读-
此动画的播放状态。
replaceState
,类型为AnimationReplaceState,只读-
此动画的替换状态。
pending
,类型为boolean,只读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
- DOMException of type
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提交计算样式:
-
对每个集合中的target:
-
如果target不是可以拥有样式属性[CSS-STYLE-ATTR]的元素(例如,它是一个伪元素或在不定义样式属性的文档格式中的元素),则抛出"
NoModificationAllowedError
"DOMException
并中止这些步骤。 -
如果在应用任何待处理的样式更改后,target未被渲染,则抛出"
InvalidStateError
"DOMException
并中止这些步骤。关于正在渲染的定义 [HTML] 以及 display: contents 仍在 讨论中。在本程序中,我们假设具有 display: contents 且通常会有相关布局框的元素(即它是 连接的,且不属于 display: none 子树的一部分)正在被渲染。
-
让inline style成为获取与target的样式属性对应的CSS 声明块的结果。如果target没有有样式属性,则令inline style成为一个新的空的CSS 声明块,所有者节点设置为target。
-
让targeted properties成为物理长手属性的集合,它是至少一个动画效果与动画关联的目标属性,animation的效果目标为target。
-
对于targeted properties中的每个属性,property:
-
更新inline style的样式属性。
-
6.4.1. AnimationPlayState
枚举
enum {
AnimationPlayState ,
"idle" ,
"running" ,
"paused" };
"finished"
6.4.2. AnimationReplaceState
枚举
enum {
AnimationReplaceState ,
"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()
返回的对象的某些属性是通用的,但它们的值可能会有所不同:-
duration
- 虽然getTiming()
可能返回字符串auto
,但getComputedTiming()
必须返回与迭代持续时间的计算值对应的数字,如duration
成员的描述中所定义EffectTiming
接口。在本规范的这个层次上,这仅意味着
auto
值被零替代。 -
fill
- 同样,getTiming()
可能返回字符串auto
,但getComputedTiming()
必须返回用于时间计算的特定FillMode,如fill
成员的描述中所定义的EffectTiming
接口。在本规范的这个层次上,这仅意味着
auto
值被none
替换FillMode。
注意: 未来可能会扩展其他时间成员以包括
auto
之类的值。进行时间计算时,鼓励作者尽可能使用getComputedTiming()
,以避免在允许指定值的范围或类型发生更改时出现不兼容情况。除了返回值可能的差异之外,与
getTiming()
相比,getComputedTiming()
返回了ComputedEffectTiming
字典定义的其他时间信息。 -
updateTiming(timing)
-
通过执行更新动画效果的时间属性的过程,传递
timing
参数作为输入,更新此动画效果的指定时间属性。optional
OptionalEffectTiming
timing-
要更新的时间属性。
timing
中不存在的任何成员的时间属性将不会被修改。
remove()
方法可用于从其父组或动画中移除效果。我们是否应该将其保留在第一级并仅将其定义为从动画中移除动画效果?[Issue #2082]
6.5.1.
EffectTiming
和 OptionalEffectTiming
字典
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
会扩展为时间模型所识别的填充模式之一,如下所示:如§ 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
的时间
要从EffectTiming
或OptionalEffectTiming
对象input中更新动画效果的时间属性,请执行以下步骤:
-
如果input的
iterationStart
成员存在且小于零,则抛出一个TypeError并中止此过程。注意: 之所以使用TypeError而不是RangeError,是为了反映WebIDL的[EnforceRange]注释的行为,假如将来可以将该注释用于浮点值。
-
如果input的
iterations
成员存在,且小于零或为NaN
,则抛出一个TypeError并中止此过程。 -
如果 input 的
easing
成员 存在,但无法使用 <easing-function> 生成式 [CSS-EASING-1] 解析,则 抛出一个 TypeError 异常并中止此过程。 -
将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 doublelocalTime
, 类型为 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
对象:-
创建一个新的
KeyframeEffect
对象,effect。 -
将 effect 的 目标元素 设置为 target。
-
将 目标伪选择器 设置为以下条件的第一个匹配结果。
- 如果 options 是一个
KeyframeEffectOptions
对象, -
将 目标伪选择器 设置为
pseudoElement
属性的值。在设置此属性时,应用接口上的
pseudoElement
setter 定义的错误处理。 如果 setter 需要抛出异常,则此过程必须抛出相同的异常并中止所有进一步的步骤。 - 否则,
-
将 目标伪选择器 设置为
null
。
- 如果 options 是一个
-
让 timing input 为以下条件的第一个匹配结果。
- 如果 options 是一个
KeyframeEffectOptions
对象, -
将 timing input 设置为 options。
- 否则(如果 options 是
double
), -
将 timing input 设置为一个新的
EffectTiming
对象,其中所有成员设置为其默认值,并且duration
设置为 options。
- 如果 options 是一个
-
调用过程 更新动画效果的时序属性,从 timing input 更新 effect。
如果该过程引发异常,则传播异常并中止此过程。
-
如果 options 是一个
KeyframeEffectOptions
对象,将 effect 的composite
属性分配为 options 的对应值。在分配此属性时,应用接口上对应 setter 定义的错误处理。 如果 setter 需要对 options 指定的值抛出异常,则此过程必须 抛出 相同的异常并中止所有进一步的步骤。
-
通过执行为
setKeyframes()
定义的过程,并将 keyframes 作为输入,初始化 关键帧 集。
Element
? target-
动画的 目标元素。 对于不针对特定元素的动画(例如使用音频 API 生成声音的动画),此值可以为
null
。 object? keyframes
-
要使用的 关键帧 集。 此参数的格式和处理在第 6.6.3 节 处理关键帧参数中定义。
optional
KeyframeEffectOptions
options-
指定效果的 迭代时长 的数字,或指定效果的时序和行为的一组属性。
此构造函数的使用示例可在第 6.6.1 节 创建新的 KeyframeEffect 对象中找到。
-
KeyframeEffect (source)
-
使用以下过程创建一个与
source
具有相同属性的新KeyframeEffect
对象:-
创建一个新的
KeyframeEffect
对象,effect。 -
使用 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"; };每个成员的含义和值如下:
由于 关键帧 由部分开放的字典类型表示,目前尚无法使用 WebIDL 表达,故此方法结果的准备过程以如下文字定义:
-
让 result 成为一个空的对象序列。
-
让 keyframes 为以下之一:
-
如果此 关键帧效果 与
CSSAnimation
关联,并且它的 关键帧 尚未被setKeyframes()
的成功调用替换; 则此 关键帧效果 的 计算的关键帧。 -
否则,应用过程 计算丢失的关键帧偏移 的结果,此 关键帧效果 的 关键帧。
注意: 我们返回 CSS 动画的 计算的关键帧, 因为并非所有在 CSS 中指定的关键帧都可以用字典表示。
-
-
对于 keyframes 中的每个 keyframe 执行以下步骤:
-
使用以下定义初始化一个字典对象 output keyframe:
dictionary
{BaseComputedKeyframe double ?
=offset null ;double
;computedOffset DOMString
= "linear";easing CompositeOperationOrAuto
= "auto"; };composite -
将
offset
、computedOffset
、easing
和composite
成员设置为 keyframe 的相应 关键帧偏移、 计算的关键帧偏移、 关键帧特定的 时序函数 和 关键帧特定的复合操作。 -
对于 keyframe 中的每个动画属性-值对 declaration,执行以下步骤:
-
将 property name 设置为通过将 declaration 的属性名称传递给算法将动画属性名称转换为 IDL 属性名称的结果。
-
将 IDL value 设置为通过将 declaration 传递给算法序列化 CSS 值 [CSSOM]得到的属性值的序列化结果。
-
将 value 设置为转换 IDL value 为 ECMAScript 字符串值的结果。
-
使用属性名称 property name 和属性描述符 { [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true, [[Value]]: value } 以及布尔标志 false, 调用 output keyframe 的[[DefineOwnProperty]] 内部方法。
-
-
将 output keyframe 添加到 result。
-
-
返回 result。
-
void setKeyframes(object? keyframes)
-
替换组成此效果的一组 关键帧。
object? keyframes
-
一系列关键帧,其格式和处理在 第 6.6.3 节 处理关键帧参数 中定义。
6.6.1. 创建一个新的 KeyframeEffect
对象
KeyframeEffect
构造函数提供了多种创建新的 KeyframeEffect
对象的方法。
在最简单的情况下,可以通过以下方式构造一个将 elem
的 "left" 属性在三秒内更改为 100px 的 KeyframeEffect
对象:
第二个参数,表示关键帧列表,可以指定多个属性。(参见 第 6.6.3 节 处理关键帧参数。)
// 一次性指定多个属性 var effectA= new KeyframeEffect( elem, { left: '100px' , top: '300px' }, 3000 ); // 指定多个关键帧 var effectB= new KeyframeEffect( elem, [ { left: '100px' }, { left: '300px' } ], 3000 );
第三个参数,表示动画的时序,可以简单地是一个表示以毫秒为单位的 迭代持续时间 的数字,如上所示,或者为了指定更多的时序属性,例如 开始延迟,可以使用 EffectTiming
对象,如下所示:
如果未指定持续时间,则使用值 0。可以通过以下方式创建一个简单设置属性而不进行任何插值的动画:
然而,正如在 第 4.6 节 填充行为 中所描述的,这种使用无限填充动画的方式是不建议的。
创建一个 KeyframeEffect
后,可以将其添加到 Animation
中然后播放该动画。然而,对于简单效果来说,Element.animate
快捷方式更为方便,因为它会自动执行这些步骤。例如,
6.6.2. 属性名称和IDL名称
property 的 动画属性名称到IDL属性名称 转换算法如下:
-
如果 property 符合 <custom-property-name> 的形式,返回 property。
-
如果 property 指的是CSS属性 float,返回字符串 "cssFloat"。
-
如果 property 指的是CSS属性 offset,返回字符串 "cssOffset"。
-
否则,返回将 property 传入 CSS属性到IDL属性 的算法结果 [CSSOM]。
attribute 的 IDL属性名称到动画属性名称 转换算法如下:
-
如果 attribute 符合 <custom-property-name> 的形式,返回 attribute。
-
如果 attribute 是字符串 "cssFloat",则返回表示CSS属性 float 的动画属性。
-
如果 attribute 是字符串 "cssOffset",则返回表示CSS属性 offset 的动画属性。
-
否则,返回将 attribute 传入 IDL属性到CSS属性 的算法结果 [CSSOM]。
6.6.3.
处理 keyframes
参数
以下方法均接受一组关键帧作为参数:
-
setKeyframes()
方法在KeyframeEffect
接口上, -
animate()
方法在Animatable
接口混入中。
此参数可以采用以下两种形式之一,如下所示。
// 以下两种表达式产生相同的结果: 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 propertyNamedouble ?offset =null ;DOMString easing = "linear";CompositeOperationOrAuto composite = "auto"; };dictionary PropertyIndexedKeyframes { // ... 属性-值 和 属性-值列表 对 ... // 即 (DOMString 或 sequence<DOMString>) 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
由于此类型无法在 WebIDL 中表示,因此其处理方式在以下文本中定义。
对于接受 keyframes
参数的每个方法,对输入运行 处理 keyframes 参数 的过程,并保留该过程的结果。
首先,我们定义两个支持定义。
指令 检查完成记录 的 结果,其中 结果 是来自调用 ECMAScript 操作的 完成记录,相当于以下步骤:
-
如果 结果 是 突然完成,则抛出 结果 的 [[value]] 字段中包含的异常并中止该过程。
-
用 结果 的 [[value]] 字段中的值替换 结果。
将 处理类似关键帧对象 的过程分为两个参数:
-
一个 ECMAScript 对象 keyframe input 和
-
一个 allow lists 布尔标志
并返回一个映射,将属性名称映射到 DOMString 值(如果 allow lists 为 false),或者将属性名称映射到字符串值序列,否则,将使用以下过程:
-
运行 将 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
= "linear";easing CompositeOperationOrAuto
= "auto"; };composite
将此过程的结果存储为 keyframe output。
-
构建一个 animatable properties 列表如下:
-
让 animatable properties 成为一个属性名称列表(包括具有可动画长手子属性的简写属性),这些属性可以由实现进行动画处理。
-
通过应用 动画属性名称到 IDL 属性名称 算法,将 animatable properties 中的每个属性名称转换为等效的 IDL 属性。
-
-
让 input properties 成为通过调用 EnumerableOwnNames 操作获得的结果,并将 keyframe input 作为对象。
-
组成一个新的列表 animation properties,该列表包含在 input properties 和 animatable properties 中的所有属性,或 在 input properties 中的属性并符合 <custom-property-name> 生产规则。
-
按每个属性名称定义的 Unicode 代码点对 animation properties 进行升序排序。
-
对于 animation properties 中的每个 property name,
-
让 raw value 成为通过调用 [[Get]] 内部方法,并将 property name 作为属性键,将 keyframe input 作为接收器的结果。
-
检查完成记录 的 raw value。
-
将 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 的结果。
-
计算 normalized property name,该名称是将 property name 应用到 IDL 属性名称到动画属性名称 算法的结果。
-
将属性添加到 keyframe output,其属性名称为 normalized property name,属性值为 property values。
-
-
返回 keyframe output。
将 处理 keyframes 参数 的过程分为可为空的 ECMAScript 对象 object 作为输入,并返回一个关键帧序列,使用以下过程:
-
如果 object 为空,返回一个空的关键帧序列。
-
让 processed keyframes 成为一个空的 关键帧 序列。
-
让 method 成为 GetMethod(object, @@iterator) 的结果。
-
检查完成记录 的 method。
-
执行与以下第一个匹配条件对应的步骤,
- 如果 method 不为 undefined,
-
-
让 iter 成为 GetIterator(object, method)。
-
检查完成记录 的 iter。
-
重复:
-
让 next 成为 IteratorStep(iter)。
-
检查完成记录 的 next。
-
如果 next 为 false,中止此循环。
-
让 nextItem 成为 IteratorValue(next)。
-
检查完成记录 的 nextItem。
-
如果 Type(nextItem) 不是 Undefined、Null 或 Object,则抛出 TypeError 并中止这些步骤。
-
将结果追加到 processed keyframes,通过运行 处理类似关键帧对象 过程,传递 nextItem 作为 keyframe input,并将 allow lists 标志设置为 false。
-
-
- 否则,
-
-
让 property-indexed keyframe 成为运行 处理类似关键帧对象 过程的结果,将 object 作为 keyframe input,并将 allow lists 标志设置为 true。
-
对于 property-indexed keyframe 中的每个成员 m,执行以下步骤:
-
让 property name 成为 m 的键。
-
如果 property name 是 "composite"、"easing" 或 "offset",跳过此循环中的剩余步骤,并从 property-indexed keyframe 中的下一个成员 m 继续。
-
让 property values 成为 m 的值。
-
让 property keyframes 成为一个空的 关键帧 序列。
-
对于 property values 中的每个值 v,执行以下步骤:
-
将 计算丢失的关键帧偏移 过程应用于 property keyframes。
-
将 关键帧 添加到 processed keyframes 中。
-
-
让 offsets 成为一个 可为空 的
double
值序列,基于 property-indexed keyframe 的 "offset" 成员的类型,分配如下:sequence<double?>
,-
"offset" 的值原样保留。
double?
,-
一个长度为一的序列,其值为 "offset",即 «
offset
»,
-
将 offsets 中的每个值分配给 processed keyframes 中对应位置的 关键帧偏移,直到到达任一序列的末尾。
-
让 easings 成为
DOMString
值的序列,基于 property-indexed keyframe 的 "easing" 成员的类型,分配如下:sequence<DOMString>
,-
"easing" 的值原样保留。
DOMString
,-
一个长度为一的序列,其值为 "easing",即 «
easing
»,
-
如果 easings 是一个空序列,则将其设为长度为一的序列,其中包含单个值 "linear",即 « "linear" »。
-
如果 easings 的项数少于 processed keyframes,则从列表的开头开始,连续重复 easings 中的元素,直到 easings 的项数与 processed keyframes 相等。
-
如果 easings 的项数多于 processed keyframes,则将多余的项存储为 unused easings。
-
将 easings 中的每个值分配给与 processed keyframes 中对应位置的 关键帧 上的 "easing" 属性,直到到达 processed keyframes 的末尾。
-
如果 property-indexed keyframe 的 "composite" 成员 不是 一个空序列:
-
让 composite modes 成为从 property-indexed keyframe 的 "composite" 成员中分配的
CompositeOperationOrAuto
值的序列。如果该成员是一个CompositeOperationOrAuto
值操作的单个值,则将 composite modes 设为长度为一的序列,其中值为 "composite"。 -
与 easings 类似,如果 composite modes 的项数少于 processed keyframes,则从列表的开头开始,连续重复 composite modes 中的元素,直到 composite modes 的项数与 processed keyframes 相等。
-
将 composite modes 中不为
auto
的每个值分配给与 processed keyframes 中对应位置的 关键帧特定复合操作 上的 关键帧,直到到达 processed keyframes 的末尾。
-
-
-
如果 processed keyframes 未按 松散排序的偏移 排序,则抛出 TypeError 并中止这些步骤。
-
如果 processed keyframes 中存在任何 关键帧,其 关键帧偏移 非空且小于零或大于一,则抛出 TypeError 并中止这些步骤。
-
对于 processed keyframes 中的每个 frame,执行以下步骤:
-
对于 frame 中的每个属性-值对,使用为该属性定义的语法解析属性值。
如果属性值根据该属性的语法无效,则丢弃属性-值对。提供支持诊断内容错误的用户代理应该生成适当的警告,以突出显示无效的属性值。
-
让 frame 的 时间函数 成为通过使用为
easing
属性在EffectTiming
字典中定义的 CSS 语法解析 "easing" 属性的结果。如果解析 "easing" 属性失败,抛出 TypeError 并中止此过程。
注意: 在上述两个步骤中使用 CSS 解析器意味着允许 CSS 注释和转义,但在成功解析值时不保留这些内容。
注意: 在 "easing" 属性解析失败的情况下,重要的是在从 object 读取所有属性后 抛出 TypeError,因为未能这样做是可观察的,并且如果以后在 WebIDL 中部分支持开放式字典,则不会匹配行为。
-
-
使用在
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
6.7.
CompositeOperation
和 CompositeOperationOrAuto
枚举
关键帧效果 的合成行为可能的值由 CompositeOperation 枚举表示。
enum {
CompositeOperation "replace" ,"add" ,"accumulate" };
replace
add
accumulate
关键帧 的合成行为的可能值与 CompositeOperation
枚举共享相同的值,以及附加的 auto
值。
enum {
CompositeOperationOrAuto "replace" ,"add" ,"accumulate" ,"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)
-
执行以下步骤:
-
令 target 为调用此方法的对象。
-
使用与
KeyframeEffect(target, keyframes, options)
构造函数相同的过程,在 target 的 相关 Realm 中构造一个新的KeyframeEffect
对象 effect,传递 target 作为 target 参数,keyframes 和 options 参数如所提供的那样。如果上述过程引发异常,则传播该异常并中止此过程。
-
如果 options 是
KeyframeAnimationOptions
对象,令 timeline 为 options 的timeline
成员,或者,如果 options 的timeline
成员缺失,则为调用此方法的元素的 默认文档时间线。 -
使用与
Animation()
构造函数相同的过程,在 target 的 相关 Realm 中构造一个新的Animation
对象 animation,传递 effect 和 timeline 作为同名参数。 -
如果 options 是
KeyframeAnimationOptions
对象,将 options 的id
成员的值分配给 animation 的id
属性。 -
运行 播放动画 的过程,animation 的 auto-rewind 标志设置为 true。
-
返回 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
-
为创建的
KeyframeEffect
和Animation
提供的时间和动画选项。
-
sequence<Animation> getAnimations(options)
-
返回该对象的 相关动画 集合,或者,如果传递了
options
参数,并且将subtree
设置为 true,则返回该对象的 子树相关动画 集合。返回的列表按照 动画 相关的效果在 § 5.4.2 效果栈 中描述的组合顺序进行排序。
调用此方法会触发 样式更改事件,适用于 目标元素。 因此,返回的列表反映了应用任何未处理的样式更改后的状态,例如尚未处理的动画相关样式属性的更改。
options
-
管理
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 (); };
6.11.
对 Element
接口的扩展
由于 DOM 元素可以作为动画的目标,Element
接口在 [DOM] 中扩展如下:
Element includes Animatable ;
这允许以下用法。
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
-
请参见
currentTime
属性的描述。 timelineTime
, 类型为 double,可为空,默认值为null
-
请参见
timelineTime
属性的描述。
6.13. 模型活跃性
对模型的任何部分进行更改,都会导致整个时间模型以及任何依赖的样式更新。
除非另有说明, 调用此规范编程接口部分定义的方法或构造函数, 或者获取或设置成员, 不会触发样式更改事件。
注意: 扩展此规范的其他规范预计将通过引入触发 这种事件的情况来细化样式更改事件的要求。 例如, 当此规范中的接口表示 由 CSS 标记定义的动画时, 其中的许多方法将需要触发样式更改事件 以反映对指定样式的更改。
基于上述要求和本规范中其他地方的规范性要求,可以观察到以下不变量:
- 对 Web 动画模型所做的更改立即生效
-
例如,如果与
KeyframeEffect
相关联的Animation
通过编程接口进行定位(参见 § 4.4.4 设置动画的当前时间), 则查询动画的startTime
时返回的值将立即反映模型的更新状态。 - 查询受动画影响的属性的计算样式将返回动画的完全最新状态
-
例如,如果在将新的
Animation
应用于元素后立即查询该元素的已使用样式, 则新动画的结果将包含在返回的值中。 - 在同一任务内进行的更改被同步,以便整个更改集一起呈现
-
由于对模型的更改立即生效并结合 ECMAScript 的运行到完成语义, 因此永远不会出现例如仅应用指定样式的更改而不应用动画的情况。
// 使用回退淡化不支持 Element.animate 的浏览器的透明度 // 设置过渡起点 elem. style. opacity= '0' ; elem. animate([ { opacity: 1 }, { opacity: 0 } ], 500 ); 注意,然而,在上述示例中,用户代理可能会呈现 未应用上述任何更改的帧。 例如,如果渲染发生在一个单独的进程中,并且该进程计划在 上述任务完成后不久运行,但在更改 可以传达给该进程之前。
- 通过 文档时间线 的
currentTime
属性返回的值在任务内不会更改 -
由于要求时间线每次运行 更新动画并发送事件 过程时更新其当前时间, 在同一个脚本块中执行的长代码块中两次查询
currentTime
将返回相同的值,如以下示例所示。 - 传递给
requestAnimationFrame
回调的时间将等于document.timeline.currentTime
-
由于 HTML 的 事件循环处理模型 定义了 更新动画并发送事件 的过程在运行动画帧回调之前执行, 并且由于传递给这些回调的时间与传递给这两个过程的 now 时间戳相同,默认文档时间线的 当前时间应该 与传递给
requestAnimationFrame
的时间一致。 - 从此编程接口调用方法通常不会触发过渡
-
请考虑以下示例:
// 设置过渡起点 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 日工作草案 以来,进行了以下更改:
-
更新了设置播放速率的过程,以保持开始时间 并在非单调时间线上翻转播放速率时交换开始时间位置。
-
将相关动画的定义分解出来,供其他规范使用。
-
更新了当前动画效果的定义, 使其在非空闲且与非递增时间线相关联时被视为当前效果。
-
修正了 § 4.6 填充行为 部分的代码示例。
更新日志提供了更详细的历史记录。
附录 A: 现有属性的动画类型
通常,属性的动画类型会与其定义一起包含。 然而,对于一些在较旧或非常成熟的规范中定义的属性, 动画类型信息未被包含。 所有这些属性都被假定为具有 通过计算值的动画类型, 除非它们是下面列出的例外之一。
font-weight 的动画
font-weight 属性值在第 4 级之前按如下方式组合:
-
通过离散步长(100 的倍数)插值。 插值在实数空间中进行,如 <number>,并通过四舍五入到最接近的 100 倍数转换为整数, 100 倍数之间的中间值向正无穷大舍入。
-
加法的 font-weight值定义为 Vresult = Va + Vb
注意: 此定义已被[CSS-FONTS-4]废弃, 其中关于font-weight值必须是 100 的倍数的要求被取消。 此时,font-weight的动画类型仅为通过计算值。
visibility 的动画
对于 visibility 属性,visible 的插值方式为 离散步长, 其中 p 的值介于 0 和 1 之间时 映射到 visible,p 的其他值映射到更接近的端点; 如果两个值都不是 visible,则使用离散动画。
box-shadow 和 text-shadow 的动画
对 box-shadow 或 text-shadow 属性的动画遵循组合 阴影列表的以下程序:
列表中的每个阴影 (将 none 视为长度为 0 的列表) 都按组件插值,类似于 通过计算值的行为。 但是,如果两个输入阴影都是 inset 或两个输入阴影都不是 inset, 则插值阴影在这方面必须与输入阴影匹配。 如果任何一对输入阴影有一个 inset 而另一个不是 inset, 则整个阴影列表使用离散动画。 如果阴影列表的长度不同, 则通过在末尾填充阴影来扩展较短的列表, 这些阴影的颜色为 透明, 所有长度为 0, 并且其 inset(或不是)与较长的列表匹配。
两个 Va 和 Vb 的加法定义为阴影列表的列表连接,使得 Vresult 等于 Va 扩展了 Vb。
阴影列表的累积遵循 上述插值的匹配规则,根据每个组件的类型执行加法, 或者如果 inset 值不匹配,则回退到离散动画。