Permalink
Fetching contributors…
Cannot retrieve contributors at this time
5864 lines (4583 sloc) 238 KB
</
<style>
.informative-bg {
margin: 1.5em 0 1em;
padding: 1em;
margin-top: 1em;
background: #efe;
border: green 1px dotted;
}
div.informative-bg *:last-child {
margin-bottom: 0;
}
div.informative-bg p:first-child {
margin-top: 0;
}
div.informative-bg h2,
div.informative-bg h3,
div.informative-bg h4 {
background: none;
}
div.switch > dl {
padding-left: 2em;
}
div.switch > dl > dt > p {
display: inline;
}
div.switch > dl > dt:before {
content: '\21AA';
padding: 0 0.5em 0 0;
display: inline-block;
width: 1em;
text-align: right;
line-height: 0.5em;
margin-left: -1.3em;
}
.attributes::before, .methods::before,
.parameters::before, .exceptions::before,
.constructors::before, .members::before {
font: bold 100% sans-serif;
text-align: left;
margin: 1.33em 0px;
color: #005A9C;
}
.attributes::before { content: 'Attributes' }
.methods::before { content: 'Methods' }
.parameters::before { content: 'Parameters' }
.exceptions::before { content: 'Exceptions' }
.constructors::before { content: 'Constructors' }
.members::before { content: 'Dictionary members' }
.param,
.exceptionname,
.estype,
.esvalue {
font-weight: bold;
}
.formula {
display: block;
margin: .5em 2em;
}
</style>
<pre class='metadata'>
Title: Web Animations
Status: ED
Work Status: Refining
Shortname: web-animations-1
TR: https://www.w3.org/TR/web-animations-1/
ED: https://drafts.csswg.org/web-animations-1/
Previous version: https://www.w3.org/TR/2018/WD-web-animations-1-20181011/
Previous version: https://www.w3.org/TR/2016/WD-web-animations-1-20160913/
Previous version: https://www.w3.org/TR/2015/WD-web-animations-1-20150707/
Previous version: https://www.w3.org/TR/2014/WD-web-animations-20140605/
Previous version: https://www.w3.org/TR/2013/WD-web-animations-20130625/
Version history: https://github.com/w3c/csswg-drafts/commits/master/web-animations-1
Level: 1
Group: csswg
!Participate: <a href="https://github.com/w3c/csswg-drafts/tree/master/web-animations-1">Fix the text through GitHub</a>
!Participate: Join the &lsquo;waapi&rsquo; channel on the <a href="https://damp-lake-50659.herokuapp.com/">Animation at Work</a> slack
!Participate: IRC: <a href="ircs://irc.w3.org:6667/webanimations">#webanimations</a> on W3C's IRC
Repository: w3c/csswg-drafts
!Issue Tracking: <a href="https://github.com/w3c/csswg-drafts/labels/web-animations-1">GitHub</a>
!Tests: <a href="https://github.com/web-platform-tests/wpt/tree/master/web-animations">web-platform-tests web-animations/</a> (<a href="https://github.com/web-platform-tests/wpt/labels/web-animations">ongoing work</a>)
Editor: Brian Birtles 43194, Mozilla, bbirtles@mozilla.com
Editor: Robert Flack 98451, Google Inc, flackr@google.com
Editor: Stephen McGruer 96463, Google Inc, smcgruer@google.com
Editor: Antoine Quint 51377, Apple Inc, graouts@apple.com
Former Editor: Shane Stephens 47691, Google Inc, shans@google.com
Former Editor: Douglas Stockwell, Google Inc, dstockwell@google.com
Former Editor: Alex Danilo 31960, Google Inc, adanilo@google.com
Former Editor: Tab Atkins 42199, Google Inc, jackalmage@gmail.com
Abstract: This specification defines a model for synchronization and
timing of changes to the presentation of a Web page.
This specification also defines an application programming interface
for interacting with this model and it is expected that further
specifications will define declarative means for exposing these
features.
Ignored Terms: double, boolean, DOMString, unsigned long, unrestricted double, (unrestricted double or DOMString)
Link Defaults: css-transforms (property) transform
</pre>
<pre class="anchors">
urlPrefix: https://heycam.github.io/webidl/#dfn-; type: dfn; spec: webidl
text: present
text: platform object
text: nullable; url: nullable-type
text: throw
text: thrown; url: throw
text: convert ecmascript to idl value
urlPrefix: https://heycam.github.io/webidl/; type: dfn; spec: webidl
text: [EnforceRange]; url: EnforceRange
text: es to dictionary
text: es to DOMString; url: es-to-DOMString
text: DOMString to es; url: DOMString-to-es
urlPrefix: http://www.ecma-international.org/ecma-262/6.0/#sec-; type: dfn; spec: ecma-262
text: code realms
text: completion record specification type
text: execution contexts
text: EnumerableOwnNames
text: GetIterator
text: GetMethod
text: IteratorStep
text: IteratorValue
text: Promise object; url: promise-objects
text: Promise; url: promise-objects
text: PromiseCapability record; url: promisecapability-records
text: Promise.resolve; url: promise.resolve
text: Type; url: ecmascript-data-types-and-values
text: well known symbols
text: [[DefineOwnProperty]]; url: ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc
text: [[Get]]; url: ordinary-object-internal-methods-and-internal-slots-get-p-receiver
urlPrefix: https://html.spec.whatwg.org/multipage/browsers.html; type: dfn; spec: html
text: active document
text: document associated with a window; url: concept-document-window
text: an entry with persisted user state
text: session history entry
urlPrefix: https://html.spec.whatwg.org/multipage/webappapis.html; type: dfn; spec: html
text: current global object
text: document.open(); url: dom-document-open
text: DOM manipulation task source
text: event loop processing model
text: queue a task
text: queue a microtask
text: relevant Realm; url: concept-relevant-realm
text: update the rendering
urlPrefix: https://html.spec.whatwg.org/multipage/webappapis.html; type: interface; spec: html
text: EventHandler
urlPrefix: https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html.html; type: dfn; spec: html
text: animation frame callbacks; url: animation-frames
text: run the animation frame callbacks
urlPrefix: https://html.spec.whatwg.org/multipage/embedded-content.html; type: dfn; spec: html
text: media element
urlPrefix: https://w3c.github.io/hr-time/; spec: highres-time
text: time origin; type: dfn
text: DOMHighResTimeStamp; type: interface
urlPrefix: https://dom.spec.whatwg.org/; type: dfn; spec: dom
text: create an event; url: concept-event-create
text: dispatch; url: concept-event-dispatch
text: constructing events
text: node document; url: concept-node-document
url: https://html.spec.whatwg.org/#document; type: interface; text: Document; spec: html
url: https://svgwg.org/svg2-draft/pservers.html#StopElementOffsetAttribute; type: element-attr; for: stop; text: offset; spec: svg2
url: https://svgwg.org/svg2-draft/mimereg.html#mime-registration; type: dfn; text: SVG MIME type; spec: svg2
urlPrefix: https://drafts.csswg.org/cssom/; type: dfn; spec: cssom
text: CSS property to IDL attribute
text: IDL attribute to CSS property
text: serialize a CSS value
urlPrefix: https://drafts.csswg.org/css-transitions/; type: dfn; spec: css-transitions-1
text: events from CSS transitions; url: transition-events
urlPrefix: https://drafts.csswg.org/css-animations/; type: dfn; spec: css-animations-1
text: events from CSS animations; url: events
urlPrefix: https://drafts.csswg.org/css-writing-modes-4/; type: dfn; spec: css-writing-modes-4
text: equivalent physical property; url: logical-to-physical
</pre>
<pre class="link-defaults">
spec:dom; type:interface; text:EventTarget
spec:dom; type:interface; text:Event
spec:css-values-3; type:type; text:<ident>
spec:css-backgrounds-3; type:property;
text:border-width
text:border-bottom-width
text:border-left-width
text:border-right-width
text:border-top-width
text:border-top-color
text:border-top
text:border-color
</pre>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({showMathMenu: false});
</script>
<script src="MathJax/MathJax.js?config=MML_SVG"></script>
<h2 id="introduction">Introduction</h2>
<div class='informative-bg'><em>This section is non-normative</em>
Web Animations defines a model for supporting animation and
synchronization on the Web platform.
It is intended that other specifications will build on this model and
expose its features through declarative means.
In addition, this specification also defines a programming interface to
the model that may be implemented by user agents that provide support
for scripting.
<h3 id="use-cases">Use cases</h3>
The Web Animations model is intended to provide the features necessary
for expressing CSS Transitions [[CSS-TRANSITIONS-1]],
CSS Animations [[CSS-ANIMATIONS-1]], and
SVG [[SVG11]].
As such, the use cases of Web Animations model is the union of use cases for
those three specifications.
The use cases for the programming interface include the following:
: Inspecting running animations
:: Often Web applications must wait for certain animated effects to complete
before updating some state.
The programming interface in this specification allows such applications
to wait for all currently running animation to complete,
regardless of whether they are defined by CSS Transitions, CSS Animations,
SVG animations, or created directly using the programming interface.
<div class='example'>
<pre class='lang-javascript'>
// Wait until all animations have finished before removing the element
Promise.all(
elem.getAnimations().map(animation =&gt; animation.finished)
).then(() =&gt; elem.remove());
</pre>
</div>
Alternatively, applications may wish to query the playback state of
animations without waiting.
<div class='example'>
<pre class='lang-javascript'>
const isAnimating = elem.getAnimations().some(
animation =&gt; animation.playState === 'running'
);
</pre>
</div>
: Controlling running animations
:: It is sometimes useful to perform playback control on animations
so that they can respond to external inputs.
For example, it may be necessary to pause all existing animations before
displaying a modal dialog so that they do not distract the user's
attention.
<div class='example'>
<pre class='lang-javascript'>
// Pause all existing animations in the document
document.getAnimations().forEach(
animation =&gt; animation.pause()
);
</pre>
</div>
: Creating animations from script
:: While it is possible to use ECMAScript to perform animation using
<code>requestAnimationFrame</code> [[!HTML]],
such animations behave differently to declarative animation in terms of
how they are represented in the CSS cascade and the performance
optimizations that are possible such as performing the animation on a
separate thread.
Using the Web Animations programming interface, it is possible to
create animations from script that have the same behavior and performance
characteristics as declarative animations.
<div class='example'>
<pre class='lang-javascript'>
// Fade out quickly
elem.animate({ transform: 'scale(0)', opacity: 0 }, 300);
</pre>
</div>
: Animation debugging
:: In a complex application, it may be difficult to determine how an
element arrived in its present state.
The Web Animations programming interface may be used to inspect
running animations to answer questions such as,
&ldquo;Why is the opacity of this element changing?&rdquo;
<div class='example'>
<pre class='lang-javascript'>
// Print the id of any opacity animations on elem
elem.getAnimations().filter(
animation =&gt;
animation.effect instanceof KeyframeEffect &&
animation.effect.getKeyframes().some(
frame =&gt; frame.hasOwnProperty('opacity')
)
).forEach(animation =&gt; console.log(animation.id));
</pre>
</div>
Likewise, in order to fine tune animations, it is often necessary to
reduce their playback rate and replay them.
<div class='example'>
<pre class='lang-javascript'>
// Slow down and replay any transform animations
elem.getAnimations().filter(
animation =&gt;
animation.effect instanceof KeyframeEffect &&
animation.effect.getKeyframes().some(
frame =&gt; frame.hasOwnProperty('transform')
)
).forEach(animation =&gt; {
animation.currentTime = 0;
animation.updatePlaybackRate(0.5);
});
</pre>
</div>
: Testing animations
:: In order to test applications that make use of animations it is often
impractical to wait for such animations to run to completion.
Rather, it is desirable to seek the animations to specific times.
<div class='example'>
<pre class='lang-javascript'>
// Seek to the half-way point of an animation and check that the opacity is 50%
elem.getAnimations().forEach(
animation =&gt;
animation.currentTime =
animation.effect.getComputedTiming().delay +
animation.effect.getComputedTiming().activeDuration / 2;
);
assert.strictEqual(getComputedStyle(elem).opacity, '0.5');
// Check that the loading screen is hidden after the animations finish
elem.getAnimations().forEach(
animation =&gt; animation.finish()
);
// Wait one frame so that event handlers have a chance to run
requestAnimationFrame(() =&gt; {
assert.strictEqual(
getComputedStyle(document.querySelector('#loading')).display, 'none');
});
</pre></div>
<h3 id="relationship-to-other-specifications">Relationship to other specifications</h3>
CSS Transitions [[CSS-TRANSITIONS-1]], CSS Animations [[CSS-ANIMATIONS-1]], and
SVG [[SVG11]] all provide mechanisms that
generate animated content on a Web page.
Although the three specifications provide many similar features,
they are described in different terms.
This specification proposes an abstract animation model that
encompasses the common features of all three specifications.
This model is backwards-compatible with the current behavior of these
specifications such that they can be defined in terms of this model
without any observable change.
The animation features in SVG 1.1 are defined in terms of SMIL
Animation [[SMIL-ANIMATION]].
It is intended that by defining SVG's animation features in terms of
the Web Animations model, the dependency between SVG and SMIL
Animation can be removed.
As with <a>animation frame callbacks</a> (commonly referred
to as &ldquo;requestAnimationFrame&rdquo;) [[HTML]],
the programming interface component of this specification allows
animations to be created from script.
The animations created using the interface defined in this
specification, however, once created, are executed entirely by the
user agent meaning they share the same performance characteristics as
animations defined by markup.
Using this interface it is possible to create animations
from script in a simpler and more performant manner.
The time values used within the programming interface
correspond with those used in <a>animation frame callbacks</a> [[HTML]]
and their execution order is defined such that the two interfaces can be used
simultaneously without conflict.
The programming interface component of this specification makes
some additions to interfaces defined in HTML [[!HTML]].
<h3 id="overview-of-this-specification">Overview of this specification</h3>
This specification begins by defining an abstract model for animation.
This is followed by a programming interface defined in terms of the
abstract model.
The programming interface is defined in terms of the abstract model
and is only relevant to user agents that provide scripting support.
</div>
<h2 id="spec-conventions">Specification conventions</h2>
This specification begins by describing abstract concepts such as [=animations=]
and [=animation effects=] and properties that belong to them such as their
[=playback rate=] or [=iteration duration=].
In addition to these properties, there are often specific procedures for
updating these properties such as the procedure to [=set the playback rate=] or
the procedure to [=set the start time=] of an animation.
Where this specification does not specifically link to a procedure, text
that requires the user agent to update a property such as, &ldquo;make
|animation|'s [=start time=] [=unresolved=]&rdquo;, should be understood to
refer to updating the property directly <em>without</em> invoking any related
procedure.
Further documentation conventions that are not specific to this specification
are described in [[#document-conventions]].
<h2 id="web-animations-model-overview">Web Animations model overview</h2>
<div class='informative-bg'><em>This section is non-normative</em>
At a glance, the Web Animations model consists of two largely
independent pieces, a <em>timing model</em> and an <em>animation
model</em>. The role of these pieces is as follows:
: Timing model
:: Takes a moment in time and converts it to a proportional distance
within a single iteration of an animation called the <em>iteration
progress</em>.
The <em>iteration index</em> is also recorded since some animations vary
each time they repeat.
: Animation model
:: Takes the <em>iteration progress</em> values and <em>iteration indices</em>
produced by the timing model and converts them into a series of values
to apply to the target properties.
Graphically, this flow can be represented as follows:
<figure>
<img src="img/timing-and-animation-models.svg" width="600"
alt="Overview of the operation of the Web Animations model.">
<figcaption>
Overview of the operation of the Web Animations model.<br>
The current time is input to the timing model which produces an iteration
progress value and an iteration index.<br>
These parameters are used as input to the animation model which produces
the values to apply.
</figcaption>
</figure>
For example, consider an animation that:
* starts after 3 seconds
* runs twice,
* takes 2 seconds every time, and
* changes the width of a rectangle from 50 pixels to 100 pixels.
The first three points apply to the timing model.
At a time of 6 seconds, it will calculate that the animation should be
half-way through its second iteration and produces the result 0.5.
The animation model then uses that information to calculate a width.
This specification begins with the timing model and then proceeds to
the animation model.
</div>
<h2 id="timing-model">Timing model</h2>
This section describes and defines the behavior of the Web Animations
timing model.
<h3 id="timing-model-overview">Timing model overview</h3>
<div class='informative-bg'>
<em>This section is non-normative</em>
Two features characterize the Web Animations timing model: it is
<em>stateless</em> and it is <em>hierarchical</em>.
<h4 id="stateless">Stateless</h4>
The Web Animations timing model operates by taking an input time and
producing an output iteration progress.
Since the output is based solely on the input time and is independent
of previous inputs, the model may be described as stateless.
This gives the model the following properties:
: Frame-rate independent
:: Since the output is independent of previous inputs, the rate at
which the model is updated will not affect its progress.
Provided the input times are proportional to the progress of
real-world time, animations will progress at an identical rate
regardless of the capabilities of the device running them.
: Direction-agnostic
:: Since the sequence of inputs is insignificant, the model is
directionless.
This means that the model can be updated to an arbitrary moment
without requiring any specialized handling.
: Constant-time seeking
:: Since each input is independent of the previous input, the
processing required to perform a seek operation, even far into the
future, is at least potentially constant.
There are a few exceptions to the stateless behavior of the timing
model.
Firstly, a number of methods defined in the <a
href="#programming-interface" section>programming interface</a> to the model
provide play control such as pausing an animation.
These methods are defined in terms of the time at which they are
called and are therefore stative.
These methods are provided primarily for convenience and are not part
of the core timing model but are layered on top.
Similarly, the <a href="#reaching-the-end" section>finishing behavior</a> of
animations means that dynamic changes to the end time of
the media (target effect) of an animation may produce a
different result depending on when the change occurs.
This behavior is somewhat unfortunate but has been deemed intuitive
and consistent with HTML.
As a result, the model can only truly be described as stateless
<em>in the absence of dynamic changes to its timing properties</em>.
Finally, each time the model is updated, it can be considered to
establish a temporary state.
While this temporary state affects the values returned from the <a
href="#programming-interface" section>programming interface</a>, it has no
influence on the subsequent updates and hence does not conflict with
the stateless qualities described above.
<h4 id="hierarchical">Hierarchical</h4>
The other characteristic feature of the timing model is that time is inherited.
Time begins at a timeline and cascades down a number of steps to each
animation effect.
At each step, time may be shifted backwards and forwards, scaled,
reversed, paused, and repeated.
<figure>
<img src="img/time-hierarchy.svg" width="350"
alt="A hierarchy of timing nodes">
<figcaption>
A hierarchy of timing nodes.
Each node in the tree derives its time from its parent node.
</figcaption>
</figure>
In this level of the specification the hierarchy is shallow.
A subsequent level of this specification will introduce the concept
of group effects which allows for deeper timing hierarchies.
</div>
<h3 id="time-value-section">Time values</h3>
Timing is based on a hierarchy of time relationships between timing nodes.
Parent nodes provide timing information to their child nodes in the form
of <a>time values</a>.
A <dfn>time value</dfn> is a real number which nominally represents
a number of milliseconds from some moment.
The connection between <a>time values</a> and wall-clock milliseconds
may be obscured by any number of transformations applied to the value as
it passes through the time hierarchy.
<p class="note">
In the future there may be timelines that are based on scroll position
or UI gestures in which case the connection between time values and
milliseconds will be weakened even further.
</p>
A <a>time value</a> may also be <dfn>unresolved</dfn> if, for example,
a timing node is not in a state to produce a <a>time value</a>.
<h3 id="timelines">Timelines</h3>
A <dfn>timeline</dfn> provides a source of <a>time values</a> for the
purpose of synchronization.
At any given moment, a [=timeline=] has a single current [=time value=] known
simply as the timeline's <dfn lt="timeline current time">current time</dfn>.
A <a>timeline</a> may not always be able to return a meaningful <a>time
value</a>, but only an <a>unresolved</a> time value. For example, it may
be defined relative to a moment that has yet to occur, such as the firing of
a document's load event.
A <a>timeline</a> is considered to be <dfn lt="inactive timeline">inactive</dfn>
when its <a>time value</a> is <a>unresolved</a>.
Specific types of [=timelines=] may define a procedure to <dfn lt="timeline time
to origin-relative time" export>convert a timeline time to an
origin-relative time</dfn> for [=time value=] |time|, so that the [=time
values=] produced by wallclock-based timelines can be compared.
A <a>timeline</a> may be <dfn lt="timeline associated with
a document">associated with a document</dfn>.
When asked to <dfn export>update animations and send events</dfn> for
a {{Document}} |doc| at timestamp |now|, run these steps:
1. Update the <a lt="timeline current time">current time</a> of all
timelines <a lt="timeline associated with a document">associated with
|doc|</a> passing |now| as the timestamp.
<div class="note">
Due to the hierarchical nature of the timing model, updating the
<a lt="timeline current time">current time</a> of a [=timeline=] also
involves:
* Updating the [=current time=] of any [=animations=] <a lt="associated
with a timeline">associated with</a> the timeline.
* Running the [=update an animation's finished state=] procedure for any
animations whose [=current time=] has been updated.
* Queueing [=animation events=] for any such animations.
</div>
1. [=Perform a microtask checkpoint=].
Note: This is to ensure that any microtasks queued up as a result of
resolving or rejecting Promise objects as part of updating timelines in the
previous step, run their callbacks prior to dispatching animation events.
1. Let |events to dispatch| be a copy of |doc|'s [=pending animation event
queue=].
1. Clear |doc|'s [=pending animation event queue=].
1. Perform a stable sort of the [=animation events=] in |events to dispatch|
as follows:
1. Sort the events by their [=scheduled event time=] such that events
that were scheduled to occur earlier, sort before events scheduled to
occur later and events whose scheduled event time is
[=unresolved=] sort before events with a <a lt=unresolved>resolved</a>
scheduled event time.
1. Within events with equal [=scheduled event times=], sort by their
[=composite order=].
Note: The purpose of sorting events is to ensure that, as best possible,
even on devices with differing capabilities and hence different frame
rates, events are dispatched in a consistent order.
Note: The requirement for the sort to be a stable sort is because sometimes
events may be queued with the same scheduled event time. For example, a CSS
animation with a duration of zero, may dispatch both
an <code>animationstart</code> and an <code>animationend</code> event and
the order of these events should be preserved.
1. [=Dispatch=] each of the events in |events to dispatch| at their
corresponding target using the order established in the previous step.
It is often convenient to describe each time this procedure is invoked as
establishing a new <dfn export>animation frame</dfn>.
Changes to the timing properties of [=animations=] or [=animation effects=], or
the addition and removal of the objects may cause the output of the timing or
animation model to change, but these operations in themselves do not create
a new [=animation frame=], rather they merely update the current <a>animation
frame</a>.
<h4 id="document-timelines">Document timelines</h4>
A <dfn>document timeline</dfn> is a type of <a>timeline</a> that is <a
lt="timeline associated with a document">associated
with a document</a> and whose <a lt="timeline current time">current time</a>
is calculated as a fixed offset from the |now| timestamp provided each time the
[=update animations and send events=] procedure is run.
This fixed offset is referred to as the document timeline's <dfn>origin
time</dfn>.
Issue(2079): There must be a better term than &ldquo;origin time&rdquo;&mdash;
it's too similar to &ldquo;time origin&rdquo;.
Prior to establishing the [=time origin=] for its associated document,
a [=document timeline=] is <a lt="inactive timeline">inactive</a>.
A <a>document timeline</a> that is associated with a {{Document}} which is not
an <a>active document</a> is also considered to be
<a lt="inactive timeline">inactive</a>.
To <a lt="timeline time to origin-relative time">convert a timeline
time, |timeline time|, to an origin-relative time</a> for a document timeline,
|timeline|, return the sum of the |timeline time| and |timeline|'s [=origin
time=]. If |timeline| is inactive, return an [=unresolved=] [=time value=].
<h4 id="the-documents-default-timeline">The default document timeline</h4>
Each {{Document}} has a <a>document timeline</a> called the <dfn>default
document timeline</dfn>.
The <a>default document timeline</a> is unique to each document and persists for
the lifetime of the document including calls to <a>document.open()</a> [[!HTML]].
The <a>default document timeline</a> has an <a>origin time</a> of zero.
<div class="informative-bg"><em>This section is non-normative</em>
Since no scaling is applied to the |now| timestamp values provided
to [=document timelines=], the <a>time values</a> it produces will be
proportional to wall-clock milliseconds.
Furthermore, since the <a>time values</a> of the <a>default document
timeline</a> have a zero offset from the [=time origin=],
<code>document.timeline.currentTime</code> will roughly correspond to <a
href="https://www.w3.org/TR/hr-time/#dom-performance-now">
<code>Performance.now()</code></a> [[HR-TIME]] with the exception that
<code>document.timeline.currentTime</code> does not change in between calls
to the [=update animations and send events=] procedure.
</div>
<h3 id="animations">Animations</h3>
<div class="informative-bg"><em>This section is non-normative</em>
The children of a <a>timeline</a> are called <em>animations</em>.
An animation takes an <a>animation effect</a> which is a static
description of some timed behavior and binds it to a <a>timeline</a>
so that it runs.
An animation also allows run-time control of the connection between the
<a>animation effect</a> and its <a>timeline</a> by providing pausing,
seeking, and speed control.
The relationship between an animation and an <a>animation effect</a> is
analogous to that of a DVD player and a DVD.
</div>
An <dfn id="concept-animation">animation</dfn> connects a single <a>animation
effect</a>, called its <dfn>target effect</dfn>, to a <a>timeline</a> and
provides playback control.
Both of these associations are optional and configurable such that
an <a>animation</a> may have no associated <a>target effect</a> or
<a>timeline</a> at a given moment.
An [=animation's=] <dfn>document for timing</dfn> is the {{Document}} with which
its [=timeline=] is <a lt="timeline associated with a document">associated</a>.
If an animation is not associated with a timeline, or its timeline is not
associated with a document, then it has no [=document for timing=].
An <a>animation</a>'s <dfn>start time</dfn> is the
<a>time value</a> of its <a>timeline</a> when its <a>target effect</a>
is scheduled to begin playback.
An animation's start time is initially <a>unresolved</a>.
An <a>animation</a> also maintains a <dfn>hold time</dfn> <a>time value</a>
which is used to fix the animation's output <a>time value</a>, called its
<a>current time</a>, in circumstances such as pausing</a>.
The <a>hold time</a> is initially <a>unresolved</a>.
In order to establish the relative ordering of conflicting <a>animations</a>,
animations are appended to a <dfn>global animation list</dfn> in the order
in which they are created. Certain <a lt="animation class">classes of
animations</a>, however, may provide alternative means of ordering animations
(see [[#animation-classes]]).
<h4 id="setting-the-timeline">Setting the timeline of an animation</h4>
The procedure to <dfn>set the timeline of an animation</dfn>,
<var>animation</var>, to <var>new timeline</var> which may be null, is as
follows:
1. Let <var>old timeline</var> be the current <a>timeline</a> of
<var>animation</var>, if any.
1. If <var>new timeline</var> is the same object as <var>old timeline</var>,
abort this procedure.
1. Let the <a>timeline</a> of <var>animation</var> be <var>new timeline</var>.
1. If the [=start time=] of <var>animation</var> is <a
lt=unresolved>resolved</a>, make <var>animation</var>'s <a>hold time</a>
<a>unresolved</a>.
Note: This step ensures that the <a>finished play state</a> of
<var>animation</var> is not &ldquo;sticky&rdquo; but is re-evaluated
based on its updated <a>current time</a>.
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to false, and
the <var>synchronously notify</var> flag set to false.
<h4 id="responding-to-a-newly-inactive-timeline">Responding to a newly inactive
timeline</h4>
Issue(2080): With the set of timelines defined in this level of this
specification, this situation is not expected to occur. As
a result, this section will likely be moved to a subsequent level
of this specification.
When the <a>timeline</a> associated with an <a>animation</a>,
<var>animation</var>, becomes newly <a lt="inactive timeline">inactive</a>,
if <var>animation</var>'s <a>previous current time</a> is <a
lt="unresolved">resolved</a>, the procedure to <a>silently set the current
time</a> of <var>animation</var> to <a>previous current time</a> is run.
<div class="note">
This step makes the behavior when an <a>animation</a>'s <a>timeline</a> becomes
<a lt="inactive timeline">inactive</a> consistent with when it is
disassociated with a <a>timeline</a>.
Furthermore, it ensures that the only occasion on which an <a>animation</a>
becomes <a lt="idle play state">idle</a>, is when the procedure to
<a>cancel an animation</a> is performed.
</div>
<h4 id="setting-the-target-effect">Setting the target effect of an
animation</h4>
The procedure to <dfn>set the target effect of an animation</dfn>,
<var>animation</var>, to <var>new effect</var> which may be null, is as
follows:
1. Let <var>old effect</var> be the current <a>target effect</a> of
<var>animation</var>, if any.
1. If <var>new effect</var> is the same object as <var>old effect</var>,
abort this procedure.
1. If <var>animation</var> has a <a>pending pause task</a>, reschedule that
task to run as soon as <var>animation</var> is <a>ready</a>.
1. If <var>animation</var> has a <a>pending play task</a>, reschedule that task
to run as soon as <var>animation</var> is <a>ready</a> to play <var>new
effect</var>.
1. If <var>new effect</var> is not <code>null</code> and
if <var>new effect</var> is the <a>target effect</a> of another
<a>animation</a>, <var>previous animation</var>, run the procedure to <a>set
the target effect of an animation</a> (this procedure) on <var>previous
animation</var> passing null as <var>new effect</var>.
1. Let the <a>target effect</a> of <var>animation</var> be <var>new
effect</var>.
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to false, and
the <var>synchronously notify</var> flag set to false.
The procedure to <dfn>reset an animation's pending tasks</dfn> for
<var>animation</var> is as follows:
1. If <var>animation</var> does not have a <a>pending play task</a> or a
<a>pending pause task</a>, abort this procedure.
1. If <var>animation</var> has a <a>pending play task</a>, cancel that task.
1. If <var>animation</var> has a <a>pending pause task</a>, cancel that task.
1. [=Apply any pending playback rate=] on |animation|.
1. <a lt="reject a Promise">Reject</a> <var>animation</var>'s <a>current ready
promise</a> with a DOMException named "AbortError".
1. Let <var>animation</var>'s <a>current ready promise</a> be the result of
<a lt="create a new resolved Promise">creating a new resolved Promise
object</a>.
<h4 id="the-current-time-of-an-animation">The current time of an animation</h4>
<a>Animations</a> provide a <a>time value</a> to their <a>target
effect</a> called the animation's <dfn>current time</dfn>.
The <a>current time</a> is calculated from the first
matching condition from below:
<div class="switch">
: If the animation's <a>hold time</a> is <a lt="unresolved">resolved</a>,
:: The <a>current time</a> is the animation's <a>hold time</a>.
: If <em>any</em> of the following are true:
1. the animation has no associated <a>timeline</a>, or
2. the associated <a>timeline</a> is
<a lt="inactive timeline">inactive</a>, or
3. the animation's [=start time=] is <a>unresolved</a>.
:: The <a>current time</a> is an <a>unresolved</a> time value.
: Otherwise,
::
<blockquote>
<code><a>current time</a> = (<var>timeline time</var> - [=start time=])
&times; [=playback rate=]</code>
</blockquote>
Where <var>timeline time</var> is the current <a>time value</a> of
the associated <a>timeline</a>.
The [=playback rate=] value is defined in [[#speed-control]].
</div>
<h4 id="setting-the-current-time-of-an-animation">Setting the current time of an animation</h4>
The <a>current time</a> of an animation can be set to a new value to
<em>seek</em> the animation.
The procedure for setting the current time is defined in two parts.
The procedure to <dfn>silently set the current time</dfn> of
an animation, <var>animation</var>, to <var>seek time</var> is as follows:
1. If <var>seek time</var> is an <a>unresolved</a> time value,
then perform the following steps.
1. If the <a>current time</a> is <a lt=unresolved>resolved</a>, then
<a>throw</a> a <span class=exceptionname>TypeError</span>.
1. Abort these steps.
2. Update either <var>animation</var>'s <a>hold time</a> or
[=start time=] as follows:
<div class="switch">
: If <em>any</em> of the following conditions are true:
* <var>animation</var>'s <a>hold time</a> is
<a lt="unresolved">resolved</a>, or
* <var>animation</var>'s [=start time=]
is <a lt="unresolved">unresolved</a>, or
* <var>animation</var> has no associated <a>timeline</a> or the
associated <a>timeline</a> is
<a lt="inactive timeline">inactive</a>, or
* <var>animation</var>'s [=playback rate=] is 0,
:: Set <var>animation</var>'s <a>hold time</a> to <var>seek time</var>.
: Otherwise,
:: Set <var>animation</var>'s [=start time=] to the result of evaluating
<code>|timeline time| - (|seek time| / [=playback rate=])</code>
where <var>timeline time</var> is the current <a>time value</a>
of <a>timeline</a> associated with <var>animation</var>.
</div>
1. If <var>animation</var> has no associated <a>timeline</a> or the associated
<a>timeline</a> is <a lt="inactive timeline">inactive</a>,
make <var>animation</var>'s [=start time=] <a>unresolved</a>.
<p class=note>
This preserves the invariant that when we don't have an active timeline it
is only possible to set <em>either</em> the [=start time=]
<em>or</em> the animation's <a>current time</a>.
</p>
4. Make <var>animation</var>'s <a>previous current time</a> <a>unresolved</a>.
The procedure to <dfn>set the current time</dfn> of an animation,
<var>animation</var>, to <var>seek time</var> is as follows:
1. Run the steps to <a>silently set the current time</a> of
<var>animation</var> to <var>seek time</var>.
1. If <var>animation</var> has a <a>pending pause task</a>, synchronously
complete the pause operation by performing the following steps:
1. Set <var>animation</var>'s <a>hold time</a> to <var>seek time</var>.
1. [=Apply any pending playback rate=] to |animation|.
1. Make <var>animation</var>'s [=start time=] <a>unresolved</a>.
1. Cancel the <a>pending pause task</a>.
1. <a lt="resolve a Promise">Resolve</a> <var>animation</var>'s
<a>current ready promise</a> with <var>animation</var>.
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to true, and
the <var>synchronously notify</var> flag set to false.
<h4 id='setting-the-start-time-of-an-animation'>Setting the start time of an animation</h4>
The procedure to <dfn>set the start time</dfn>
of <a>animation</a>, <var>animation</var>, to <var>new start time</var>,
is as follows:
1. Let <var>timeline time</var> be the current <a>time value</a> of the
<a>timeline</a> that <var>animation</var> is associated with.
If there is no <a>timeline</a> associated with <var>animation</var> or the
associated timeline is <a lt="inactive timeline">inactive</a>,
let the <var>timeline time</var> be <a>unresolved</a>.
1. If <var>timeline time</var> is <a>unresolved</a> and <var>new start
time</var> is <a lt="unresolved">resolved</a>, make <var>animation</var>'s
<a>hold time</a> <a>unresolved</a>.
<p class=note>
This preserves the invariant that when we don't have an active timeline it
is only possible to set <em>either</em> the [=start time=]
<em>or</em> the animation's <a>current time</a>.
</p>
1. Let <var>previous current time</var> be <var>animation</var>'s <a>current
time</a>.
Note: This is the <a>current time</a> after applying the changes from the
previous step which may cause the <a>current time</a> to become
<a>unresolved</a>.
1. [=Apply any pending playback rate=] on |animation|.
1. Set <var>animation</var>'s [=start time=] to <var>new start time</var>.
1. Update <var>animation</var>'s <a>hold time</a> based on the first matching
condition from the following,
<div class="switch">
: If <var>new start time</var> is <a lt="unresolved">resolved</a>,
:: If <var>animation</var>'s [=playback rate=] is not zero,
make <var>animation</var>'s <a>hold time</a> <a>unresolved</a>.
: Otherwise (<var>new start time</var> is <a>unresolved</a>),
:: Set <var>animation</var>'s <a>hold time</a> to <var>previous current
time</var> even if <var>previous current time</var> is
<a>unresolved</a>.
</div>
1. If <var>animation</var> has a <a>pending play task</a> or
a <a>pending pause task</a>, cancel that task and
<a lt="resolve a Promise">resolve</a> <var>animation</var>'s
<a>current ready promise</a> with <var>animation</var>.
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to true, and
the <var>synchronously notify</var> flag set to false.
<h4 id='waiting-for-the-target-effect'>Waiting for the target effect</h4>
<div class='informative-bg'>
<em>This section is non-normative</em>
Some operations performed by an <a>animation</a> may not occur
instantaneously.
For example, some user agents may delegate the playback of an
animation to a separate process or to specialized graphics hardware
each of which may incur some setup overhead.
If such an animation is timed from the moment when the animation was
triggered there may be a significant jump between the first and second
frames of the animation corresponding to the setup time involved.
To avoid this problem, Web Animations typically begins timing
animations from the moment when the first frame of the animation is
complete.
This is represented by an <a>unresolved</a>
[=start time=] on the <a>animation</a> which becomes
resolved when the animation is <a>ready</a>.
Content may opt out of this behavior by setting the
[=start time=] to a <a lt="unresolved">resolved</a> <a>time value</a>.
</div>
An animation is <dfn>ready</dfn> at the first moment where <em>both</em> of the
following conditions are true:
* the user agent has completed any setup required to begin the playback of
the animation's <a>target effect</a>
including rendering the first frame of any <a>keyframe
effect</a>.
* the animation is associated with a <a>timeline</a> that is not
<a lt="inactive timeline">inactive</a>.
<h4 id='promise-objects'>Promise objects</h4>
<a>Promise objects</a> are defined by [[!ECMA-262]].
To <dfn>resolve a Promise</dfn> with <var>value</var>,
call the \[[Call]] internal \[[Resolve]] method on the <a>PromiseCapability
record</a> for the promise, passing <code>undefined</code> as
<var>thisArgument</var> and (<var>value</var>) as <var>argumentsList</var>.
To <dfn>reject a Promise</dfn> with <var>reason</var>,
call the \[[Call]] internal \[[Reject]] method on the <a>PromiseCapability
record</a> for the promise, passing <code>undefined</code> as
<var>thisArgument</var> and
(<var>reason</var>) as <var>argumentsList</var>.
To <dfn>create a new resolved Promise</dfn> with <var>value</var>, call
<a>Promise.resolve</a>, passing <var>value</var> as <var ignore>x</var>.
<h4 id='the-current-ready-promise'>The current ready promise</h4>
Each <a>animation</a> has a <dfn>current ready promise</dfn>.
The <a>current ready promise</a> is initially a resolved <a>Promise</a> created
using the procedure to <a>create a new resolved Promise</a>.
The object is replaced with a new <a>Promise object</a> every time the animation
queues a <a>pending play task</a> or a <a>pending pause task</a> when it
previously did not have a pending task, or when the animation is canceled (see
[[#canceling-an-animation-section]]).
<div class="note">
Note that since the same object is used for both pending play and
pending pause requests, authors are advised to check the state of the
animation when the <a>Promise object</a> is resolved.
For example, in the following code fragment, the state of the animation
will be <a lt="running play state">running</a> when the
<a>current ready promise</a> is resolved.
This is because the <code>play</code> operation occurs while a <a>pending
play task</a> is still queued and hence the <a>current ready promise</a>
is re-used.
<div class='example'><pre class='lang-javascript'>
animation.pause();
animation.ready.then(function() {
// Displays 'running'
alert(animation.playState);
});
animation.play();</pre></div>
</div>
<h4 id='playing-an-animation-section'>Playing an animation</h4>
The procedure to <dfn>play an animation</dfn>, <var>animation</var>, given
a flag <var>auto-rewind</var>, is as follows:
Note: The <var>auto-rewind</var> flag is provided for other specifications
that build on this model but do not require the rewinding behavior, such
as CSS Animations [[CSS-ANIMATIONS-1]].
1. Let <var>aborted pause</var> be a boolean flag that is true if
<var>animation</var> has a <a>pending pause task</a>, and false otherwise.
1. Let <var>has pending ready promise</var> be a boolean flag that is
initially false.
1. Perform the steps corresponding to the <em>first</em> matching
condition from the following, if any:
<div class="switch">
: If |animation|'s [=effective playback rate=] &gt; 0,
the <var>auto-rewind</var> flag is true and <em>either</em>
<var>animation</var>'s:
* <a>current time</a> is <a>unresolved</a>, or
* <a>current time</a> &lt; zero, or
* <a>current time</a> &ge; <a>target effect end</a>,
:: Set <var>animation</var>'s <a>hold time</a> to zero.
: If |animation|'s [=effective playback rate=] &lt; 0,
the <var>auto-rewind</var> flag is true and <em>either</em>
<var>animation</var>'s:
* <a>current time</a> is <a>unresolved</a>, or
* <a>current time</a> &le; zero, or
* <a>current time</a> &gt; <a>target effect end</a>,
:: If <a>target effect end</a> is positive infinity,
<a>throw</a> an <span class=exceptionname>InvalidStateError</span> and
abort these steps.
Otherwise, set <var>animation</var>'s <a>hold time</a> to <a>target
effect end</a>.
: If |animation|'s [=effective playback rate=] = 0 and |animation|'s
[=current time=] is [=unresolved=],
:: Set <var>animation</var>'s <a>hold time</a> to zero.
</div>
1. If <var>animation</var> has a <a>pending play task</a> or a
<a>pending pause task</a>,
1. Cancel that task.
1. Set <var>has pending ready promise</var> to true.
1. If the following three conditions are <em>all</em> satisfied:
* |animation|'s [=hold time=] is [=unresolved=], and
* |aborted pause| is false, and
* |animation| does <em>not</em> have a [=pending playback rate=],
abort this procedure.
1. If <var>animation</var>'s <a>hold time</a> is <a lt=unresolved>resolved</a>,
let its [=start time=] be <a>unresolved</a>.
1. If <var>has pending ready promise</var> is false,
let <var>animation</var>'s <a>current ready promise</a> be a new
(pending) <a>Promise</a> object.
1. Schedule a task to run as soon as <var>animation</var> is <a>ready</a>.
The task shall perform the following steps:
1. Assert that at least one of |animation|'s [=start time=] or [=hold
time=] is <a lt=unresolved>resolved</a>.
1. Let <var>ready time</var> be the <a>time value</a> of
the <a>timeline</a> associated with <var>animation</var> at the moment
when <var>animation</var> became <a>ready</a>.
1. Perform the steps corresponding to the first matching condition below,
if any:
<div class="switch">
: If |animation|'s [=hold time=] is <a lt=unresolved>resolved</a>,
:: 1. [=Apply any pending playback rate=] on |animation|.
1. Let |new start time| be the result of evaluating
<code>|ready time| - [=hold time=] / [=playback rate=]</code>
for |animation|.
If the [=playback rate=] is zero, let
|new start time| be simply |ready time|.
1. Set the [=start time=] of |animation| to |new start time|.
1. If |animation|'s [=playback rate=] is not 0, make |animation|'s
[=hold time=] [=unresolved=].
: If |animation|'s [=start time=] is resolved and |animation| has
a [=pending playback rate=],
:: 1. Let |current time to match| be the result of evaluating
<code>(|ready time| - [=start time=]) &times;
[=playback rate=]</code> for |animation|.
1. [=Apply any pending playback rate=] on |animation|.
1. If |animation|'s [=playback rate=] is zero, let |animation|'s
[=hold time=] be |current time to match|.
1. Let |new start time| be the result of evaluating
<code>|ready time| - |current time to match| /
[=playback rate=]</code> for |animation|.
If the [=playback rate=] is zero, let |new start time| be simply
|ready time|.
1. Set the [=start time=] of |animation| to |new start time|.
</div>
1. <a lt="resolve a Promise">Resolve</a> <var>animation</var>'s <a>current
ready promise</a> with <var>animation</var>.
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to false,
and the <var>synchronously notify</var> flag set to false.
<div class="note">
Note that the order of the above two steps is important
since it means that an animation with zero-length <a>target
effect</a> will resolve its <a>current ready promise</a>
before its <a>current finished promise</a>.
</div>
So long as the above task is scheduled but has yet to run,
<var>animation</var> is described as having a
<dfn>pending play task</dfn>.
While the task is running, however, |animation| does <em>not</em> have
a [=pending play task=].
If a user agent determines that |animation| is immediately <a>ready</a>, it
may schedule the above task as a microtask such that it runs at the next
<a lt="perform a microtask checkpoint">microtask checkpoint</a>, but it must
<em>not</em> perform the task synchronously.
<div class="note">
The above requirement to run the <a>pending play task</a> asynchronously
ensures that code such as the following behaves consistently between
implementations:
<div class='example'><pre class='lang-javascript'>
animation.play();
animation.ready.then(
() => { console.log('Playback commenced'); },
() => { console.log('Playback was canceled'); }
);
// Suppose some condition requires playback to be canceled...
animation.cancel();
// "Playback was canceled" will be printed to the console.
</pre></div>
In the above code, were the [=pending play task=] run synchronously, the
[=current ready promise=] would not be rejected.
</div>
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to false, and
the <var>synchronously notify</var> flag set to false.
<h4 id='pausing-an-animation-section'>Pausing an animation</h4>
Whenever an <a>animation</a> has an <a>unresolved</a> [=start time=],
its <a>current time</a> will be suspended.
As with <a lt="play an animation">playing an animation</a>, pausing may not
happen instantaneously (see [[#waiting-for-the-target-effect]]).
For example, if animation is performed by a separate process, it may
be necessary to synchronize the <a>current time</a> to ensure that it
reflects the state drawn by the animation process.
The procedure to <dfn>pause an animation</dfn>, <var>animation</var>, is as
follows:
1. If <var>animation</var> has a <a>pending pause task</a>, abort these steps.
1. If the <a>play state</a> of <var>animation</var> is <a
lt="paused play state">paused</a>, abort these steps.
1. If the <var>animation</var>'s <a>current time</a> is <a>unresolved</a>,
perform the steps according to the first matching condition from below:
<div class="switch">
: If <var>animation</var>'s [=playback rate=] is &ge; 0,
:: Let <var>animation</var>'s <a>hold time</a> be zero.
: Otherwise,
:: If <a>target effect end</a> for <var>animation</var> is positive
infinity,
<a>throw</a> an <span class=exceptionname>InvalidStateError</span>
and abort these steps.
Otherwise, let <var>animation</var>'s <a>hold time</a> be
<a>target effect end</a>.
</div>
1. Let <var>has pending ready promise</var> be a boolean flag that is
initially false.
1. If <var>animation</var> has a <a>pending play task</a>, cancel that task
and let <var>has pending ready promise</var> be true.
1. If <var>has pending ready promise</var> is false,
set <var>animation</var>'s <a>current ready promise</a> to a new
(pending) <a>Promise</a> object.
1. Schedule a task to be executed at the first possible moment after
the user agent has performed any processing necessary to suspend
the playback of
<var>animation</var>'s <a>target effect</a>, if any.
The task shall perform the following steps:
1. Let <var>ready time</var> be the time value of the timeline associated
with <var>animation</var> at the moment when the user agent completed
processing necessary to suspend playback of <var>animation</var>'s
<a>target effect</a>.
1. If <var>animation</var>'s [=start time=]
is <a lt=unresolved>resolved</a> and its <a>hold time</a> is
<em>not</em> resolved,
let <var>animation</var>'s <a>hold time</a> be the result of evaluating
<code>(<var>ready time</var> - [=start time=]) &times;
[=playback rate=]</code>.
Note: The <a>hold time</a> might be already set if the animation
is <a lt="finished play state">finished</a>, or if the animation
has a <a>pending play task</a>.
In either case we want to preserve the <a>hold time</a> as we
enter the <a lt="paused play state">paused</a> state.
1. [=Apply any pending playback rate=] on |animation|.
1. Make <var>animation</var>'s [=start time=] unresolved.
1. <a lt="resolve a Promise">Resolve</a> <var>animation</var>'s <a>current
ready promise</a> with <var>animation</var>.
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to false,
and the <var>synchronously notify</var> flag set to false.
So long as the above task is scheduled but has yet to run,
<var>animation</var> is described as having a <dfn>pending pause task</dfn>.
While the task is running, however, <var>animation</var> does
<em>not</em> have a <a>pending pause task</a>.
As with the [=pending play task=], the user agent must run the [=pending
pause task=] asynchronously, although that may be as soon as the next <a
lt="perform a microtask checkpoint">microtask checkpoint</a>.
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to false,
and the <var>synchronously notify</var> flag set to false.
<h4 id="reaching-the-end">Reaching the end</h4>
<div class='informative-bg'>
<em>This section is non-normative</em>
DVD players or cassette players typically continue playing until they reach
the end of their media at which point they stop.
If such players are able to play in reverse, they typically stop
playing when they reach the beginning of their media.
In order to emulate this behavior and to provide consistency
with HTML's <a>media elements</a> [[HTML]], the <a>current time</a> of Web
Animations' animations do not play forwards beyond the <a>end time</a> of their
<a>target effect</a> or play backwards past time zero.
An animation that has reached the natural boundary of its playback range
is said to have <em>finished</em>.
Graphically, the effect of limiting the current time is shown below.
<figure>
<img src="img/limiting.svg" width="500"
alt="The effect of limiting the current time of an animation.">
<figcaption>
The effect of limiting the <a>current time</a> of an <a>animation</a>
with a start time of 1s, a target effect of length 3s, and
a positive [=playback rate=].
After the <a>current time</a> of the animation reaches the end of the
target effect, it is capped at 3s.
</figcaption>
</figure>
It is possible, however, to <em>seek</em> the <a>current time</a> of
an <a>animation</a> to a time past the end of the <a>target effect</a>.
When doing so, the <a>current time</a> will not progress but the
animation will act as if it had been paused at the seeked time.
This allows, for example, seeking the <a>current time</a> of
an animation with <em>no</em> <a>target effect</a> to 5s.
If <a>target effect</a> with an <a>end time</a> later than 5s is
later associated with the animation, playback will begin from the 5s
mark.
Similar behavior to the above scenario may arise when the
length of an animation's <a>target effect</a> changes.
Similarly, when the [=playback rate=] is negative, the
<a>current time</a> does not progress past time zero.
</div>
<h4 id="the-current-finished-promise">The current finished promise</h4>
Each animation has a <dfn>current finished promise</dfn>.
The <a>current finished promise</a> is initially a pending <a>Promise</a>
object.
The object is replaced with a new (pending) <a>Promise</a> object every time
the animation leaves the <a>finished play state</a>.
<h4 id="updating-the-finished-state">Updating the finished state</h4>
For an animation with a positive [=playback rate=], the <a>current time</a>
continues to increase until it reaches the <a>target effect end</a>.
The <dfn>target effect end</dfn> of an animation is equal to the <a>end
time</a> of the animation's <a>target effect</a>.
If the animation has no <a>target effect</a>, the <a>target effect
end</a> is zero.
For an animation with a negative [=playback rate=], the <a>current time</a>
continues to decrease until it reaches zero.
A running animation that has reached this boundary (or overshot it) and has a
<a lt="unresolved">resolved</a> [=start time=]
is said to be <a lt="finished play state">finished</a>.
The crossing of this boundary is checked on each modification to the
animation object using the procedure to <a>update an animation's finished
state</a> defined below. This procedure is also run as part of the [=update
animations and send events=] procedure. In both cases the <var>did seek</var>
flag, defined below, is set to false.
For each animation, the user agent maintains a <dfn>previous current
time</dfn> <a>time value</a> that is originally <a>unresolved</a>.
Whilst during normal playback the <a>current time</a> of an <a>animation</a> is
limited to the boundaries described above, it is possible to seek the <a>current
time</a> of an <a>animation</a> to times outside those boundaries using the
procedure to <a>set the current time</a> of an <a>animation</a>.
The procedure to <dfn>update an animation's finished state</dfn> for
<var>animation</var>, given a flag <var>did seek</var> (to indicate if
the update is being performed after <a lt="set the current time">setting the
current time</a>), and a flag <var>synchronously notify</var> (to indicate
the update was called in a context where we expect finished event queueing
and finished promise resolution to happen immediately, if at all) is as
follows:
1. Let the <var>unconstrained current time</var> be the result of calculating
the <a>current time</a> substituting an <a>unresolved</a> time value for the
<a>hold time</a> if <var>did seek</var> is false.
If <var>did seek</var> is true, the <var>unconstrained current time</var>
is equal to the <a>current time</a>.
Note: This is required to accommodate timelines that may change direction.
Without this definition, a once-finished animation would remain finished
even when its timeline progresses in the opposite direction.
1. If <em>all three</em> of the following conditions are true,
* the <var>unconstrained current time</var> is <a
lt=unresolved>resolved</a>, <em>and</em>
* <var>animation</var>'s [=start time=] is
<a lt="unresolved">resolved</a>, <em>and</em>
* <var>animation</var> does <em>not</em> have a <a>pending play task</a>
or a <a>pending pause task</a>,
then update <var>animation</var>'s <a>hold time</a> based on the first
matching condition for <var>animation</var> from below, if any:
<div class='switch'>
: If [=playback rate=] &gt; 0 and
<var>unconstrained current time</var> is greater than or equal to
<a>target effect end</a>,
:: If <var>did seek</var> is true, let the <a>hold time</a>
be the value of <var>unconstrained current time</var>.
If <var>did seek</var> is false, let the <a>hold time</a> be the maximum
value of <a>previous current time</a> and <a>target effect end</a>.
If the <a>previous current time</a> is
<a>unresolved</a>, let the <a>hold time</a> be <a>target
effect end</a>.
: If [=playback rate=] &lt; 0 and
<var>unconstrained current time</var> is less than or equal to 0,
:: If <var>did seek</var> is true, let the <a>hold time</a>
be the value of <var>unconstrained current time</var>.
If <var>did seek</var> is false, let the <a>hold time</a> be the
minimum value of <a>previous current time</a> and zero.
If the <a>previous current time</a> is
<a>unresolved</a>, let the <a>hold time</a> be zero.
: If [=playback rate=] &ne; 0, and
<var>animation</var> is associated with an
<a lt="inactive timeline">active timeline</a>,
:: Perform the following steps:
1. If <var>did seek</var> is true and the <a>hold time</a> is <a
lt=unresolved>resolved</a>, let <var>animation</var>'s
[=start time=] be equal to the result of evaluating
<code>|timeline time| - ([=hold time=] / [=playback rate=])</code>
where <var>timeline time</var> is the current <a>time value</a>
of <a>timeline</a> associated with <var>animation</var>.
1. Let the <a>hold time</a> be <a>unresolved</a>.
</div>
1. Set the <a>previous current time</a> of <var>animation</var> be the
result of calculating its <a>current time</a>.
1. Let <var>current finished state</var> be true if the <a>play
state</a> of <var>animation</var> is
<a lt="finished play state">finished</a>.
Otherwise, let it be false.
1. If <var>current finished state</var> is true and the <a>current finished
promise</a> is not yet resolved, perform the following steps:
1. Let <dfn>finish notification steps</dfn> refer to the following
procedure:
1. If <var>animation</var>'s <a>play state</a> is not equal to <a
lt="finished play state">finished</a>, abort these steps.
1. <a lt="resolve a promise">Resolve</a> <var>animation</var>'s
<a>current finished promise</a> object with
<var>animation</var>.
1. <a lt="create an event">Create</a> an {{AnimationPlaybackEvent}},
|finishEvent|.
1. Set |finishEvent|'s {{Event/type}} attribute to <a lt="finish
event">finish</a>.
1. Set |finishEvent|'s {{AnimationPlaybackEvent/currentTime}} attribute
to the [=current time=] of |animation|.
1. Set |finishEvent|'s {{AnimationPlaybackEvent/timelineTime}}
attribute to the <a lt="timeline current time">current time</a>
of the [=timeline=] with which |animation| is associated.
If |animation| is not associated with a timeline, or the timeline
is <a lt="inactive timeline">inactive</a>, let
{{AnimationPlaybackEvent/timelineTime}} be
<code class=esvalue>null</code>.
1. If |animation| has a [=document for timing=], then append
|finishEvent| to its [=document for timing=]'s [=pending animation
event queue=] along with its target, |animation|.
For the [=scheduled event time=], use the result of <a lt="animation
time to origin-relative time">converting</a>
|animation|'s [=target effect end=] to an origin-relative time.
Otherwise, [=queue a task=] to [=dispatch=] |finishEvent| at
|animation|.
The task source for this task is the [=DOM manipulation task
source=].
1. If <var>synchronously notify</var> is true, cancel any queued
microtask to run the <a>finish notification steps</a> for this
<var>animation</var>, and run the <a>finish notification
steps</a> immediately.
Otherwise, if <var>synchronously notify</var> is false, <a>queue
a microtask</a> to run <a>finish notification steps</a> for
<var>animation</var> unless there is already a microtask queued to run
those steps for <var>animation</var>.
1. If <var>current finished state</var> is false and
<var>animation</var>'s <a>current finished promise</a> is already
resolved, set <var>animation</var>'s <a>current finished promise</a> to
a new (pending) Promise object.
<div class="informative-bg">
Typically, notification about the finished state of an animation is
performed asynchronously. This allows for the animation to temporarily
enter the <a lt="finished play state">finished state</a> without
triggering events to be fired or promises to be resolved.
For example, in the following code fragment, <code>animation</code> temporarily
enters the finished state. If notification of the finished state occurred
synchronously this code would cause the <a>finish event</a> to be queued
and the <a>current finished promise</a> to be resolved. However, if we
reverse the order of the two statements such that the
<code>iterationCount</code> is updated first, this would not happen.
To avoid this surprising behavior, notification about the finished state of
an animation is typically performed asynchronously.
<div class='example'><pre class='lang-javascript'>
var animation = elem.animate({ left: '100px' }, 2000);
animation.playbackRate = 2;
animation.currentTime = 1000; // animation is now finished
animation.effect.updateTiming({ iterationCount: 2 }); // animation is no longer finished
</pre></div>
The one exception to this asynchronous behavior is when the <a>finish an
animation</a> procedure is performed (typically by calling the
{{Animation/finish()}} method). In this case the author's intention to finish
the animation is clear so the notification about the finished state of the
animation occurs synchronously as demonstrated below.
<div class='example'><pre class='lang-javascript'>
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;
</pre></div>
Note that like the procedure to <a>finish an animation</a>,
the procedure to <a>cancel an animation</a> similarly queues the
<a>cancel event</a> and rejects the <a>current finished promise</a> and
<a>current ready promise</a> in a <em>synchronous</em> manner.
</div>
<h4 id="finishing-an-animation-section">Finishing an animation</h4>
An animation can be advanced to the natural end of its current playback
direction by using the procedure to <dfn>finish an animation</dfn>
for <var>animation</var> defined below:
1. If |animation|'s [=effective playback rate=] is zero,
or if |animation|'s [=effective playback rate=] &gt; 0
and <a>target effect end</a> is infinity,
<a>throw</a> an <span class=exceptionname>InvalidStateError</span> and
abort these steps.
1. [=Apply any pending playback rate=] to |animation|.
1. Set <var>limit</var> as follows:
<div class="switch">
: If [=playback rate=] &gt; 0,</dt>
:: Let <var>limit</var> be <a>target effect end</a>.</dd>
: Otherwise,
:: Let <var>limit</var> be zero.</dd>
</div>
1. <a>Silently set the current time</a> to <var>limit</var>.
1. If <var>animation</var>'s [=start time=] is
<a>unresolved</a> and <var>animation</var> has an associated <a
lt="inactive timeline">active</a> <a>timeline</a>, let the
[=start time=] be the result of evaluating
<code><var>timeline time</var> -
(<var>limit</var> / [=playback rate=])</code>
where <var>timeline time</var> is the current <a>time
value</a> of the associated <a>timeline</a>.
1. If there is a <a>pending pause task</a> and
[=start time=] is <a lt="unresolved">resolved</a>,
1. Let the <a>hold time</a> be <a>unresolved</a>.
<div class="note">Typically the <a>hold time</a> will already be
unresolved except in the case when the animation was previously <a
lt="idle play state">idle</a>.</div>
1. Cancel the <a>pending pause task</a>.
1. <a lt="resolve a Promise">Resolve</a> the <a>current ready promise</a>
of <var>animation</var> with <var>animation</var>.
1. If there is a <a>pending play task</a> and [=start time=] is
<a lt="unresolved">resolved</a>, cancel that task and <a lt="resolve
a Promise">resolve</a> the <a>current ready promise</a> of
<var>animation</var> with <var>animation</var>.
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to true,
and the <var>synchronously notify</var> flag set to true.
<h4 id='canceling-an-animation-section'>Canceling an animation</h4>
An animation can be canceled which causes the <a>current time</a> to
become <a>unresolved</a> hence removing any effects caused by the
<a>target effect</a>.
The procedure to <dfn>cancel an animation</dfn> for <var>animation</var> is
as follows:
1. If <var>animation</var>'s <a>play state</a> is <em>not</em>
<a lt="idle play state">idle</a>, perform the following steps:
1. Run the procedure to <a>reset an animation's pending tasks</a> on
<var>animation</var>.
1. <a lt="reject a Promise">Reject</a> the <a>current finished promise</a>
with a DOMException named "AbortError".
1. Let <a>current finished promise</a> be a new (pending) <a>Promise</a>
object.
1. <a lt="create an event">Create</a> an {{AnimationPlaybackEvent}},
|cancelEvent|.
1. Set |cancelEvent|'s {{Event/type}} attribute to <a lt="cancel
event">cancel</a>.
1. Set |cancelEvent|'s {{AnimationPlaybackEvent/currentTime}} to
<code class=esvalue>null</code>.
1. Let |timeline time| be the <a lt="timeline current time">current
time</a> of the [=timeline=] with which |animation| is associated.
If |animation| is not associated with an <a lt="inactive
timeline">active timeline</a>, let |timeline time| be n
[=unresolved=] [=time value=].
1. Set |cancelEvent|'s {{AnimationPlaybackEvent/timelineTime}} to
|timeline time|. If |timeline time| is [=unresolved=], set it to
<code class=esvalue>null</code>.
1. If |animation| has a [=document for timing=], then append
|cancelEvent| to its [=document for timing=]'s [=pending animation
event queue=] along with its target, |animation|.
If |animation| is associated with an <a lt="inactive timeline">active
timeline</a> that defines a procedure to <a lt="timeline time to
origin-relative time">convert timeline times to origin-relative
time</a>, let the [=scheduled event time=] be the result of applying
that procedure to |timeline time|.
Otherwise, the [=scheduled event time=] is an [=unresolved=] [=time
value=].
Otherwise, [=queue a task=] to [=dispatch=] |cancelEvent| at
|animation|.
The task source for this task is the [=DOM manipulation task
source=].
1. Make <var>animation</var>'s <a>hold time</a> <a>unresolved</a>.
1. Make <var>animation</var>'s [=start time=] <a>unresolved</a>.
<h4 id="speed-control">Speed control</h4>
<div class="informative-bg">
The rate of play of an animation can be controlled by setting its
<em>playback rate</em>.
For example, setting a playback rate of 2 will cause the animation's
<a>current time</a> to increase at twice the rate of its <a>timeline</a>.
Similarly, a playback rate of -1 will cause the animation's <a>current
time</a> to decrease at the same rate as the <a>time values</a> from
its <a>timeline</a> increase.
</div>
<a>Animations</a> have a <dfn>playback rate</dfn>
that provides a scaling factor from the rate of change of the associated
<a>timeline</a>'s <a>time values</a> to the animation's <a>current time</a>.
The [=playback rate=] is initially 1.
Setting an animation's [=playback rate=]
to zero effectively pauses the animation (however, the <a>play state</a>
does not necessarily become <a lt="paused play state">paused</a>).
<h5 id="setting-the-playback-rate-of-an-animation">Setting the playback rate of an animation</h5>
The procedure to <dfn>set the playback rate</dfn> of
an <a>animation</a>, <var>animation</var> to <var>new playback rate</var>
is as follows:
1. Clear any [=pending playback rate=] on |animation|.
1. Let <var>previous time</var> be the value of the
<a>current time</a> of <var>animation</var> before changing the
[=playback rate=].
1. Set the [=playback rate=] to <var>new playback rate</var>.
1. If <var>previous time</var> is <a lt="unresolved">resolved</a>,
<a>set the current time</a> of <var>animation</var> to
<var>previous time</var>
<h5 id="seamlessly-updating-the-playback-rate-of-an-animation">Seamlessly updating the playback rate of an animation</h5>
For an in-flight animation that is running on another process or thread, the
procedure to <a>set the playback rate</a> may cause the animation to jump if the
process or thread running the animation is not currently synchronized with the
process or thread performing the update.
In order to produce seamless changes to the [=playback rate=] of an
[=animation=], animation's may have a <dfn>pending playback rate</dfn> that
defines a playback rate to be applied after any necessary synchronization has
taken place (for the case of animations running in a different thread or
process).
Initially the [=pending playback rate=] of an [=animation=] is unset.
The <dfn>effective playback rate</dfn> of an |animation| is
its [=pending playback rate=], if set,
otherwise it is the animation's [=playback rate=].
When an [=animation=], |animation|, is to <dfn>apply any pending playback
rate</dfn> the following steps are performed:
1. If |animation| does not have a [=pending playback rate=], abort these steps.
1. Set |animation|'s [=playback rate=] to its [=pending playback rate=].
1. Clear |animation|'s [=pending playback rate=].
The procedure to <dfn>seamlessly update the playback rate</dfn> an
[=animation=], |animation|, to |new playback rate| preserving its [=current
time=] is as follows:
1. Let |previous play state| be |animation|'s [=play state=].
Note: It is necessary to record the play state
before updating |animation|'s [=effective playback rate=] since,
in the following logic,
we want to immediately apply the [=pending playback rate=] of |animation|
if it is <em>currently</em> <a lt="finished play state">finished</a>
regardless of whether or not it will still be finished after we
apply the [=pending playback rate=].
1. Let |animation|'s [=pending playback rate=] be |new playback rate|.
1. Perform the steps corresponding to the first matching condition from below:
<div class="switch">
: If |animation| has a [=pending play task=] or a [=pending pause task=],
:: Abort these steps.
Note: The different types of pending tasks will apply the [=pending
playback rate=] when they run so there is no further action required in
this case.
: If |previous play state| is <a lt="idle play state">idle</a>
or <a lt="paused play state">paused</a>,
:: [=Apply any pending playback rate=] on |animation|.
: If |previous play state| is <a lt="finished play state">finished</a>,
:: 1. Let the |unconstrained current time| be the result of calculating
the [=current time=] of |animation| substituting an [=unresolved=]
time value for the [=hold time=].
1. Let |animation|'s [=start time=] be
the result of evaluating the following expression:
<blockquote>
<code>|timeline time| - (|unconstrained current time|
/ [=pending playback rate=])</code>
</blockquote>
Where |timeline time| is the current <a>time value</a> of
the <a>timeline</a> associated with |animation|.
If [=pending playback rate=] is zero, let |animation|'s
[=start time=] be |timeline time|.
1. [=Apply any pending playback rate=] on |animation|.
1. Run the procedure to [=update an animation's finished state=] for
|animation| with the <var>did seek</var> flag set to false,
and the <var>synchronously notify</var> flag set to false.
: Otherwise,
:: Run the procedure to [=play an animation=] for |animation| with the
|auto-rewind| flag set to false.
</div>
<h4 id="reversing-an-animation-section">Reversing an animation</h4>
The procedure to <dfn>reverse an animation</dfn> of <a>animation</a>
<var>animation</var> is as follows:
1. If there is no <a>timeline</a> associated with <var>animation</var>, or the
associated <a>timeline</a> is <a lt="inactive timeline">inactive</a>
<a>throw</a> an <span class=exceptionname>InvalidStateError</span> and
abort these steps.
1. Let |original pending playback rate| be |animation|'s [=pending playback
rate=].
1. Let |animation|'s [=pending playback rate=] be the additive inverse of
its [=effective playback rate=]
(i.e. <code>-[=effective playback rate=]</code>).
1. Run the steps to <a>play an animation</a> for <var>animation</var>
with the <var>auto-rewind</var> flag set to true.
If the steps to <a>play an animation</a> throw an exception, set
|animation|'s [=pending playback rate=] to |original pending playback rate|
and propagate the exception.
<h4 id="play-states">Play states</h4>
An <a>animation</a> may be described as being in one of the following
<dfn lt="play state">play states</dfn> for each of which, a
non-normative description is also provided:
<div class=informative-bg>
: <a lt="idle play state">idle</a>
:: The <a>current time</a> of the animation is <a>unresolved</a> and
there are no pending tasks.
In this state the animation has no effect.
: <a lt="running play state">running</a>
:: The animation has a resolved <a>current time</a> that changes on each
<a>animation frame</a> (provided the [=playback rate=] is not zero).
: <a lt="paused play state">paused</a>
:: The animation has been suspended and the <a>current time</a>
is no longer changing.
: <a lt="finished play state">finished</a>
:: The animation has reached the natural boundary of its playback range
and the <a>current time</a> is no longer updating.
</div>
The <a>play state</a> of <a>animation</a>, <var>animation</var>, at a given
moment is the state corresponding to the <em>first</em> matching
condition from the following:
<div class="switch">
: <em>All</em> of the following conditions are true:
* The <a>current time</a> of <var>animation</var> is <a>unresolved</a>,
<em>and</em>
* <var>animation</var> does <em>not</em> have <em>either</em>
a <a>pending play task</a> <em>or</em> a <a>pending pause task</a>,
:: &rarr; <dfn lt="idle play state">idle</dfn>
: <em>Either</em> of the following conditions are true:
* <var>animation</var> has a <a>pending pause task</a>, <em>or</em>
* <em>both</em> the [=start time=] of
<var>animation</var> is <a>unresolved</a> <em>and</em> it does
<em>not</em> have a <a>pending play task</a>,
:: &rarr; <dfn lt="paused play state">paused</dfn>
: For <var>animation</var>, <a>current time</a> is <a
lt=unresolved>resolved</a> and <em>either</em> of the following conditions
are true:
* |animation|'s [=effective playback rate=] &gt; 0 and
<a>current time</a> &ge; <a>target effect end</a>; <em>or</em>
* |animation|'s [=effective playback rate=] &lt; 0 and
<a>current time</a> &le; 0,
:: &rarr; <dfn lt="finished play state">finished</dfn>
: Otherwise,
:: &rarr; <dfn lt="running play state">running</dfn>
</div>
<div class="note">
Note that the <a>paused play state</a> effectively
&ldquo;wins&rdquo; over the <a>finished play state</a>.
However, an animation that is paused outside of its natural playback range can
be converted from a <a lt="paused play state">paused</a>
animation into a <a lt="finished play state">finished</a> animation
without restarting by setting the [=start time=] such as below:
<div class='example'><pre class='lang-javascript'>
animation.effect.updateTiming({ duration: 5000 });
animation.currentTime = 4000;
animation.pause();
animation.ready.then(function() {
animation.effect.updateTiming({ duration: 3000 });
alert(animation.playState); // Displays 'paused'
animation.startTime =
document.timeline.currentTime - animation.currentTime * animation.playbackRate;
alert(animation.playState); // Displays 'finished'
});</pre></div>
</div>
<h4 id="animation-events-section">Animation events</h4>
<dfn>Animation events</dfn> include the [=animation playback events=] defined in
this specification as well as the <a>events from CSS transitions</a>
[[CSS-TRANSITIONS-1]] and <a>events from CSS animations</a>
[[CSS-ANIMATIONS-1]].
Future specifications may extend this set with further types of [=animation
events=].
Each {{Document}} maintains a <dfn>pending animation event queue</dfn> that
stores [=animation events=] along with their corresponding event targets and
<dfn>scheduled event time</dfn>.
The [=scheduled event time=] is a [=time value=] relative to the [=time origin=]
representing when the event would ideally have been dispatched were animations
updated at an infinitely high frequency.
It is used by the procedure to [=update animations and send events=] to sort
queued [=animation events=] chronologically.
Note that this value may be [=unresolved=] if, for example, the [=animation=]'s
[=timeline=] produces values that are unrelated to the [=time origin=] (e.g.
a timeline that tracks scroll-position) or if the [=timeline=] is <a
lt="inactive timeline">inactive</a>.
<h5 id="sorting-animation-events">Sorting animation events</h5>
The following definitions are provided to assist with sorting queued events.
To <dfn lt="animation time to timeline time">convert an animation time to
timeline time</a> a [=time value=], |time|, that is relative to the [=start
time=] of an animation, |animation|, perform the following steps:
1. If |time| is [=unresolved=], return |time|.
1. If |time| is infinity, return an [=unresolved=] [=time value=].
1. If |animation|'s [=playback rate=] is zero,
return an [=unresolved=] [=time value=].
1. If |animation|'s [=start time=] is [=unresolved=],
return an [=unresolved=] [=time value=].
1. Return the result of calculating:
<code>|time| &times; (1 / |playback rate|) + |start time|</code>
(where |playback rate| and |start time| are the [=playback rate=]
and [=start time=] of |animation|, respectively).
To <dfn lt="animation time to origin-relative time">convert a timeline time to
an origin-relative time</dfn> a [=time value=], |time|, that is expressed in the
same scale as the [=time values=] of a [=timeline=], |timeline|, perform the
following steps:
1. Let |timeline time| be the result of <a lt="animation time to timeline
time">converting</a> |time| from an animation time to a timeline time.
1. If |timeline time| is [=unresolved=],
return |time|.
1. If |animation| is not associated with a [=timeline=],
return an [=unresolved=] time value.
1. If |animation| is associated with an [=inactive timeline=],
return an [=unresolved=] time value.
1. If there is no procedure to <a lt="timeline time to origin-relative
time">convert a timeline time to an origin-relative time</a> for the
timeline associated with |animation|,
return an [=unresolved=] [=time value=].
1. Return the result of <a lt="timeline time to origin-relative
time">converting</a> |timeline time| to an origin-relative time using
the procedure defined for the [=timeline=] associated with |animation|.
<h5 id="animation-playback-events-section">Animation playback events</h5>
As <a>animations</a> play, they report changes to their status through
<dfn>animation playback events</dfn>.
<a>Animation playback events</a> are a property of the timing model. As a result
they are dispatched even when the <a>target effect</a> of the <a>animation</a>
is absent or has no observable result.
<h5 id="animation-playback-event-types">Types of animation playback events</h5>
: <dfn lt="finish event">finish</dfn>
:: Queued whenever an animation enters the <a>finished play state</a>.
: <dfn lt="cancel event">cancel</dfn>
:: Queued whenever an animation enters the <a>idle play state</a> from
another state. Creating a new <a>animation</a> that is initially
idle does <em>not</em> generate a new <a>cancel event</a>.
<h3 id="animation-effects">Animation effects</h3>
An <dfn>animation effect</dfn> is an abstract term referring to an item in
the timing hierarchy.
<h4 id="animation-effects-and-animations">Relationship between animation effects
and animations</h4>
The <a>target effect</a> of an <a>animation</a>, if set, is a type of
<a>animation effect</a>.
The <a>target effect</a> of an <a>animation</a> is said to be <dfn
lt="associated with an animation">associated</dfn> with that animation.
At a given moment, an <a>animation effect</a> can be <a
lt="associated with an animation">associated</a> with at most one
<a>animation</a>.
An <a>animation effect</a>, <var>effect</var>, is <dfn>associated with
a timeline</dfn>, <var>timeline</var>, if <var>effect</var> is
<a>associated with an animation</a> which, in turn, is associated with
<var>timeline</var>.
<h4 id="types-of-animation-effects">Types of animation effects</h4>
This specification defines a single type of <a>animation effect</a>:
<a>keyframe effects</a>.
Subsequent levels of this specification will define further types of
<a>animation effects</a>.
All types of <a>animation effects</a> define a number of common
properties which are described in the following sections.
<h4 id="the-active-interval">The active interval</h4>
<div class="informative-bg">
The period that an <a>animation effect</a> is scheduled to run is
called its <a>active interval</a>.
Each <a>animation effect</a> has only one such interval.
The lower bound of the <a>active interval</a> typically corresponds
to the [=start time=] of the <a>animation</a>
associated with this <a>animation effect</a> but may be shifted by a
<a>start delay</a> on the <a>animation effect</a>.
The upper bound of the interval is determined by the <a>active
duration</a>.
The relationship between the [=start time=], <a>start
delay</a>, and <a>active duration</a> is illustrated below.
<figure>
<img src="img/active-interval-examples.svg" width="600"
alt="Examples of the effect of the start delay on the endpoints
of the active interval">
<figcaption>
Examples of the effect of the <a>start delay</a> on the endpoints of
the <a>active interval</a>.<br>
(a) An animation effect with no delay; the [=start time=] and beginning of
the <a>active interval</a> are coincident.<br>
(b) An animation effect with a positive delay; the beginning of the
<a>active interval</a> is deferred by the delay.<br>
(c) An animation effect with a negative delay; the beginning of the
<a>active interval</a> is brought forward by the delay.
</figcaption>
</figure>
An <a>end delay</a> may also be specified but is primarily only of
use when sequencing animations.
</div>
<a>Animation effects</a> define an <dfn>active interval</dfn> which is
the period of time during which the effect is scheduled to produce its
effect with the exception of <a>fill modes</a> which apply outside the
<a>active interval</a>.
The lower bound of the <a>active interval</a> is defined by the
<a>start delay</a>.
The <dfn>start delay</dfn> of an <a>animation effect</a> is a signed offset
from the [=start time=] of the <a>animation</a>
with which the animation effect is associated.
The length of the <a>active interval</a> is called the <a>active
duration</a>, the calculation of which is defined in
[[#calculating-the-active-duration]].
Similar to the <a>start delay</a>, an <a>animation effect</a> also has
an <dfn>end delay</dfn> which is primarily of use when sequencing animations
based on the <a>end time</a> of another <a>animation effect</a>.
Although this is typically only useful in combination with sequence effects
which are introduced in a subsequent level of this specification, it is included
here for the purpose of representing the <a
href="https://www.w3.org/TR/SVG/animate.html#MinAttribute"><code>min</code></a>
attribute in SVG ([[SVG11]], Chapter 19).
The <dfn>end time</dfn> of an <a>animation effect</a> is
the result of evaluating <code>max(<a>start delay</a> + <a>active
duration</a> + <a>end delay</a>, 0)</code>.
<h4 id="local-time-section">Local time</h4>
The <dfn>local time</dfn> of an <a>animation effect</a> at a given
moment is based on the first matching condition from the following:
<div class='switch'>
: If the <a>animation effect</a> is <a>associated with an animation</a>,
:: the local time is the <a>current time</a> of the
<a>animation</a>.
: Otherwise,
:: the local time is <a>unresolved</a>.
</div>
<h4 id="animation-effect-phases-and-states">Animation effect phases and
states</h4>
<div class="informative-bg"><em>This section is non-normative</em>
At a given moment, an <a>animation effect</a> may be in one of three
possible <em>phases</em>.
If an <a>animation effect</a> has an <a>unresolved</a> <a>local
time</a> it will not be in any phase.
The different phases are illustrated below.
<figure>
<img src="img/animation-effect-phases-and-states.svg" width="700"
alt="An example of the different phases and states used to
describe an animation effect.">
<figcaption>
An example of the different phases and states used to describe
an <a>animation effect</a>.
</figcaption>
</figure>
The phases are as follows:
: <a>before phase</a>
:: The <a>animation effect</a>'s <a>local time</a> falls before the
effect's <a>active interval</a> and <a>end time</a>, <em>or</em>
occurs during the range when a negative <a>start delay</a> is in
effect.
: <a>active phase</a>
:: The <a>animation effect</a>'s <a>local time</a> falls inside the
effect's <a>active interval</a> and outside the range of any
negative <a>start delay</a> or negative <a>end delay</a>.
: <a>after phase</a>
:: The <a>animation effect</a>'s <a>local time</a> falls after the
effect's <a>active interval</a> or after the <a>end time</a> if that
comes first (due to a negative <a>end delay</a>), but <em>not</em>
during the range when a negative <a>start delay</a> is in effect.
In addition to these phases, an <a>animation effect</a> may also be
described as being in one of several overlapping <em>states</em>.
These states are only established for the duration of a single
[=animation frame=] and are primarily a convenience for describing stative parts
of the model.
These states and their useage within the model are summarized as
follows:
: <a>in play</a>
:: Corresponds to an <a>animation effect</a> whose <a>active time</a>
is changing on each frame.
: <a>current</a>
:: Corresponds to an <a>animation effect</a> that is either <a>in
play</a> or may become <a>in play</a> in the future.
This will be the case if the <a>animation effect</a> is <a>in
play</a> or in its <a>before phase</a>.
: <a>in effect</a>
:: Corresponds to an <a>animation effect</a> that has a resolved
<a>active time</a>.
This occurs when either the <a>animation effect</a> is in its
<a>active phase</a> or outside the <a>active phase</a> but at
a time where the effect's <a>fill mode</a> (see [[#fill-behavior]])
causes its <a>active time</a> to be resolved.
Only <a>in effect</a> <a>animation effects</a> apply
a result to their target.
The normative definition of each of these states follows.
</div>
Determining the phase of an <a>animation effect</a> requires the following
definitions:
: <dfn>animation direction</dfn>
:: &lsquo;backwards&rsquo; if the effect is <a>associated with an
animation</a> <em>and</em> the associated <a>animation</a>'s
[=playback rate=] is less than zero;
in all other cases, the <a>animation direction</a> is
&lsquo;forwards&rsquo;.
: <dfn>before-active boundary time</dfn>
:: <code>max(min(<a>start delay</a>, <a>end time</a>), 0)</code>
: <dfn>active-after boundary time</dfn>
:: <code>max(min(<a>start delay</a> + <a>active duration</a>,
<a>end time</a>), 0)</code>
An <a>animation effect</a> is in the <dfn>before phase</dfn> if the
animation effect's <a>local time</a> is not <a>unresolved</a> and
<em>either</em> of the following conditions are met:
1. the <a>local time</a> is less than the <a>before-active boundary time</a>,
<em>or</em>
1. the <a>animation direction</a> is &lsquo;backwards&rsquo; and the <a>local
time</a> is equal to the <a>before-active boundary time</a>.
An <a>animation effect</a> is in the <dfn>after phase</dfn> if the
animation effect's <a>local time</a> is not <a>unresolved</a> and
<em>either</em> of the following conditions are met:
1. the <a>local time</a> is greater than the <a>active-after boundary
time</a>, <em>or</em>
1. the <a>animation direction</a> is &lsquo;forwards&rsquo; and the <a>local
time</a> is equal to the <a>active-after boundary time</a>.
An <a>animation effect</a> is in the <dfn>active phase</dfn> if the
animation effect's <a>local time</a> is not <a>unresolved</a> and it is
not in either the <a>before phase</a> nor the <a>after phase</a>.
Furthermore, it is often convenient to refer to the case when an animation
effect is in none of the above phases as being in the <dfn>idle phase</dfn>.
An <a>animation effect</a> is <dfn>in play</dfn> if <em>all</em>
of the following conditions are met:
1. the <a>animation effect</a> is in the <a>active phase</a>, and
2. the <a>animation effect</a> is <a>associated with an animation</a> that is not
<a lt="finished play state">finished</a>.
An <a>animation effect</a> is <dfn>current</dfn> if <em>either</em>
of the following conditions is true:
* the <a>animation effect</a> is in the <a>before phase</a>, or
* the <a>animation effect</a> is <a>in play</a>.
An animation effect is <dfn>in effect</dfn> if its <a>active time</a>, as
calculated according to the procedure in [[#calculating-the-active-time]],
is <em>not</em> <a>unresolved</a>.
<h3 id="fill-behavior">Fill behavior</h3>
The effect of an <a>animation effect</a> when it is not <a>in play</a> is
determined by its <dfn>fill mode</dfn>.
The possible <a>fill modes</a> are:
* none,
* forwards,
* backwards, and
* both.
The normative definition of these modes is incorporated in the
calculation of the <a>active time</a> in [[#calculating-the-active-time]].
<h4 id="fill-modes">Fill modes</h4>
<div class='informative-bg'><em>This section is non-normative</em>
The effect of each <a>fill mode</a> is as follows:
: none
:: The animation effect has no effect when it is not <a>in play</a>.
: forwards
:: When the animation effect is in the <a>after phase</a>,
the animation effect will produce the same <a>iteration progress</a>
value as the last moment it is scheduled to be <a>in play</a>.
For all other times that the animation effect is not <a>in play</a>,
it will have no effect.
: backwards
:: When the animation effect is in the <a>before phase</a>,
the animation effect will produce the same <a>iteration progress</a>
value as the earliest moment that it is scheduled to be <a>in play</a>.
For all other times that the animation effect is not <a>in play</a>,
it will have no effect.
: both
:: When the animation effect
is in its <a>before phase</a>,
<span class="prop-value">backwards</span> fill behavior is used.
When the animation effect
is in its <a>after phase</a>,
<span class="prop-value">forwards</span> fill behavior is used.
Some examples of the these fill modes are illustrated below.
<figure>
<img src="img/animation-state-and-fill-behavior.svg" width="600"
alt="Examples of various fill modes and the states produced.">
<figcaption>
Examples of various fill modes and the states produced.<br>
(a) fill mode &lsquo;none&rsquo;. The animation effect has no effect
outside its active phase.<br>
(b) fill mode &lsquo;forwards&rsquo;. After the active phase has
finished, the <a>iteration progress</a> value continues to maintain
a fill value.<br>
(c) fill mode &lsquo;backwards&rsquo;. The animation effect produces
a fill value until the start of the active phase.<br>
(d) fill mode &lsquo;both&rsquo;. Both before and after the active
phase the animation effect produces a fill value.
</figcaption>
</figure>
Note: setting a fill mode has no bearing on the endpoints of the
<a>active interval</a> or the boundaries between <a
href="#animation-effect-phases-and-states">phases</a>.
However, the fill mode <em>does</em> have an effect on various other
properties of the timing model since the <a>active time</a> of an
animation effect is only defined (that is, not <a>unresolved</a>) inside
the <a>active phase</a> <em>or</em> when a fill is applied.
</div>
<h3 id="repeating">Repeating</h3>
<h4 id="iteration-intervals">Iteration intervals</h4>
It is possible to specify that an animation effect should repeat
a fixed number of times or indefinitely.
This repetition occurs <em>within</em> the <a>active interval</a>.
The span of time during which a single repetition takes place is
called an <dfn>iteration interval</dfn>.
Unlike the <a>active interval</a>, an animation effect can have multiple
<a>iteration intervals</a> although typically only the interval
corresponding to the <a>current iteration</a> is of interest.
The length of a single iteration is called the <dfn>iteration
duration</dfn>.
The initial <a>iteration duration</a> of an animation effect is zero.
<div class="informative-bg"><em>This section is non-normative</em>
Comparing the <a>iteration duration</a> and the <a>active
duration</a> we have:
: Iteration duration
:: The time taken for a single iteration of the animation effect to
complete.
: Active duration
:: The time taken for the entire animation effect to complete,
including repetitions.
This may be longer or shorter than the <a>iteration duration</a>.
The relationship between the <a>iteration duration</a> and <a>active
duration</a> is illustrated below.
<figure>
<img src="img/iteration-intervals.svg" width="600"
alt="Comparison of the iteration duration and active time.">
<figcaption>
A comparison of the <a>iteration duration</a> and <a>active
duration</a> of an animation effect with an <a>iteration count</a> of
2.5.
Note that the <a>iteration duration</a> for the final iteration does
not change, it is simply cut-off by the <a>active duration</a>.
</figcaption>
</figure>
</div>
<h4 id="controlling-iteration">Controlling iteration</h4>
The number of times an <a>animation effect</a> repeats is called its
<dfn>iteration count</dfn>.
The <a>iteration count</a> is a real number greater than or equal to
zero.
The <a>iteration count</a> may also be positive infinity to represent
that the <a>animation effect</a> repeats indefinitely.
In addition to the <a>iteration count</a>, <a>animation effects</a> also
have an <dfn>iteration start</dfn> property which specifies an offset
into the series of iterations at which the <a>animation effect</a>
should begin.
The <a>iteration start</a> is a finite real number greater than or
equal to zero.
The behavior of these parameters is defined in the calculations in
[[#core-animation-effect-calculations]].
<div class="informative-bg"><em>This section is non-normative</em>
The effect of the <a>iteration count</a> and <a>iteration start</a>
parameters is illustrated below.
<figure>
<img src="img/iteration-count-and-start.svg" width="600"
alt="The effect of the iteration count and iteration start parameters">
<figcaption>
The effect of the <a>iteration count</a> and <a>iteration start</a>
parameters.<br>
In the first case the <a>iteration count</a> is 2.5 resulting in the
third iteration being cut-off half way through its <a>iteration
interval</a>.<br>
The second case is the same but with an <a>iteration start</a> of
0.5.
This causes the <a>animation effect</a> to begin half way through the
first iteration.
</figcaption>
</figure>
Unlike the <a>iteration count</a> parameter, the <a>iteration
start</a> parameter does not effect the length of the <a>active
duration</a>.
Note that values of <a>iteration start</a> greater than or equal to
one are generally not useful unless used in combination with an
<a>animation effect</a> that has an <a>iteration composite
operation</a> of
<a lt="iteration composite operation accumulate">accumulate</a>.
</div>
<h4 id="iteration-time-space">Iteration time space</h4>
<div class="informative-bg"><em>This section is non-normative</em>
In Web Animations all times are relative to some point of reference.
These different points of reference produce different <em>time
spaces</em>.
This can be compared to coordinate spaces as used in computer
graphics.
The zero time of a time space is analogous to the origin of
a coordinate space.
We can describe animations that repeat as establishing a new time space
each time the animation repeats: the <em>iteration time space</em>.
<em>Iteration time space</em> is a time space whose zero time is the
beginning of an animation effect's current iteration.
Within the Web Animations model we also refer to <a>active
time</a> which is a time relative to the beginning of the active
interval.
This time space, however, is internal to the model and not exposed in
the programming interface or in markup.
These time spaces are illustrated below.
<figure>
<img src="img/time-spaces.svg" width="600"
alt="A comparison of local time, active time, and iteration time.">
<figcaption>
A comparison of local time, active time, and iteration time for an
animation with a iteration duration of 1s and an iteration count of
2.5.
</figcaption>
</figure>
Note: While the time spaces themselves are not bounded, Web
Animations defines <a>active time</a> and the <a>iteration
progress</a> such that they are clamped to a set range as shown in the
diagram.
For example, whilst a time of -1 second is a valid time in
<em>active time space</em>, the procedure for calculating the
<a>active time</a> defined in [[#calculating-the-active-time]] will
never return a negative value.
In addition to these time spaces we can also refer to the
<em>document time space</em> which is time space of the <a>time
values</a> of the <a>default document timeline</a> of the
{{Document}} of the <a>current global object</a>.
</div>
<h4 id="interval-timing">Interval timing</h4>
<div class="informative-bg"><em>This section is non-normative</em>
When an animation effect repeats we must define the behavior at the
iteration boundaries.
For this, and indeed for all interval timing, Web Animations uses an
endpoint-exclusive timing model.
This means that whilst the begin time of an interval
is included in the interval, the end time time is not.
In interval notation this can written <code>[begin, end)</code>.
This model provides sensible behavior when intervals are repeated and
sequenced since there is no overlap between the intervals.
In the examples below, for the repeated effect, at local time 1s,
the iteration time is 0.
For the sequenced animations, at timeline time 1s, only animation B's
<a>target effect</a> will be <a>in play</a>; there is no overlap.
<figure>
<img src="img/endpoint-exclusive-timing.svg" width="600"
alt="Illustration of end-point exclusive timing.">
<figcaption>
Illustration of end-point exclusive timing. For both repeated and
sequenced animation effects there is no overlap at the boundaries
between intervals.
</figcaption>
</figure>
An exception to this behavior is that when performing a <a
href="#fill-behavior">fill</a>, if the fill begins at an
interval endpoint, the endpoint is used.
This behavior falls out of the algorithm given in <a
href="#calculating-the-simple-iteration-progress"
section></a> and is illustrated below.
<figure>
<img src="img/endpoint-exclusive-timing-and-fill.svg" width="600"
alt="Effect of iterations and fill on iteration time.">
<figcaption>
After one iteration, the <a>iteration progress</a> is 0, but after two
iterations (and there onwards), the <a>iteration progress</a> is 1
due to the special behavior defined when an animation effect fills.
</figcaption>
</figure>
</div>
<h3 id="core-animation-effect-calculations">Core animation effect
calculations</h3>
<h4 id="animation-effect-calculations-overview">Overview</h4>
<div class="informative-bg"><em>This section is non-normative</em>
At the core of the Web Animations timing model is the process that
takes a <a>local time</a> value and converts it to an <a>iteration progress</a>.
The first step in this process is to calculate the bounds of the
<a>active interval</a> which is determined by the <a>active
duration</a>.
This process is illustrated below.
<figure>
<img src="img/active-duration-calculation.svg" width="650"
alt="Calculation of the active duration.">
<figcaption>
Calculation of the <a>active duration</a> is based on
multiplying the <a>iteration duration</a> by the
<a>iteration count</a>.
</figcaption>
</figure>
<p>
The process for calculating the <a>active duration</a> is normatively
defined in <a href="#calculating-the-active-duration"
section></a>.
</p>
<p>
Having established the <a>active duration</a>, the process for
transforming an <a>animation effect</a>'s <a>local time</a> into its
<a>transformed progress</a> (<a>iteration progress</a>) is illustrated below.
</p>
<figure>
<img src="img/time-calculations.svg" width="650"
alt="An overview of timing model calculations.">
<figcaption>
An overview of timing model calculations.<br>
(1) The <a>local time</a> is determined from the associated
<a>animation</a>.<br>
(2) The <a>local time</a> is converted into an <a>active time</a> by
incorporating the <a>start
delay</a>.<br>
(3) The <a>active time</a> is divided by the <a>iteration duration</a>
incorporating also the <a>iteration start</a>
property to produce the <a>overall progress</a>.<br>
(4) The <a>overall progress</a> time is then converted to an offset
within a single iteration: the <a>simple iteration progress</a>.<br>
(5) The <a>simple iteration progress</a> is converted into a <a>directed
progress</a> by incorporating the <a>playback direction</a>.<br>
(6) Finally, a timing function is applied to the <a>directed
progress</a> to produce the <a>transformed progress</a>.
</figcaption>
</figure>
The first step, calculating the <a>local time</a> is described in
[[#local-time-section]].
Steps 2 to 4 in the diagram are described in the following sections.
Steps 5 and 6 are described in [[#calculating-the-directed-progress]] and
[[#calculating-the-transformed-progress]] respectively.
</div>
<h4 id="calculating-the-active-duration">Calculating the active duration</h4>
The <a>active duration</a> is calculated as follows:
<blockquote>
<dfn>active duration</dfn> =
<code><a>iteration duration</a> &times;
<a>iteration count</a></code>
<p>
If either the <a>iteration duration</a> or <a>iteration count</a>
are zero, the <a>active duration</a>
is zero.
</p>
<p class="note">
This clarification is needed since the result of infinity
multiplied by zero is undefined according to IEEE 754-2008.
</p>
</blockquote>
<h4 id="transforming-the-local-time">Transforming the local time</h4>
<h5 id="calculating-the-active-time">Calculating the active time</h5>
The <dfn>active time</dfn> is based on the <a>local time</a>
and <a>start delay</a>.
However, it is only defined when the <a>animation effect</a> should
produce an output and hence depends on its <a>fill mode</a> and
phase as follows,
<div class="switch">
: If the animation effect is in the <a>before phase</a>,
:: The result depends on the first matching condition from the
following,
<div class="switch">
: If the <a>fill mode</a> is <span
class="prop-mode">backwards</span> or <span
class="prop-mode">both</span>,
:: Return the result of evaluating
<code>max(<a>local time</a> - <a>start delay</a>, 0)</code>.
: Otherwise,
:: Return an <a>unresolved</a> <a>time value</a>.
</div>
: If the animation effect is in the <a>active phase</a>,
:: Return the result of evaluating
<code><a>local time</a> - <a>start delay</a></code>.
: If the animation effect is in the <a>after phase</a>,
:: The result depends on the first matching condition from the
following,
<div class="switch">
: If the <a>fill mode</a> is <span
class="prop-mode">forwards</span> or <span
class="prop-mode">both</span>,
:: Return the result of evaluating
<code>max(min(<a>local time</a> - <a>start delay</a>,
<a>active duration</a>),
0)</code>.
: Otherwise,
:: Return an <a>unresolved</a> <a>time value</a>.
</div>
: Otherwise (the <a>local time</a> is <a>unresolved</a>),
:: Return an <a>unresolved</a> <a>time value</a>.
</div>
<h5 id="calculating-the-overall-progress">Calculating the overall progress</h5>
The <dfn>overall progress</dfn> describes the number of iterations that
have completed (including partial iterations) and is defined as follows:
1. If the <a>active time</a> is <a>unresolved</a>, return <a>unresolved</a>.
1. Calculate an initial value for <var>overall progress</var> based on the
first matching condition from below,
<div class="switch">
: If the <a>iteration duration</a> is zero,
:: If the animation effect is in the <a>before phase</a>, let
<var>overall progress</var> be zero, otherwise, let it be equal
to the <a>iteration count</a>.
: Otherwise,
:: Let <var>overall progress</var> be the result of calculating
<code><a>active time</a> / <a>iteration duration</a></code>.
</div>
1. Return the result of calculating <code><var>overall
progress</var> + <a>iteration start</a></code>.
<h5 id="calculating-the-simple-iteration-progress">Calculating the simple iteration progress</h5>
The <dfn>simple iteration progress</dfn> is a fraction of the progress
through the current iteration that ignores transformations to the time
introduced by the <a>playback direction</a> or <a>timing functions</a>
applied to the effect, and is calculated as follows:
1. If the <a>overall progress</a> is <a>unresolved</a>,
return <a>unresolved</a>.
1. If <a>overall progress</a> is infinity, let the <var>simple iteration
progress</var> be
<code><a>iteration start</a> % 1.0</code>,
otherwise, let the <var>simple iteration progress</var> be
<code><a>overall progress</a> % 1.0</code>.
1. If <em>all</em> of the following conditions are true,
* the <var>simple iteration progress</var> calculated above is zero,
<em>and</em>
* the animation effect is in the <a>active phase</a> <em>or</em> the
<a>after phase</a>, <em>and</em>
* the <a>active time</a> is equal to the <a>active duration</a>,
<em>and</em>
* the <a>iteration count</a> is <em>not</em> equal to zero.
let the <var>simple iteration progress</var> be 1.0.
<div class="note">
The above step implements the behavior that when an animation's
active interval ends precisely at the end of an iteration, it fills by
holding the endpoint of the final iteration rather than the start of the
next iteration.
The final condition prevents this from applying when we never played
any iterations of the animation to begin with because the
<a>iteration count</a> was zero.
</div>
1. Return <var>simple iteration progress</var>.
<h4 id="calculating-the-current-iteration">Calculating the current iteration</h4>
The <dfn>current iteration</dfn> can be calculated using the
following steps:
1. If the <a>active time</a> is <a>unresolved</a>, return
<a>unresolved</a>.
1. If the animation effect is in the <a>after phase</a> <em>and</em>
the <a>iteration count</a> is infinity, return infinity.
1. If the <a>simple iteration progress</a> is 1.0,
return <code>floor(<a>overall progress</a>) - 1</code>.
1. Otherwise, return <code>floor(<a>overall progress</a>)</code>.
<h3 id="direction-control">Direction control</h3>
<a>Animation effects</a> may also be configured to run iterations in
alternative directions using direction control.
For this purpose, <a>animation effects</a> have a <dfn>playback
direction</dfn> parameter which takes one of the following values:
* normal,
* reverse,
* alternate, or
* alternate-reverse.
The semantics of these values are incorporated into the calculation of
the <a>directed progress</a> which follows.
<div class="informative-bg"><em>This section is non-normative</em>
A non-normative definition of these values is as follows:
: normal
:: All iterations are played as specified.
: reverse
:: All iterations are played in the reverse direction from the way
they are specified.
: alternate
:: Even iterations are played as specified, odd iterations are played
in the reverse direction from the way they are specified.
: alternate-reverse
:: Even iterations are played in the reverse direction from the way
they are specified, odd iterations are played as specified.
</div>
<h4 id="calculating-the-directed-progress">Calculating the directed progress</h4>
The <dfn>directed progress</dfn> is calculated from the
<a>simple iteration progress</a> using the following steps:
1. If the <a>simple iteration progress</a> is <a>unresolved</a>, return
<a>unresolved</a>.
1. Calculate the <var>current direction</var> using the first
matching condition from the following list:
<div class="switch">
: If <a>playback direction</a> is <code>normal</code>,
:: Let the <var>current direction</var> be forwards.
: If <a>playback direction</a> is <code>reverse</code>,
:: Let the <var>current direction</var> be reverse.
: Otherwise,
::
1. Let <var>d</var> be the <a>current iteration</a>.
1. If <a>playback direction</a> is
<code>alternate-reverse</code> increment
<var>d</var> by 1.
1. If <code><var>d</var> % 2 == 0</code>, let the
<var>current direction</var> be forwards, otherwise let
the <var>current direction</var> be reverse.
If <var>d</var> is infinity, let the <var>current direction</var>
be forwards.
</div>
1. If the <var>current direction</var> is forwards then return
the <a>simple iteration progress</a>.
Otherwise, return <code>1.0 - <a>simple iteration progress</a></code>.
<h3 id="time-transformations">Time transformations</h3>
It is often desirable to control the rate at which an <a>animation
effect</a> progresses.
For example, easing the rate of animation can create a sense of
momentum and produce a more natural effect.
The CSS Easing Functions Module [[!CSS-EASING-1]] defines <a>timing
functions</a> for this purpose.
<a>Animation effects</a> have one <a>timing function</a> associated with
them.
The default <a>timing function</a> is the <a>linear timing
function</a>.
<h4 id="calculating-the-transformed-progress">Calculating the transformed progress</h4>
The <dfn>transformed progress</dfn> is calculated from the
<a>directed progress</a> using the following steps:
1. If the <a>directed progress</a> is <a>unresolved</a>,
return <a>unresolved</a>.
1. Calculate the value of the <var>before flag</var> as follows:
1. Determine the <var>current direction</var> using the procedure
defined in <a href="#calculating-the-directed-progress"
section></a>.
1. If the <var>current direction</var> is <span
class="prop-value">forwards</span>,
let <var>going forwards</var> be true, otherwise it is false.
1. The <var>before flag</var> is set if the animation effect is
in the <a>before phase</a> and <var>going forwards</var> is true; or if
the animation effect is in the <a>after phase</a> and <var>going
forwards</var> is false.
1. Return the result of evaluating the <a>animation effect</a>'s <a>timing
function</a> passing <a>directed progress</a> as the <a
spec=css-easing>input progress value</a> and <var>before flag</var> as the
<a spec=css-easing>before flag</a>.
<h3 id="the-iteration-progress">The iteration progress</h3>
The <dfn>iteration progress</dfn> of an <a>animation effect</a> is simply
its <a>transformed progress</a>.
<!-- End of timing model -->
<h2 id="animation-model">Animation model</h2>
<div class='informative-bg'><em>This section is non-normative</em>
For some kinds of <a>animation effects</a>, the Web Animations <em>animation
model</em> takes the <a>iteration progress</a> and <a>current iteration</a>
values produced by the <em>timing model</em> and uses them to calculate
a corresponding output.
The output of each such animation effect is then combined with
that of others using an <a>effect stack</a> before being
applied to the target properties (see [[#combining-effects]]).
</div>
<h3 id="introduction-to-the-animation-model">Introduction</h3>
An <a>animation effect</a> has zero or more associated properties that
it affects in response to changes to its timing output. These properties
are referred to as the effect's <dfn lt="target property">target
properties</dfn>.
Given an <a>iteration progress</a>, a <a>current iteration</a>, and an
<a>underlying value</a>, an <a>animation effect</a> produces
an <dfn>effect value</dfn> for each <a>animatable</a> <a>target property</a>
by applying the procedures from the <a>animation type</a> appropriate to the
property.
<h3 id="animating-properties">Animating properties</h3>
Unless otherwise specified, all CSS properties are
<dfn id="concept-animatable">animatable</dfn>.
How property values combine is defined by
the <dfn export>Animation type</dfn> line
in each property's property definition table:
<dl export>
<dt><dfn>not animatable</dfn>
<dd>
The property is not animatable.
It is not processed when listed in an animation keyframe,
and is not affected by transitions.
Note: Properties are typically excluded from animation
because animating them would create excessive complications.
For example, properties defining animation parameters are <a>not animatable</a>
since doing so would create complex recursive behavior.
Note: An <a>animation effect</a> that targets only properties
that are <a>not animatable</a> will still exhibit
the usual behavior for an <a>animation effect</a>
such as firing events and
delaying the fulfilment of the <a>animation</a>'s
<a>current finished promise</a>.
<dt><dfn>discrete</dfn>
<dd>
The property's values cannot be meaningfully combined,
thus it is <a>not additive</a> and <a>interpolation</a> swaps
from <var ignore>V<sub>a</sub></var> to <var ignore>V<sub>b</sub></var>
at 50% (<var ignore>p=0.5</var>),
i.e.
<div role="math" class="formula">
<math xmlns="http://www.w3.org/1998/Math/MathML">
<msub><mi>V</mi><mn>result</mn></msub><mo>=</mo>
<mrow>
<mfenced open="{" close="">
<mtable>
<mtr>
<mtd columnalign="left">
<msub><mi>V</mi><mn>start</mn></msub>
</mtd>
<mtd columnalign="left">
<mtext>if&nbsp;</mtext><mi>p</mi>
<mo>&lt;</mo><mn>0.5</mn>
</mtd>
</mtr>
<mtr>
<mtd columnalign="left">
<msub><mi>V</mi><mn>end</mn></msub>
</mtd>
<mtd columnalign="left">
<mtext>if&nbsp;</mtext><mi>p</mi>
<mo>&ge;</mo><mn>0.5</mn>
</mtd>
</mtr>
</mtable>
</mfenced>
</mrow>
</math>
</div>
<dt><dfn>by computed value</dfn>
<dd>
Corresponding individual components of the <a>computed values</a>
are combined (interpolated, added, or accumulated)
using the indicated procedure for that value type
(see [[css-values-4#combining-values]]).
If the number of components or the types of corresponding components
do not match,
or if any component value uses [=discrete=] animation
and the two corresponding values do not match,
then the property values combine as [=discrete=].
<dt><dfn>repeatable list</dfn>
<dd>
Same as <a>by computed value</a>
except that if the two lists have differing numbers of items,
they are first repeated to the least common multiple number of items.
Each item is then combined <a>by computed value</a>.
If a pair of values cannot be combined
or if any component value uses [=discrete=] animation,
then the property values combine as [=discrete=].
Note: The repeatable list concept ensures that
a list that is conceptually repeated to a certain length
(as 'background-origin' is repeated to the length of
the 'background-image' list)
or repeated infinitely
will smoothly transition between any values,
and so that the computed value
will properly represent the result
(and potentially be inherited correctly).
<dt>(See prose)
<dd>
Some properties have specific interpolation behavior not covered by the
above cases;
in this case the animation behavior will be specified explicitly
for that property.
</dl>
The [=animation type=] of properties that do not yet include
an [=Animation type=] line in their property definition,
is defined in [[#animation-types]].
<h4 id=custom-properties>
Custom Properties</h4>
For <a>custom properties</a> registered
using the {{CSS/registerProperty()}} method for the <a>current global object</a>,
the <a>animation type</a> is <a>by computed value</a>,
derived from the type used to define the property's {{PropertyDescriptor/syntax}}.
Where there is no <a>computed value</a> type that corresponds
to the property's specified {{PropertyDescriptor/syntax}}
(e.g. when the syntax is "*")
or when the <a>custom property</a> is not registered,
the <a>animation type</a> is <a>discrete</a>.
<h3 id="keyframe-effects">Keyframe effects</h3>
<dfn lt="keyframe effect">Keyframe effects</dfn> are a kind of <a>animation
effect</a> that use the output of the timing model to update CSS properties for
an element or pseudo-element such as <code>::before</code> or
<code>::after</code> [[!SELECT]] referred to as the <dfn>target element</dfn>.
<h4 id="keyframes-section">Keyframes</h4>
The <a>effect values</a> for a <a>keyframe effect</a>
are calculated by interpolating between a series of property values
positioned at fractional offsets.
Each set of property values indexed by an offset is called
a <dfn>keyframe</dfn>.
The <dfn lt="keyframe offset">offset of a keyframe</dfn> is a value
in the range [0, 1] or the special value null.
The list of <a>keyframes</a> for a <a>keyframe effect</a> must be
<dfn>loosely sorted by offset</dfn> which means that for each
<a>keyframe</a> in the list that has a <a>keyframe offset</a> that is
not null, the offset is greater than or equal to the offset of the
previous <a>keyframe</a> in the list with a <a>keyframe offset</a> that
is not null, if any.
The behavior when <a>keyframes</a> overlap or have unsupported values
is defined in <a
href="#the-effect-value-of-a-keyframe-animation-effect"
section></a>.
Each keyframe also has a <a>timing function</a> associated with it
that is applied to the period of time between the keyframe on which it
is specified and the <em>next</em> keyframe in the list.
The <a>timing function</a> specified on the last keyframe in the
list is never applied.
Each <a>keyframe</a> may have a <dfn>keyframe-specific composite
operation</dfn> that, if set, is applied to all values specified in that
<a>keyframe</a>.
The possible operations and their meanings are identical to those defined
for the <a>composite operation</a> associated with the <a>keyframe effect</a>
as a whole in [[#effect-composition]].
If the <a>keyframe-specific composite operation</a> for a <a>keyframe</a>
is not set, the <a>composite operation</a> specified for the
<a>keyframe effect</a> as a whole is used for values specified in that keyframe.
<h4 id="calculating-computed-keyframes">Calculating computed keyframes</h4>
Before calculating the <a>effect value</a> of a <a>keyframe effect</a>,
the property values specified on its <a>keyframes</a> are resolved to
<a>computed values</a>, and the offset to use for any keyframes with a null
<a>keyframe offset</a> is computed. The result of resolving these values is
a set of <dfn>computed keyframes</dfn>.
The calculated <a>keyframe offsets</a> of a set of <a>keyframe</a> that
includes suitable values for each null <a>keyframe offset</a> are referred
to as the <dfn lt="computed keyframe offset">computed keyframe offsets</a>.
To produce <a>computed keyframe offsets</a>, we define a procedure to
<dfn>compute missing keyframe offsets</dfn> that takes a sequence of
<a>keyframes</a>, <var>keyframes</var>, and has the following steps:
1. For each <a>keyframe</a>, in <var>keyframes</var>,
let the <a>computed keyframe offset</a> of the <a>keyframe</a>
be equal to its <a>keyframe offset</a> value.
1. If <var>keyframes</var> contains more than one
<a>keyframe</a> and the <a>computed keyframe offset</a> of
the first <a>keyframe</a> in <var>keyframes</var> is null,
set the <a>computed keyframe offset</a> of
the first <a>keyframe</a> to 0.
1. If the <a>computed keyframe offset</a> of the last <a>keyframe</a> in
<var>keyframes</var> is null, set its
<a>computed keyframe offset</a> to 1.
1. For each pair of <a>keyframes</a> <var>A</var> and <var>B</var>
where:
* <var>A</var> appears before <var>B</var> in
<var>keyframes</var>, and
* <var>A</var> and <var>B</var> have a <a>computed keyframe
offset</a> that is not null, and
* all <a>keyframes</a> between <var>A</var> and <var>B</var>
have a null <a>computed keyframe offset</a>,
calculate the <a>computed keyframe offset</a> of
each <a>keyframe</a> between <var>A</var> and <var>B</var>
as follows:
1. Let <dfn>offset<sub><var>k</var></sub></dfn> be the <a>computed
keyframe offset</a> of a <a>keyframe</a> <var>k</var>.
1. Let <var>n</var> be the number of keyframes <em>between</em> and
including <var>A</var> and <var>B</var> minus 1.
1. Let <var>index</var> refer to the position of
<var>keyframe</var> in the sequence of keyframes between
<var>A</var> and <var>B</var> such that the first keyframe
after <var>A</var> has an <var>index</var> of 1.
1. Set the <a>computed keyframe offset</a> of <var>keyframe</var> to
<a lt="offsetk">offset</a><sub><var>A</var></sub> +
(<a lt="offsetk">offset</a><sub><var>B</var></sub> &minus;
<a lt="offsetk">offset</a><sub><var>A</var></sub>)
&times; <var>index</var> / <var>n</var>.
<a>Computed keyframes</a> are produced using the following procedure.
Note that this procedure is only performed on a <a>keyframe effect</a> having
a <a>target element</a> for which computed property values can be calculated.
1. Let <var>computed keyframes</var> be an empty list of <a>keyframes</a>.
1. For each <var>keyframe</var> in the list of <a>keyframes</a> specified on
this <a>keyframe effect</a>, perform the following steps:
1. Add a new empty <a>keyframe</a>, <var>computed keyframe</var>, to
<var>computed keyframes</var>.
1. For each property specified in <var>keyframe</var>, calculate the
computed value specified on <var>keyframe</var> using the <a>target
element</a> as the context for computing values and add the
corresponding property and computed value to <var>computed
keyframe</var>.
For shorthand properties, add the equivalent longhand properties.
For logical properties [[!CSS-LOGICAL-1]], add the [=equivalent physical
properties=] [[!CSS-WRITING-MODES-4]] based on the computed value of
'writing-mode' and/or 'direction' for <a>target element</a>.
For example, if <var>keyframe</var> has a value of &lsquo;12pt&rsquo;
for the 'border-width' property, the user agent may produce a computed
value of &lsquo;16px&rsquo; for each of the longhand properties:
'border-bottom-width', 'border-left-width', 'border-right-width', and
'border-top-width'.
As a result, <var>computed keyframe</var> would <em>not</em> have a
value for the 'border-width' property, but would instead include
each of the longhand properties, and each with the computed value,
&lsquo;16px&rsquo;.
If conflicts arise when expanding shorthand properties or replacing
logical properties with physical properties, apply the following rules
in order until the conflict is resolved:
1. Longhand properties override shorthand properties (e.g.
'border-top-color' overrides 'border-top').
1. Shorthand properties with fewer longhand components override those
with more longhand components (e.g. 'border-top' overrides
'border-color').
1. For shorthand properties with an equal number of longhand
components, properties whose IDL name (see the <a>CSS property to
IDL attribute</a> algorithm [[!CSSOM]]) appears earlier when
sorted in ascending order by the Unicode codepoints that make up
each IDL name, override those who appear later.
1. Physical properties override logical properties.
1. Apply the procedure to <a>compute missing keyframe offsets</a> to
<var>computed keyframes</var>.
1. Return <var>computed keyframes</var>.
<h4 id="the-effect-value-of-a-keyframe-animation-effect">The effect value of
a keyframe effect</h4>
The <a>effect value</a> of a single property
referenced by a <a>keyframe effect</a> as one of its
<a lt="target property">target properties</a>, for a given
<var>iteration progress</var>, <var>current iteration</var> and
<var>underlying value</var> is calculated as follows.
1. If <var>iteration progress</var> is <a>unresolved</a> abort this
procedure.
1. Let <var>target property</var> be the longhand property for which the
<a>effect value</a> is to be calculated.
1. If <a>animation type</a> of the <var>target property</var> is
<a>not animatable</a> abort this procedure
since the effect cannot be applied.
1. Define the <dfn>neutral value for composition</dfn> as a value
which, when combined with an <a>underlying value</a> using the <a
lt="composite operation add">add</a> <a>composite
operation</a>, produces the <a>underlying value</a>.
1. Let <var>property-specific keyframes</var> be the result of
getting the set of <a>computed keyframes</a> for this <a>keyframe
effect</a>.
1. Remove any <a>keyframes</a> from <var>property-specific
keyframes</var> that do not have a property value for
<var>target property</var>.
1. If <var>property-specific keyframes</var> is empty, return
<var>underlying value</var>.
1. If there is no <a>keyframe</a> in <var>property-specific
keyframes</var> with a <a>computed keyframe offset</a> of
0, create a new <a>keyframe</a> with a <a>computed keyframe offset</a> of
0, a property value set to the <a>neutral value for
composition</a>, and a <a>composite operation</a> of <a
lt="composite operation add">add</a>, and prepend it to the
beginning of <var>property-specific keyframes</var>.
1. Similarly, if there is no <a>keyframe</a> in
<var>property-specific keyframes</var> with a <a>computed keyframe
offset</a> of 1,
create a new <a>keyframe</a> with a <a>computed keyframe offset</a> of 1,
a property value set to the <a>neutral value for composition</a>,
and a <a>composite operation</a> of <a
lt="composite operation add">add</a>, and append it to the
end of <var>property-specific keyframes</var>.
1. Let <var>interval endpoints</var> be an empty sequence of
keyframes.
1. Populate <var>interval endpoints</var> by following the steps from
the first matching condition from below:
<div class="switch">
: If <var>iteration progress</var> &lt; 0 and there is more
than one <a>keyframe</a> in <var>property-specific
keyframes</var> with a <a>computed keyframe offset</a> of
0,
:: Add the first <a>keyframe</a> in <var>property-specific
keyframes</var> to <var>interval endpoints</var>.
: If <var>iteration progress</var> &ge; 1 and there is more
than one <a>keyframe</a> in <var>property-specific
keyframes</var> with a <a>computed keyframe offset</a> of
1,
:: Add the last <a>keyframe</a> in <var>property-specific
keyframes</var> to <var>interval endpoints</var>.
: Otherwise,
::
1. Append to <var>interval endpoints</var> the last
<a>keyframe</a> in <var>property-specific
keyframes</var> whose <a>computed keyframe offset</a> is less than
or equal to <var>iteration progress</var> and less than 1. If
there is no such <a>keyframe</a> (because, for example,
the <a>iteration progress</a> is negative), add the last
<a>keyframe</a> whose <a>computed keyframe offset</a> is 0.
1. Append to <var>interval endpoints</var> the next
<a>keyframe</a> in <var>property-specific keyframes</var>
after the one added in the previous step.
</div>
1. For each <var>keyframe</var> in <var>interval endpoints</var>:
1. If <var>keyframe</var> has a <a>composite operation</a>
that is <em>not</em> <a
lt="composite operation replace">replace</a>, or
<var>keyframe</var> has no <a>composite operation</a>
and the <a>composite operation</a> of this <a>keyframe
effect</a> is <em>not</em> <a
lt="composite operation replace">replace</a>, then
perform the following steps:
1. Let <var>composite operation to use</var> be the
<a>composite operation</a> of <var>keyframe</var>, or if
it has none, the <a>composite operation</a> of this
<a>keyframe effect</a>.
1. Let <var>value to combine</var> be the property value of
<var>target property</var> specified on
<var>keyframe</var>.
1. Replace the property value of <var>target property</var>
on <var>keyframe</var> with the result of combining
<var>underlying value</var> (<var>V</var><sub>a</sub>) and
<var>value to combine</var> (<var>V</var><sub>b</sub>)
using the procedure for the <var>composite operation to use</var>
corresponding to the <var>target property</var>'s
<a>animation type</a>.
1. If this <a>keyframe effect</a> has an <a>iteration
composite operation</a> of <a
lt="iteration composite operation accumulate">accumulate</a>,
apply the following step <var>current iteration</var> times:
* replace the property value of <var>target property</var>
on <var>keyframe</var> with the result of combining the
property value on the final keyframe in <var>property-specific
keyframes</var> (<var>V</var><sub>a</sub>) with the
property value on <var>keyframe</var>
(<var>V</var><sub>b</sub>) using the <a
lt="value accumulation">accumulation procedure</a>
defined for <var>target property</var>.
Note: The order of arguments here is important. In the case where
the animation type of the target property does not define a
procedure for accumulation or addition, the default definition
for these procedures result in <var>V</var><sub>b</sub> being
returned. When performing iteration composition on propreties
that do not support accumulation, the result should be the
initial property value of <var>target property</var> on
<var>keyframe</var>, hence we we make this
<var>V</var><sub>b</sub> in the above step.
1. If there is only one keyframe in <var>interval endpoints</var>
return the property value of <var>target property</var> on that
keyframe.
1. Let <var>start offset</var> be the <a>computed keyframe offset</a> of the
first keyframe in <var>interval endpoints</var>.
1. Let <var>end offset</var> be the <a>computed keyframe offset</a> of
last keyframe in <var>interval endpoints</var>.
1. Let <var>interval distance</var> be the result of evaluating
<code>(<var>iteration progress</var> - <var>start offset</var>) /
(<var>end offset</var> - <var>start offset</var>)</code>.
1. Let <var>transformed distance</var> be the result of evaluating
the <a>timing function</a> associated with the first keyframe in
<var>interval endpoints</var> passing <var>interval distance</var>
as the input progress.
1. Return the result of applying the <a
lt="interpolation">interpolation procedure</a> defined
by the <a>animation type</a> of the <var>target property</var>,
to the values of the <var>target property</var> specified on the
two keyframes in <var>interval endpoints</var> taking the first such
value as <var>V</var><sub>start</sub> and the second as
<var>V</var><sub>end</sub> and using <var>transformed
distance</var> as the interpolation parameter <var ignore>p</var>.
<div class="note">
Note that this procedure assumes the following about the list of
<a>keyframes</a> specified on the effect:</p>
* Each <a>keyframe</a> has a specified <a>computed keyframe offset</a> in
the range [0, 1].
* The list of <a>keyframes</a> is sorted in ascending order by
<a>computed keyframe offset</a>.
* For a given property, there is at most one specified property
value on each keyframe.
It is the responsibility of the user of the model (for example,
a declarative markup or programming interface) to ensure these
conditions are met.
For example, for the <a href="#programming-interface">programming
interface</a> defined by this specification, these conditions are
met by the procedure to produce the <a>computed keyframes</a> that
become the input to this procedure.
</div>
Note: this procedure permits overlapping <a>keyframes</a>.
The behavior is that at the point of overlap the output value jumps to
the value of the last defined <a>keyframe</a> at that offset.
For overlapping keyframes at 0 or 1, the output value for <a>iteration
progress</a> values less than 0 or greater than
or equal to 1 is the value of the first <a>keyframe</a> or the last
<a>keyframe</a> in <var>keyframes</var> respectively.
<div class="issue">
In the presence of certain timing functions, the input iteration
progress to an animation effect is not limited to the range [0, 1].
Currently, however, keyframe offsets <em>are</em> limited to the
range [0, 1] and property values are simply extrapolated for input
iteration progress values outside this range.
We have considered removing this restriction since some cases exist
where it is useful to be able to specify non-linear
changes in property values at iteration progress values outside the
range [0, 1].
One example is an animation that interpolates from green to yellow
but has an overshoot timing function that makes it temporarily
interpolate &lsquo;beyond&rsquo; yellow to red before settling back
to yellow.
While this effect could be achieved by modification of the keyframes
and timing function, this approach seems to break the model's
separation of timing concerns from animation effects.
It is not clear how this effect should be achieved but we note that
allowing keyframe offsets outside [0, 1] may make the currently
specified behavior where keyframes at offset 0 and 1 are synthesized
as necessary, inconsistent.
See <a
href='http://lists.w3.org/Archives/Public/public-fx/2013AprJun/0184.html'>section
4 (Keyframe offsets outside [0, 1]) of minuted discussion from Tokyo
2013 F2F</a>.
<a href="https://github.com/w3c/csswg-drafts/issues/2081">&lt;https://github.com/w3c/csswg-drafts/issues/2081&gt;</a>
</div>
<h3 id="combining-effects">Combining effects</h3>
<div class='informative-bg'><em>This section is non-normative</em>
After calculating the <a>effect values</a> for a
<a>keyframe effect</a>, they are applied to the <a>animation
effect</a>'s <a lt="target property">target properties</a>.
Since it is possible for multiple <a>in effect</a> <a>keyframe effects</a> to
target the same property it is often necessary to combine the results of several
<a>keyframe effects</a> together.
This process is called <dfn lt='composite | composition'>compositing</dfn>
and is based on establishing an <a>effect stack</a> for each property
targeted by an <a>in effect</a> <a>animation effect</a>.
After [=compositing=] the results of <a>keyframe
effects</a> together, the composited result is combined with other
values specified for the <a>target property</a>.
The arrangement is illustrated below:
<figure>
<img src="img/animation-cascade.svg" width="500"
alt="Overview of the application of effect values to their target properties">
<figcaption>
Overview of the application of <a>effect values</a> to
their <a>target properties</a>.<br>
The results of <a>keyframe effects</a> targeting the same property
are composited together using an <a>effect stack</a>.<br>
The result of this composition is then inserted into the CSS cascade
at an appropriate point.
</figcaption>
</figure>
For the first part of this operation&mdash;combining <a>effect values</a> that
target the same <a lt="target property">property</a>&mdash; it is necessary to
determine both
<em>how</em> <a>keyframe effects</a>
are combined with one another,
as well as the <em>order</em> in which they are applied, that is,
their relative <em>composite order</em>.
The matter of <em>how</em> <a>effect values</a> are
combined is governed by the <a>composite operation</a> of
the corresponding <a>keyframe effects</a>.
The relative <em>composite order</em> of <a>effect values</a>
is determined by an <a>effect stack</a> established for each
animated property.
</div>
<h4 id="animation-classes">Animation classes</h4>
This specification provides a common animation model intended to be used
by other specifications that define markup or programming interfaces on
top of this model. The particular markup or programming interface that
generated an <a>animation</a> defines its <dfn>animation class</dfn>.
Further specifications may define specialized behavior for composite
ordering between different classes of animations or within a particular class.
<div class='informative-bg'><em>This section is non-normative</em>
For example, animations whose <a lt="animation class">class</a> is &lsquo;CSS
animation&rsquo; are defined as having a <em>higher</em> composite order than
animations whose class is &lsquo;CSS transition&rsquo; but <em>lower</em> than
other animations without a specific class.
Within the set of &lsquo;CSS animation&rsquo; objects, specialized
composite ordering is defined based on the 'animation-name' property
amongst other factors.
</div>
<h4 id="the-effect-stack">The effect stack</h4>
Associated with each property <a lt="target property">targeted</a>
by one or more <a>keyframe effects</a> is an <dfn>effect
stack</dfn> that establishes the relative composite order of the <a>keyframe
effects</a>.
The relative
<dfn lt="animation composite order" local-lt="composite order" export>composite order</dfn>
of any two <a>keyframe effects</a>, <var>A</var> and <var>B</var>,
within an <a>effect stack</a> is
established by comparing their properties as follows:
1. Let the <dfn>associated animation of an animation effect</dfn>
be the <a>animation</a> <a
lt="associated with an animation">associated</a> with the
<a>animation effect</a> that affecting the property with which this
<a>effect stack</a> is associated.
2. Sort <var>A</var> and <var>B</var> by applying the following
conditions in turn until the order is resolved,
1. If <var>A</var> and <var>B</var>'s associated animations differ by
<a lt="animation class">class</a>, sort by any inter-class composite
order defined for the corresponding classes.
1. If <var>A</var> and <var>B</var> are still not sorted,
sort by any <a lt="animation class">class</a>-specific composite order
defined by the common class of <var>A</var> and <var>B</var>'s
associated animations.
1. If <var>A</var> and <var>B</var> are still not sorted,
sort by their corresponding position in the <a>global animation
list</a>.