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 provides a programming interface to the model. This interface can be used to inspect and extend animations produced by declarative means or for directly producing animations when a procedural approach is more suitable.
The Web Animations model aims at three broad areas of application:
Animation can be used to give visual clues and feedback to make a user interface more readily comprehensible.
For example, a user action results in a table row being removed to represent an item being removed from a shopping cart. In such a case, fading the row to transparent and then shifting the subsequent rows up to fill the space over a few hundred milliseconds provides the user with clear feedback as to the results of their action as opposed to instantly removing the row from the DOM.
To support this scenario not only are the animated effects of fading and shifting required, but so is synchronization, both between the animations, and between animations and scripted actions (removing the table row from the DOM after the animations have completed).
Another type of animation uses the animated effect to convey a story or represent some information. Unlike user interface effects which are largely a presentational adjunct to the content, these animations form an essential part of the content presented to the user.
For example, in an animated cartoon two cats fly through space to another planet leaving a rainbow trail behind them. After arriving at the planet a change of scene occurs and the user should decide whether or not the cats enter a magic mountain by selecting one of two preset destinations in the scene.
This scenario requires the following features:
Similar use cases in this category include visualising physical phenomena such as spring motion for educational purposes, or visualising data such as the prevalence of a disease over a geographical space over a year whereby animation provides a natural means of presenting the time-based component of the data.
Finally, animation is often used in conjunction with other media such as video and audio.
As an example, the subtitles shown on a karaoke video may indicate the current position in the lyrics through a ball bouncing from one syllable to the next. In this scenario it is necessary to maintain strict synchronization between the audio and animation even in the face of user interaction such as rewinding or pausing the audio or network events such as a delay due to media buffering.
A more complicated example of this category is applying a series of independent sound effects to an animated cartoon or adding a video as the background of an animated scene such that is cued to start mid-way into the scene.
The nature of these uses cases differs significantly and whilst Web Animations attempts to address each use case, technologies built on top of the Web Animations may expose features tailored towards more specific use cases rather catering to each scenario equally.
CSS Transitions [[CSS3-TRANSITIONS]], CSS Animations [[CSS3-ANIMATIONS]], and SVG [[SVG112]] all provide mechanisms that generate animated content on a web page. Although the three specifications provide many similar features, the syntaxes are incompatible and heretofore the animations have not been able to be interchanged and synchronized.
This specification proposes an abstract animation model that encompasses the abilities of both CSS and SVG, and additionally provides a common programming interface to expose these features to script.
This specification is accompanied by a CSS embedding specification, which describes how CSS features can be implemented in terms of Web Animations primitives, and an SVG embedding specification which does the same for SVG.
As a result, this specification does not directly alter the behavior of CSS Transitions, CSS Animations, or SVG. However, Web Animations is intended to replace the SMIL Animation [[SMIL-ANIMATION]] specification where it is currently used to define the behavior of SVG's animation features.
This specification makes some additions to some interfaces defined in HTML5 [[HTML5]].
At a glance, Web Animations consists of two largely independent pieces, a timing model and an animation model. The role of these pieces is as follows:
Graphically, this flow can be represented as follows:
Overview of the operation of Web Animations.
The current time is input to the timing model which produces a time
fraction.
This distance is used as input to the animation model which produces the
values to apply.
For example, consider an animation that:
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 for the rectangle of 75.
This specification begins with the timing model and then proceeds to the animation model.
Two features characterise the Web Animations timing model: it is stateless and it is hierarchical.
The Web Animations timing model operates by taking an input time and producing an output time fraction. Since the output is based solely on the input time and is entirely independent of previous inputs, the model may be described as stateless. This gives the model the following properties:
There are two apparent exceptions to the stateless behavior of the timing model.
Firstly, events are fired when, for example, one sample falls on the opposite side of an animation's interval boundary to the previous sample. This is certainly stative behavior. However, events should be considered as a layer added on top of the core timing model. In the case where no event listeners are registered, the model is stateless.
The other exception to this stateless behavior is that a number of
methods such as pause
and reverse
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, like events, are layered on top.
Finally, each time the model is sampled, it can be considered to establish a temporary state. While this temporary state affects the values returned from the API, it has no influence on the subsequent samples and hence does not conflict with the stateless qualities described above.
The other characteristic feature of the Web Animations timing model is that time is inherited. Time begins with a monotonically increasing time source and cascades down a number of steps to each animation. At each step, time may be shifted backwards and forwards, scaled, reversed, paused, and repeated.
A hierarchy of time sources. Each node in the tree refers to a time source from which it derives its time. This parent time source is referred to here as the node's timeline.
A consequence of this hierarchical arrangement is that complex animation arrangements can be reversed, scheduled, accelerated and so on, as a whole unit since the manipulations applied to the parent cascade down to its descendants. Furthermore, since time has a common source, it is easy to synchronize animations.
In Web Animations timing is based on a hierarchy of time relationships where the nodes are called time sources. Time sources provide a time value for the purpose of deriving further time sources and for eventual input into the animation model.
A future version or module may offer time sources based on UI gestures or other quantities that are less related to time.
Some time sources further separate the time value into a current time value and a result time value.
The current time value is a point of progress in the time source and is used for seeking the time source.
The output time value is the current time value with an optional transformation applied and is used for synchronizing consumers of the time source.
Other options for naming:
result time value (resultTime
),
filtered time value (filteredTime
),
child time value (childTime
),
sync time value (syncTime
)
…
For simple time sources, and unless otherwise stated, the current time value and output time value are one and the same.
This complexity arises from the fact that in other APIs
currentTime
is used both as the input for seeking (e.g.
HTMLMediaController, HTMLMediaElement) and also as the output for
syncing (e.g. HTMLMediaController).
We want to be consistent in allowing currentTime
to be
used for seeking and we also want to allow such objects to be used as
time sources.
As a result we allow currentTime
to do double-duty in
simple cases, and introduce outputTime
for cases where
it is necessary to distinguish between the two times.
Time sources are represented in the Web Animations API by the TimeSource interface.
TimeSource
interfaceThe current time value of this time source in seconds. This value is updated each time the model is sampled (see ).
Note that this will be null
if the time source cannot
currently produce a time value.
For example, a time source that derives its time from another time
source will return null
if the parent time source is
not provided, is unavailable, or if the parent time source reports
null
as its output time value.
Note that while currentTime
is read-only in this
interface, it is writeable in the derived interface
TimedItem.
Towards the root of the time hierarchy is the document time source. A document time source is associated with a Document [[HTML5]] object and provides a monotonically increasing time value that begins at zero when the Document begins to play.
A document time source begins to play at the earliest of the following possible moments:
play
on the
DocumentTimeSource.autoplay
attribute on the DocumentTimeSource is true
at
that moment.
For HTML documents, this is the moment after the current document readiness has changed to "complete" but before dispatching the load event. In terms of the timings defined in [[NAVIGATION-TIMING]], this occurs between the domComplete and loadEventStart timings.
autoplay
attribute is made newly
true
at a moment after opportunity 2.
Before a document time source begins to play, its current time
value is null
.
Once a document time source has begun to
play its immediate time value is zero and from that point onwards it
reports the number of seconds since it began
to play subject to the constraints on updating the time value in .
Once a document time source has begun to play it cannot be restarted or
paused and continues to be non-null
for the remainder of
its lifetime.
Indicates if the document time source should begin to play automatically at the moment immediately
before the "load" event is fired.
Initially true
.
Returns the time value from another DocumentTimeSource, other, converted to a time value relative to the when this document time source began.
Returns null
if documentTime time represents
a time earlier than when this document time source began to play or if this document time
source has not yet begun to play.
Exceptions:
IndexSizeError
Returns the number of seconds between when event was fired and when this document time source began.
Since the timeStamp
attribute of the
Event
interface specified in [[DOM-LEVEL-3-EVENTS]]
is not guaranteed to be monotonically increasing implementations
SHOULD record alongside each event a suitable monotonically
increasing timestamp that can be used to convert to an appropriate
time value here.
Returns null
if the time of the event precedes when
this document time source began to
play or if this document time source has not yet begun to
play.
This might be deferred to a later version.
Returns the corresponding time value for a given time source, other, for a time value relative to this document time source using the following procedure.
timeline
property on A if
such a property exists and refers to a TimeSource
object.DOMException
of type
HierarchyRequestError
.
toDocumentTime(documentTime,
remote document)
on this object.
currentTime
of other by
following the steps described somewhere...
Exceptions:
HierarchyRequestError
Is this needed? And is the automatic cross-document time source negotation required?
As document time sources are associated with a Document object, the following extension is required to the Document interface defined in [[HTML5]].
In addition to time sources, the other actor in the Web Animations timing model is timed items.
The complement of time sources—which produce time values—is timed items, which consume time values. The time source from which a timed item receives time values is called its timeline.
In the context of its timeline, each timed item is defined by an interval during which it is scheduled to be active.
At time t timed items A and B are active. Timed item C is no longer active. Timed item D has yet to begin and is not active.
As well as being consumer of time values, timed items are also themselves time sources. After consuming a time value most timed values apply a transformation to the value and then pass it on as an output time value.
The period that a timed item is scheduled to run is called the active interval. Each timed item has only one such interval.
The lower bound of the active interval is determined by the start time of the timed item but may be shifted by a start delay on the timed item.
The upper bound of the interval is determined by the active duration.
The relationship between the start time, start delay, and active duration is illustrated below.
Examples of the effect of the start delay on the endpoints of
the active interval.
(a) A timed item with no delay; the start time and start of
item are coincident.
(b) A timed item with a positive delay; the start of the
item is deferred by the delay.
(c) A timed item with a negative delay; the start of the
item is brought forward by the delay.
In Web Animations all times are relative to some point of reference. These different points of reference produce different time spaces.
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.
For example, since a timed item can specify its own start time, the time values produced by a timed item will be offset from those received from its time source. As a result we say that the timed item establishes a new time space which we call local time as illustrated below.
Inherited time and local time.
At time t, the inherited time is 2.5.
For timed item (a), the local time is 1.5.
For timed item (b), the local time is also 1.5 since local time is
based on a timed item's start time only, and not on its start delay.
From the point of a view of a given timed item we can talk about the following time spaces:
In addition to these time spaces we can also refer to the document time space which is equivalent to the inherited time space of the DocumentTimeSource of the active document.
Furthermore, timed items that allow repeating introduce a further time space, iteration time space (see ).
For a given timed item, the inherited time at a given
moment is the value of the output time value of the time
source. If no time source is associated with the timed item, the
inherited time is null
.
From the point of view of the API, the inherited time is based on the first matching condition of the following:
timeline
is null
,null
.timeline
has an attribute
outputTime
,timeline.currentTime
.
For some calculations it is useful to be able to use zero when the
inherited time is null
. For this purpose we define
the effective inherited time as the inherited time
unless that value is null
in which case the effective
inherited time is zero.
The normative definition of local time follows the definition of time drift and is found in .
At given moment, a timed item may be described as being in one of several overlapping states. These states are only established for the duration of a single sample and are primarily a convenience for defining the behavior of events and various API methods.
The different states are illustrated below.
An example of the different states used for describing a timed item at a given time. Note that a timed item is considered to be in effect for all times inside the active interval as well as times outside the active interval where a fill mode is applied.
These states and their useage within the model are summarised as follows:
getCurrentAnimations
method defined on the
Document
and Element
interfaces.
null
.
The normative definition of each of these states follows.
A timed item is scheduled if either of the following conditions is true:
null
and is
less than the item's startDelay
, ortimeline
attribute) which is
a scheduled timed item.A timed item is active if all of the following conditions are met:
null
,
andstartDelay
, andactiveDuration
.A timed item is current if it is either scheduled or active.
A timed item is in effect if its active time as
calculated according to the procedure in is not
null
.
Timed items are represented in the Web Animations API by the TimedItem interface.
The time which, when combined with the startDelay
,
defines the lower bound of the active interval.
It is expressed in seconds in the inherited time space.
When this item is the child of a sequence timing group, any
previously set value for startTime
is ignored and
instead the value of this property is determined by the procedure
defined in .
Furthermore, attempts to set this property will raise a
DOMException
of type InvalidStateError
.
If this item is later removed from the sequence timing group,
any previously set value will be restored.
Exceptions:
InvalidStateError
The number of seconds which, when added to the timed item's
startTime
, defines the lower bound of the timed
item's active interval.
A non-normative description of the effect of this property on timing is given in .
The length in seconds of the active interval.
Initially, this attribute will reflect the intrinsic run-time active duration. Changes to the model that cause the intrinsic run-time active duration to change are reflected in the value returned here.
The intrinsic run-time active duration may be overridden by setting this attribute to any real number (see ).
If the zero run-time playback rate flag is set, returns
Infinity
.
How can you restore the active duration to reflecting the
intrinsic run-time active duration?
resetActiveDuration
?
Call this simply duration
?
Exceptions:
IndexSizeError
It might be simpler and more consistent to split out
defaultActiveDuration
and activeDuration
here?
However, in this case defaultActiveDuration
would not
be overridable? i.e. readonly?
The upper bound of the active interval expressed in
seconds relative to the inherited time space of
timeline
.
Should this reflect the run-time active interval or the default
active interval? i.e. if you set playbackRate
to
2 should this change?
It might be more useful to have to reflect the default active
interval since activeDuration
already reflects the
run-time interval.
If this reflects the default it should probably ignore the
timeDrift
and then we could remove the definition
of the default end time defined in .
endTime
is calculated as follows:
locallyPaused
is true
,endTime
is positive infinity.
endTime
is the result of evaluating
startTime + startDelay +
activeDuration + timeDrift
.
Note that while the endTime
is read-only, it can be
set indirectly by overriding the activeDuration
property.
For example, to set endTime
to time t in
inherited time space, set activeDuration
to
t - startTime - startDelay - timeDrift
.
Make this writeable?
We could make it just do the above calculation and set
activeDuration
, the caveat being that setting
endTime
< startTime
would fail
(even if the next line of code fixed startTime
to
make it valid).
Alternatively we could allow endTime
to be less
than startTime
and add logic to ignore such
intervals elsewhere.
The time source upon which this timed item's timing is based. The output time values of this time source provide the inherited time space for this timed item.
May be null
if this timed item is not associated with
a time source.
Animations without a time source do not affect their target.
Is this right? It's probably useful to be able to put animations
in a state where they have no effect on their target such as when
calling cancel
.
Should we just set target
to null
in that case?
As well as scheduling timed items to run for a given interval, it is possible to make adjustments to their playback as they run by seeking their current position, pausing and resuming, and disabling the animation outright.
Changing the current playback position of a timed item can be used to rewind a timed item to its start point, fast-forward to a point in the future, or to provide ad-hoc synchronization between items.
Seeking a timed item effectively creates an additional offset between the item and its time source. This offset is called the time drift and is illustrated below.
A time t a seek is performed on animation A changing its
local time from 1.5s to 2s.
As a result animation A′ has a time drift of
-0.5s.
Note that the start time indicated by a red star does not change
since it is expressed in inherited time space.
Timed items that do not have a time source may still be seeked. If they are later attached to a time source they will resume playback from the seeked time.
The normative description for seeking must account for pausing (see ) as well as follows.
In order to supporting seeking and pausing, timed items track the following three values:
timeDrift
.
locallyPaused
.
Seeking is performed in response to a change to a timed item's
currentTime
attribute and is realised by adjusting the
time drift or the pause start time of the item as
follows:
currentTime
should be set.effective inherited time - startTime -
seek time
.
The animation events dispatched when a seek is performed are described in .
Pausing can be used to temporarily suspend a timed item. Like seeking, pausing effectively causes the local time of a timed item to be offset from its time source and hence causes the time drift to be updated.
Pausing before a timed item begins will cause the item to begin in a paused state but does not delay its start as illustrated below.
The effect on pausing a timed item at different moments.
Timed item A is paused and unpaused before it would otherwise begin.
The pause has no effect.
Timed item B is paused before it would begin and remains paused.
At the schedule start time item B begins playing in a paused state.
When it is unpaused it continues playing.
The resulting timed item has a time drift equivalent to the interval
from the start time to when the item was unpaused.
Timed item C is paused and unpaused during its active interval.
After unpausing timed item has a time drift equivalent to the
duration of the pause interval.
The procedure for updating the local pause state is as follows:
currentTime
as
pause start time.
The time drift value is both a stored and a calculated value. When a timed item is paused, the value is calculated from the pause start time. When a timed item is not paused, the stored value is used. The stored value is initially zero, and is updated when the item is unpaused or seeked.
Since pausing does not take effect prior to when a timed item begins, we first define the beginning of a timed item for the purposes of pausing as a timed item's earliest moment. The calculation is as follows:
earliest moment = min(0, startDelay)
Using this definition we can define the value of time drift at a given moment as follows:
effective inherited time - startTime -
max(pause start time, earliest moment)
.
Having defined the time drift we can now define local time as described in .
local time =
inherited time - startTime - time drift
If either inherited time is null
, the local time
is null
.
During playback it is sometimes useful to completely disable a timed item such that it has no effect. This is achieved by disassociating the timed item from its time source.
The procedure for cancelling a timed item is as follows:
timeline
attribute is
null
, return immediately.timeline
is a timing group,
remove this timed item from the group and set
timeline
to null
.
Otherwise simply set timeline
to null
.
In either case, do not dispatch a timingend event on this
timed item or any of its descendents.
Returns the effective local time of the timed item.
This differs from the definition of local time used
elsewhere in the model in that when the inherited time time
is null
(e.g. because timeline
is
null
, or because the timeline has a null
time value) an inherited time of zero is used.
This allows the item to be seeked prior to being attached to
a time source.
The value returned is calculated as follows:
currentTime =
effective inherited time - startTime
- timeDrift
Note that unlike the currentTime
attribute on
the TimeSource interface, this attribute can be set.
Setting this property performs a seek operation according to the
steps described in described in .
Furthermore, as a result of the definition above, for
TimedItem objects this property will never be
null
.
Attempts to set this property to null
will result in
a DOMException
of type
InvalidModificationError
being thrown.
Note that the currentTime
property is not actually
set directly but is updated to the seek time as a result of the
performing the calculation above using the updated
timeDrift
established using the procedure described
in .
Exceptions:
InvalidModificationError
null
.
The number of seconds that the actual local time of this item lags behind its scheduled time as a result of pausing and seeking this item.
The calculation of the time drift is described in .
resetDrift()
).
The local pause state of this timed item.
The behavior when setting this value is described in .
Should this be writeable? Once we integrate with media controllers we might want to make this writeable only when not associated with a media controller much like things currently work in HTML5.
This attribute is at-risk.
It remains to be seen if really needs to be exposed, or if
pause()
, play()
, and
unpause()
are sufficient for most use cases.
true
if and only if the
local pause state of at least one of this item or one of
its ancestor timed items is true.
We really want to say ancestor time sources here but time sources don't have pause state.
true
by
following the procedure described in .
This method is intended to behave in a comparable manner to HTMLMediaElement's pause method.
Unpauses this item and, if the local time of the item is not within the active interval, seeks to the beginning of the active interval by taking the following steps.
currentTime > startDelay
+ activeDuration
and
playbackRate ≥ 0
, seek to
startDelay
following the procedure described in
.false
using
the procedure described in .
To unpause the item without seeking, set
locallyPaused
to false
.
This method is intended to behave in a comparable manner to HTMLMediaElement's play method.
Disassociates this timed item from its TimeSource
(represented by timeline
).
As a result, if the timed item or its children are
Animation objects, they will no longer affect their target
elements.
Outside of the active interval, a timed item may still have an effect depending on its fill mode.
The different fill modes are as follows:
Some examples of the these fill modes are illustrated below.
Examples of various fill modes and the states produced.
(a) fill mode ‘none’. The timed item has no effect outside
its active interval.
(b) fill mode ‘forwards’. After the active interval has
finished, the timed value continues to maintain a fill value.
(c) fill mode ‘backwards’. The timed item produces a fill
value until the start of the active interval.
(d) fill mode ‘both’. Both before and after the active
interval the timed item produces a fill value.
The normative definition of fill behavior is incorporated in the calculation of the active time in .
Note that setting a fill mode has no bearing on the endpoints of the
active interval.
However, the fill mode does have an effect on various other
properties of the timing model since the active time of a timed
item is only defined (that is, not null
) inside the
active interval or when a fill is applied.
FillMode
enumerationIt is possible to specify that a timed item should repeat a fixed number of times or even indefinitely. This repetition occurs within the active interval. The span of time during which a single repetition takes place is called an iteration interval.
Unlike the active interval, a timed item can have multiple iteration intervals although typically only the interval corresponding to the current iteration is of interest.
The length of a single iteration is called the iteration duration. Comparing the iteration duration and the active duration we have:
The relationship between the iteration duration and active duration is illustrated below.
A comparison of the iteration and active durations for a timed item with an iteration count of 2.5. Note that the iteration duration for the final iteration does not change, it is simply cut-off by the active duration.
We have already encountered different time spaces in describing local time and inherited time. Repetition introduces yet another time space: the iteration time space.
Iteration time space is a time space whose zero time is the beginning of a timed item's current iteration.
Within the Web Animations model we also refer to active time which is a time relative to the beginning of the active interval. This time space, however, is not used in the API.
These time spaces are illustrated below.
A comparison of item time, active time, and iteration time for an animation with a iteration duration of 1s and an iteration count of 2.5.
Note that while the time spaces themselves are not bounded, Web Animations defines active time and iteration time 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 active time space, the procedure for calculating the active time defined in will never return a negative value.
In addition to these time spaces we can also refer to the document time space which is equivalent to the local time of the DocumentTimeSource of the active document.
When a timed item 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 [begin,end)
.
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 item, at local time 1s, the iteration time is 0. For the sequenced items, at inherited time 1s, only item B will be active; there is no overlap.
Illustration of end-point exclusive timing. For both repeated and sequenced timed items there is no overlap at the boundaries between intervals.
An exception to this behavior is that when performing a fill, if the fill begins at an interval endpoint, the endpoint is used. This behavior falls out of the algorithm given in and is illustrated below.
After one iteration, the iteration time is 0, but after two iterations (and thereonwards), the iteration time is equal to the iteration duration due to the special behavior defined when a timed item fills.
A finite real number greater than or equal to zero representing the number of iterations into the timed item at which to begin. For example, a value of 0.5 would cause the timed item to begin half-way through the first iteration.
The iterationCount
is effectively added to
the iterationStart
such that a timed item with an
iterationStart
of ‘0.5’ and
iterationCode
of ‘2’ would still
repeat twice however it would begin and end half-way through
the timed item's iteration interval.
Setting the iterationStart
to a value greater than
or equal to one is typically only useful in combination with an
animation effect that has an accumulateOperation
other than ‘replace’.
Exceptions:
IndexSizeError
A real number greater than or equal to zero (including positive infinity) representing the number of times to repeat the timed item.
Exceptions:
IndexSizeError
The duration in seconds of a single iteration. Unless set, this will reflect the intrinsic iteration duration of the timed item. If set, it must be greater than or equal to zero (including positive infinity).
After setting, this attribute can be restored to reflecting the
intrinsic iteration duration of the timed item by calling
resetIterationDuration
.
Exceptions:
IndexSizeError
This used to be called duration
for consistency with
CSS and SVG.
Which is better?
Causes the iterationDuration
attribute to reflect the
intrinsic iteration duration of the timed item, discarding
any previously set value for the attribute.
Returns the intrinsic iteration duration of the timed item.
In future we may introduce iterationTime
which
corresponds to the outputTime
prior to applying time
transformations.
The advantage of exposing this is that it could be writeable since the
transformation from iteration time to local time should be invertible.
It is also probably the time we will use for TimingEvent times.
The rate of play of a timed item can be controlled by setting its playback rate. For example, setting a playback rate of 2 will cause the item to run twice as fast. Likewise, a playback rate of -1 will cause the item to run backwards.
The playback rate is applied only to the active interval of a timed item and not to the time while it is delayed or filling.
Setting a negative playback rate also has the effect of inverting the fill mode.
There are two side of effects of changing the playback rate of a timed item that may be unexpected.
Since sometimes these side effects are undesirable Web Animations provides two means for controlling a timed item's playback rate.
These parameters and the corresponding API attributes are intended
to parallel (somewhat imprecisely) the attributes on the
HTMLMediaElement
interface.
In order to support speed control, timed items track the following three values:
defaultPlaybackRate
.
playbackRate
to zero.
Initially false.
Not exposed in the API.
In addition, we define the effective run-time playback rate as follows:
The value of the effective run-time playback rate is the run-time playback rate if it is defined. If the run-time playback rate the value of the effective run-time playback rate is that of the default playback rate.
Setting a negative playback rate has the effect of inverting a timed item's fill mode and hence we define the effective fill mode as follows:
fillMode
.
fillMode
as follows,
fillMode
is forwards
,backwards
.
fillMode
is backwards
,forwards
.
fillMode
.
The following procedure adjusts the run-time playback rate to new rate whilst maintaining the current scaled active time. If the current local time is outside the active interval the distance from the interval is scaled according to the change in speed.
Special handling is required for when the run-time playback rate is zero. Intuitively, this should pause the timed item at its current position however the way the model is designed that will not happen. Instead, the active duration will become infinite and the item will effectively jump back to the start position.
We work around that by providing special handling for when the run-time playback rate is zero. This handling consists of the following strategy:
playbackRate
to zero, record the
time as the pause time and basically employ pause behavior.
Don't actually update the playback rate in this case.activeDuration
report
Infinity
when zero run-time playback rate
is set.playbackRate
report
0
when zero run-time playback rate
is set.currentTime
.
null
,seek adjustment =
(local time - startDelay
) *
(1 - previous rate / new
rate)
. item time
- seek adjustment
.A real number that acts as a multiplier on the item's rate of play. This attribute reflects the default playback rate value described in .
The playbackRate
attribute, when set, explicitly
overrides this attribute for the purpose of calculating the
iteration time.
Run-time playback control. Setting this attribute allows overriding the playback rate in such a way that the default active duration does not change and performs a compensatory seek so that the position of the timed item does not jump.
The attribute returns the effective run-time playback rate
and as such will initially equal defaultPlaybackRate
.
If the zero run-time playback rate flag is set, returns 0.
Setting this value updates the run-time playback rate which in turn will be used as input when calculating the iteration time.
Setting this attribute also performs a compensatory seek on the timed item using the procedure defined in .
After setting, this attribute can be restored to reflecting
defaultPlaybackRate
by calling
resetPlaybackRate
.
Reverses this timed item such that it begins reversing from its
current point.
Note that this means that for a timed item that has yet to begin,
calling reverse
will mean that it never starts.
currentTime < startDelay
,startDelay + activeDuration
.
currentTime >
startDelay + activeDuration
,startDelay
.
activeDuration - currentTime - startDelay
.
playbackRate
to -playbackRate
.
currentTime
to seek time.
Makes the run-time playback rate undefined.
As a result the playbackRate
will reflect
defaultPlaybackRate
.
This also has the effect of setting the zero run-time playback rate flag to false.
Returns the default playback rate.
At the core of the Web Animations timing model is the process that takes an inherited time value and converts it to an iteration time.
Following this further transformations are applied before resulting at a final output time.
The first step in this process is to calculate the boundary when the item is active, that is, the active duration.
This process is illustrated below.
Calculation of the active duration is based on multiplying the iteration duration by the iteration count and then dividing by the playback rate.
The process for calculating the active duration is normatively defined in .
Having established the active duration, the process for transforming a timed item's inherited time into its transformed time is illustrated below.
An overview of timing model calculations.
(1) The inherited time is converted into a local time by
incorporating the start time and time drift.
(2) The local time is converted into an active time by
incorporating the start delay.
(3) The playback rate and iteration start properties are
applied to the active time to produce the scaled active
time.
(4) The scaled active time is then converted to an offset
within a single iteration: the iteration time.
(5) The iteration time is converted into a directed time
by incorporating the playback direction.
(6) Finally any timing functions are applied to the directed
time to produce the transformed time.
Steps 2 to 4 in the diagram are described following. The first step, calculating the local time is described in . Steps 5 and 6 are described in and respectively.
The iteration duration, unless overridden (see below), is simply its intrinsic iteration duration.
The intrinsic iteration duration of a timed item is zero, however some specific types of timed item such as timing groups override this behavior and provide an alternative intrinsic duration (see and ).
The Web Animations API also provides a means to
override the intrinsic iteration duration and provide another
value for the iteration duration via that
iterationDuration
attribute.
Since the intrinsic iteration duration of an animation is
zero, and the default fillMode
when constructing an
Animation is forwards, it is possible to
create animations that simply set a property without any
interpolation as follows,
new Animation(elem, { display: 'none' });
This is particularly useful in combination with other animations or timed items. For example, fading an element before switching ‘display’ to ‘none’ can be achieved as follows,
new SeqGroup( [ new Animation(elem, { opacity: '0%' }, 1), new Animation(elem, { display: 'none' }) ] );
The active duration is based on the timed item's playback rate of which we have both the default playback rate and run-time playback rate. As a result, we can define both the default active duration and run-time active duration. As with the playback rate, the default active duration is used by timing groups to schedule children whilst the run-time active duration is used to control playback.
In order to calculate the default active duration we first define the repeated duration as follows:
repeated duration =
iteration duration *
iterationCount
The default active duration is calculated according to the following steps:
Infinity
.
repeated duration
/ abs(default playback rate)
.
For the run-time active duration, we first define the intrinsic run-time active duration as the result of following the same steps for calculating the default active duration but substituting the effective run-time playback rate for the default playback rate.
Next we define the run-time active duration as simply the
intrinsic run-time active duration unless overridden by
providing another value for the run-time active duration via the
activeDuration
attribute.
The active time is based on the local time
and startDelay
.
It is defined only when the timed item is in effect and is
calculated according to the following steps:
null
, return
null
.
local time < startDelay
the result depends on the fillMode
as follows,
null
.
local time < startTime + run-time active
duration
, return local time
- startDelay
.
null
.
Before the active time can be converted to an iteration
time we must factor in the timed item's effective run-time
playback rate and iterationStart
.
This is called the scaled active time.
In order to calculate the scaled active time we first define the start offset as follows:
start offset =
iterationStart * iteration duration
The scaled active time is calculated according to the following steps:
null
, return
null
.
(active time -
run-time active duration)
* effective run-time playback rate
+ start offset
.
active time
* effective run-time playback rate
+ start offset
.
The iteration time is calculated according to the following steps:
null
,
return null
.
scaled active time - start
offset
is equal to the repeated duration,
and iterationCount
is not zero,
and (iterationCount + iterationStart) % 1
is zero,
return the iteration duration.
scaled active time
% iteration duration
.
The current iteration can be calculated using the following steps:
null
, return
null
.
floor(timing.iterationStart + timing.iterationCount)
.
timing.iterationStart
+ timing.iterationCount - 1
.
floor(scaled active time /
iteration duration)
.
If the iteration duration is infinite, the
result of floor(scaled active time /
iteration duration)
will be zero as defined by
IEEE 754-2008.
Timed items may also be configured to run iterations in alternative directions using direction control. Unlike speed control, setting a reverse direction does not invert the effective fill mode of the timed item.
PlaybackDirection
enumerationThe directed time is based on the iteration time using the following steps:
direction
is normal
,
direction
is reverse
,
direction
is
alternate-reverse
increment
d by 1.
There used to be a step here which seemed to be adding special handling for filling when the item ends on a repeat boundary but it seems like that is taken care of by the calcuation of iteration time and current iteration. Is anything actually needed here?
d % 2 == 0
, let the
current direction be forwards, otherwise let
the current direction be reverse.
Otherwise, return the iteration duration - iteration time.
Direction behavior as specified by one of the PlaybackDirection enumeration values.
It is often desirable to control the rate at which a timed item progresses. For example, easing the rate of animation can create a sense of momentum and produce a more natural effect. Conversely, in other situations such as when modelling a discrete change, a smooth transition is undesirable and instead it is necessary for the timed item to progress in a series of distinct steps.
For such situations Web Animations provides a variety of timing functions that scale the progress of a timed item.
Timing functions take an input time fraction in the range [0, 1] and produce a scaled output time fraction whose range is unbounded.
Example of a timing function that produces a ease-in effect.
Given a input timing fraction of 0.7, the timing function scales the
value to produce an output time fraction of 0.52.
By applying this timing function, time will appear to progress more
slowly at first but then gradually progress more quickly.
Such timing functions can be applied to an iteration of a timed item as a whole via the TimedItem interface or to a segment of a keyframe animation via the KeyframeAnimationEffect interface.
The timing functions provided by Web Animations share a common TimingFunction interface as defined below.
Takes an input time fraction in the range [0, 1] and applies some transformation on the value to produce an output time fraction (typically, but not necessarily, also in the range [0, 1]).
The TimedItem for which the time scaling operation is being performed.
Some timing functions, for example, may produce different results depending on the animation values involved to produce an even rate of change.
This may be null
, for example, when invoked
directly by user code for the purpose of testing or re-using
the scaling operation in another context.
Implementations of this interface for which there is no
meaningful result in the absence of a TimedItem will
simply return time
unchanged when
item
is null
.
For implementations of this interface that have local state, produces an identical but independent copy of this object. For implementations without local state, returns the same object.
Creates a new TimingFunction object based on a string-based specification (e.g. "ease-in").
The acceptable values and their meanings are those defined for the transition-timing-function property in CSS Transitions [[!CSS3-TRANSITIONS]].
In addition to the values defined in CSS Transitions, this method
extends the steps()
function notation to allow
‘middle’ as a transition point keyword (e.g.
steps(3, middle)
) corresponding to the
‘middle’ StepPosition value.
Similarly, the keyword ‘steps-middle’ is recognized by
and given the meaning steps(1, middle)
.
Strings that specify a cubic-bezier timing function result in a new SplineTimingFunction being returned. Strings that specify a stepping function produce a new StepTimingFunction.
If spec
is unrecognized, null
is
returned.
User agents that provide debugging feedback SHOULD report the
unrecognized value.
So the string cubic-bezier(...)
produces
a SplineTimingFunction.
That's a bit confusing.
But CubicBezierTimingFunction
is a lot to type and
not very user-friendly.
Should we make the ‘linear’ keyword return
null
or new SplineTimingFunction([0, 0, 1,
1])
?
A common method of producing easing effects is to use a cubic Bézier curve to scale the time. The endpoints of the curve are fixed at (0,0) and (1,1) while two control points P1 and P2 define the shape of the curve. Provided the x values of P1 and P2 lie within the range [0,1] such a curve produces a function that is used to map input times (the x values) onto output times (the y values). This arrangement is illustrated below.
A cubic Bézier curve used as a timing function.
The shape of the curve is determined by the location of the control
points P1 and P2.
Input time fractions serve as x values of the curve, whilst
the y values are the output time fractions.
The curves produced by the keywords accepted by the
Timing.createFromString
method are illustrated below.
The timing functions produced by each of the keyword values.
SplineTimingFunction
interface
Cubic bézier curve based timing functions are represented using
the SplineTimingFunction
interface defined below.
Creates a new SplineTimingFunction object and initializes
the points
member to the passed in list of
points
.
It would be more convenient for authors if the passed in list of points could be longer than four items and we simply read the first four items and ignored the rest. However, applications may begin to depend on that behavior and we could not easily allow this object to take longer lists (to represent more complex curves) in the future without adding a separate constructor for that purpose.
Exceptions:
IndexSizeError
points
is outside the range [0, 1]
or if the length of points
is not 4 items.
A sequence of four real numbers representing the coordinates of the two control points in the following sequence <p1-x> <p1-y> <p2-x> <p2-y>.
Each of the x values (i.e. p1-x and p2-x) must be in the range [0, 1].
Exceptions:
IndexSizeError
InvalidModificationError
points
.
points[0], points[1]
),
(points[2]
, points[3]
), (1, 1).
Returns the resulting y value when the x
value is max(0, min(1, time))
.
It is possible to scale a timed item's timing so that the timed item occurs in a series of discrete steps using a stepping function.
A stepping function divides the input time into a specified number of intervals that are equal in duration. The output time, starting at zero, rises by an amount equal to the interval duration once during each interval at the transition point which may be either the start, midpoint, or end of the interval.
In keeping with Web Animation's model for endpoint exclusive interval timing (see ), the output time at the transition point is the time after applying the increase (i.e. the top of the step).
Some example step timing functions are illustrated below.
Example step timing functions.
In each case the domain is the input time fraction whilst the range
represents the output time fraction produced by the step function.
The first row shows the function for each transition point when only
one step is specified whilst the second row shows the same for three
steps.
StepPosition
enumerationThe point within a step interval at which the change in value occurs is specified using one of the StepPosition enumeration values.
StepTimingFunction
interfaceStep timing functions are represented by the StepTimingFunction interface.
Creates a new StepTimingFunction with the specified number of steps and transition point.
Exceptions:
IndexSizeError
numSteps
is zero.
A number greater than or equal to one representing the number of steps in the function.
Exceptions:
IndexSizeError
The transformed time is calculated from the directed time using the following steps:
null
,
return null
.
timingFunction
is
a TimingFunction object,directed time / iteration
duration
unless iteration duration is
zero, in which case let iteration fraction be
zero.directed time *
timingFunction.scaleTime(iteration fraction))
.
The output time of a timed item is simply the transformed time.
The output time in seconds of this timed item.
As a result of the steps for calculating this value in and
elsewhere, this attribute will be
non-null
if and only if the timed item is in
in effect.
This attribute is not writeable since doing so would require
being able to do a reverse conversion from the value to set into
to local time space and then updating the currentTime
accordingly.
However, that's not possible since timing functions may be applied
and some of them are not invertible.
Animations are a kind of timed item that apply an animation effect to
a target element.
The are represented in the Web Animations API by the
Animation
objects.
Animation
interfaceCreates a new Animation object using the following procedure:
double
,iterationDuration
set to
timing.
null
or
undefined),timingFunction
is of type
DOMString
create a new TimingFunction
object by calling
Timing.createFromString
(timing
dictionary.timingFunction)
and assign the
result to
animation.timingFunction
.animation.effect
to
effect.
null
,animation.effect
to
null
.
animation.effect
to the return
value of calling AnimationEffect.createFromProperties
(effect)
.
timeline
to the DocumentTimeSource for
the active
document.Still need to work out the autoplay behaviour here.
I think we want it to work like this:
var anim = new Animation(elem, { left: '100px' }, 3); // anim is paused until you call play // anim.startTime = ??? anim.play(); // anim.startTime = anim.timeline.outputTime
Examples of the usage of this constructor are given in .
null
for animations that do not target
a specific element.
effect
property of the newly created Animation object.
If this parameter is an AnimationEffect object or CustomAnimationEffect object, it will shared with any other Animation objects referring to the same AnimationEffect or CustomAnimationEffect object. It will not be copied.
If this parameter is null
, the newly created
Animation will also have a null
animation
effect.
Otherwise, if this parameter is any other type of object, it
will be passed to AnimationEffect.createFromProperties
and the resulting AnimationEffect object used as the
animation effect.
The timing properties of the new animation.
If this parameter is a double
, then it
specifies the duration of a single iteration of the animation,
that is, the iteration duration, in seconds.
If this parameter is null
the default value for
iteration duration specified in the
TimingDictionary definition will be used, that is,
zero.
If this parameter is of type TimingDictionary, then the values of the passed-in dictionary will be used.
Finally, if this parameter is not specified, it is as if
a value of null
were specified.
The animation effect to apply (see ).
May be null
in which case the animation will produce
no noticeable effect other than dispatching events (see ).
Exceptions:
NoModificationAllowedError
template
is not null
).
The element or pseudo-element being animated by this object.
This may be null
for animations that do not target
a specific element such as an animation that produces a sound
using an audio API.
Note that in a future version AnimationTarget may be
extended to allow targetting, for example, a sequence of elements.
Therefore, code that is intended to be used with arbitrary
Animation objects should test the concrete type of
target
before using it and not assume that it refers
to an Element.
Allowing target
to refer to multiple elements may
suggest cancelling works differently so that it is possible to
cancel the animation as-applied to a single element.
Likewise, it may influence how SVG is mapped to this API if SVG is
extended to support multiple targets.
Animation
objectThe Animation constructor offers a number of approaches to creating a new Animation object. At its simplest, an Animation object that changes the ‘left’ property of elem to 100 over three seconds can be achieved as follows:
var anim = new Animation(elem, { left: '100px' }, 3);
To specify further timing properties such as the playback rate, a TimingDictionary can be used as follows:
var anim = new Animation(elem, { left: '100px' }, { duration: 3, playbackRate: 2 });
The animation effect parameter may specify multiple properties, an AnimationEffect object, or even a callback object.
// Specify multiple properties at once var animA = new Animation(elem, { left: '100px', top: '300px' }, 5); // Share the animation effect of another animation var animB = new Animation(elem, animA.effect, 3); // Supply a specialized animation effect var animC = new Animation(elem, new PathAnimationEffect( ... ), 3); // Supply a custom script-based animation effect var animC = new Animation(elem, { sample: function(time) { if (time !== null) { document.documentElement.currentScale = 1.0 + time * 2.0; } else { document.documentElement.currentScale = 1.0; } }, clone: function { return this; } }, 3);
Fill in the parameters for PathAnimationEffect above once we have decided on them.
TimingDictionary
dictionary
To simplify creation of Timing objects
a TimingDictionary
can be used.
Except where otherwise noted, the acceptable values for each member and their meanings are defined in the Timing interface.
TBD. See notes on autoplay behavior in the Animation constructor definition.
The number of seconds from a timed item's startTime
to the start of the animation interval.
See startDelay
on the
TimedItem interface.
The fill mode of the animation.
See fillMode
on the TimedItem
interface.
Note that in both CSS Animations [[CSS3-ANIMATIONS]] and SVG [[SVG112]] the default fill mode is "none". Web Animations differs in this regard since it was determined that when generating animations from script forwards filling is the more commonly-desired behavior.
Some feedback suggests the default here should be "both".
The number of iterations into the item at which to begin.
See iterationStart
on the
TimedItem interface.
The number of times to repeat the item.
See iterationCount
on the
TimedItem interface.
The duration in seconds of a single iteration, that is, the
iteration duration.
See duration
on the TimedItem
interface.
A multiplier applied to the inherited time potentially causing
the item to run at a different rate to its natural speed.
See defaultPlaybackRate
on the
TimedItem interface.
The direction in which animation proceeds, e.g. "reverse".
See direction
on the TimedItem
interface.
An optional timing function used to scale the time to produce easing effects and the like.
Unlike the TimedItem interface, the member here may be set
to either a TimingFunction object or
a DOMString
corresponding to one of the values
recognized by Timing.createFromString
.
Animations may target not only regular elements but also
pseudo-elements such as ::before
and
::first-line
[[!SELECT]].
To represent pseudo-elements, we introduce the PseudoElement interface.
::first-line
’).
Exceptions:
NoModificationAllowedError
::after
’.
Exceptions:
NoModificationAllowedError
This is an interface that may be useful in other specifications and so there are a few considerations to bear in mind:
Currently no validation is performed at all.
You can set pseudoElement
to
‘::batman
’ and we'll just ignore it
when compositing (still need to define this).
This is in order to provide better fallback. For example, if a UA doesn't support a particular pseudo-element it will still run the animation in terms of occupying time and firing events. There simply won't be any visual effect.
Also, we have a general pattern in this specification that constructors don't throw.
So while this works for this API, is this behavior useful in other specifications?
The other issue is that we could just make PseudoElement
have an optional pseudoElement
attribute and use
that everywhere instead of a union of Element or
PseudoElement (see AnimationTarget).
The reason we don't is that it complicates the 99% case where
you're not using pseudo-elements. For example, you have to type
anim.target.element
which is what so many people
don't like about SVG.
So, again, this works for this specification, but is it ok for others?
Element
interfaceTo simplify the creation of Animation objects for a given Element, the Element interface [[!DOM4]] is extended as follows:
Creates a new Animation object whose target element is the
Element object on which the method is called, and calls
play()
on the newly created animation.
The following code fragment:
var anim = elem.animate({ 'opacity': 0 }, 2);
is equivalent to:
var anim = new Animation(elem, { 'opacity': 0 }, 2); anim.play();
Returns the newly created Animation object.
It is possible to duplicate an Animation using the
clone
method.
Doing so duplicates only the scheduled state of the Animation
and not its run-time state.
cloned timing.timingFunction
to
the result of calling
source.timingFunction.clone()
or null
if
source.timingFunction
is
null
.source.effect
as follows,
source.effect
is an
Animation object,source.effect.clone()
.
source.effect
is a
CustomAnimationEffect object,source.effect
has a method called
clone
let cloned effect be the result of
calling that method, otherwise let cloned effect be
source.effect
.
null
.
Animation(source.target, cloned
effect, cloned timing)
.
This does not attach the cloned animation to
source.timeline
.
I think that's the more useful and expected behavior (especially when
dealing with children of a sequence group or repeating parallel
groups) but we may wish to add a facility in future for simplifying
this.
Before passing the output time of an Animation to the animation effect we must convert it to a time fraction. The time fraction is calculated according to the following steps:
the time fraction is as follows,
startDelay
,
iterationCount
and an
iteration duration of 1.
Since timing functions are allowed to produce output times outside the range [0,1] it is possible that the value calculated for a time fraction also lies outside this range.
While it is possible to set the timing properties of animations individually, it is often useful to bundle animations together and control their timing as a group.
This can be used to share common timing properties as illustrated below:
Using groups to share common timing properties.
(a) Shows setting a delay of 5 seconds on individual animations.
(b) Produces the same effect by setting the delay on the group.
As well as sharing timing information, by grouping animations together they can be seeked, paused, and stopped as a unit.
The timing of the children of a group is based on the timing of the group. Specifically, times for the children are based on the parent's iteration time. That is, the children animate inside an iteration of the parent.
As an example, consider repetition. If a group has an iteration count of 2, then the children of of the group will all play twice since they effectively play inside the group's iterations.
Since children of an timing group base their timing on the group's iteration time, when the group repeats, the children play again.
If an iteration count is specified for the children of a group as well as for the group the effect is as if the iteration count of the group was multiplied with the iteration count of the children.
Specifying an iteration count of 2 on an timing group and an iteration count of 3 on one of its children results in that child playing 6 times.
A further result of the children of a group basing their timing on the group's iteration time is that they cannot animate outside of the group's animation interval. This is because the iteration time of a group will not change outside its animation interval. This allows groups to clip the playback of their children.
In the first instance, an animation has a negative delay and an
infinite iteration count.
However, when a similar animation is placed inside a group with
a specified duration it has the effect of clipping the animation's
duration.
Some further consequences of group children basing their timing on their parent group's iteration time are:
Groups can be used to provide synchronization behavior for its children. For example, one type of group runs its children in parallel, whilst another type runs the children in sequence.
Compare the two arrangements illustrated below:
Two types of timing groups.
(a) is a parallel group where all the children run simultaneously.
(b) is a sequence group where the children run in turn.
Groups can also contain other groups which allows for more sophisticated synchronization.
A sequence timing group that contains a parallel timing group as
a child.
The parallel group waits for the previous child of the sequence
group to finish, and then the children of the parallel group play
simultaneously.
After they have finished the next child of the sequence group plays.
Web Animations defines two types of timing groups.
TimingGroup
interfaceRepresents a list of timed items grouped together for the purposes of synchronization.
The TimingGroup
interface supports indexed
properties with indices in the range 0 ≤ index <
group.size
.
Removes all child timed items from the group.
Returns the item at index
.
If index
is greater than or equal to
length
returns null
.
Replaces the item at index
with newItem
by calling splice(index, 1, newItem)
.
No attempt is made to check if the item at index
is
already newItem
. In such a case, newItem
will be removed from this group and re-added as per the usual
operation of slice
.
Returns newItem
.
Exceptions:
HierarchyRequestError
newItem
is the timeline for a document
(see ).
IndexSizeError
index
is outside of the range 0 ≤
index < group.length
.
Whilst splice
allows negative indices,
WebIDL requires index property setters to take an index of type
unsigned long
and hence index
is
restricted to the range 0 ≤ index <
group.length
.
Add newItem
and each otherItems
as the
last item(s) in the group by calling splice(group.length, 0,
newItem, otherItem1, ... otherItemN)
.
Returns a sequence containing the added items:
[newItem, otherItem1, ... otherItemN]
.
Exceptions:
HierarchyRequestError
newItem
objects will be added to the group.
Removes the item(s) at index
by calling
splice(index, count)
.
Returns the removed items.
Modifies the list of children of this group by first removing
deleteCount
items from start
followed by
adding newItems
at the same point.
The operation of slice is based on ECMAScript 5's Array.prototype.splice.
Returns a sequence of the items removed from group during the removal step (regardless of whether these items were re-added during the addition step).
length
,
length
].
start
.
Negative values are clamped to zero, and all other values are
clamped such that
0 < start
+ deleteCount
≤
length.
start
.
Each item, if it already has a parent group (including this
group), is first removed from its parent group before being
added to this group.
Exceptions:
HierarchyRequestError
newItem
is the
timeline for a document (see ).
splice
to take a variadic list of items
rather than requiring a sequence.
The operation is identical to splice(unsigned long start,
unsigned long deleteCount, sequence<TimedItem>
newItems)
.
item
within the group.
If item
is not in the group, returns -1
.
Parallel timing groups run their children such they potentially play
simultaneously.
The start time of each child depends on its own startTime
property.
The intrinsic duration of a parallel timing group is the maximum of default end time of each child TimedItem.
If the group has no children then the intrinsic duration is zero.
The default end time of a TimedItem is defined as follows:
default end time =
startTime + startDelay + default active duration
ParGroup
interface
Parallel timing groups are represented by ParGroup
objects.
Creates a new ParGroup object using the following procedure:
double
,iterationDuration
set to
timing.
null
or
undefined),timingFunction
is of type
DOMString
create a new TimingFunction
object by calling
Timing.createFromString
(timing
dictionary.timingFunction)
and assign the
result to
group.timingFunction
.group.splice(0, 0,
children)
.
timeline
to the DocumentTimeSource for
the active
document.A sequence of timed items to add as children of this group.
These children are appended in sequence using the same
semantics as the TimingGroup.add
method.
The timing properties of the new timing group.
If this parameter is a double
, then it
specifies the duration of a single iteration of the
animation, that is, the iteration duration, in
seconds.
If this parameter is null
the default value for
iteration duration specified in the
TimingDictionary definition will be used, that is,
zero.
If this parameter is of type TimingDictionary, then the values of the passed-in dictionary will be used.
Finally, if this parameter is not specified, it is as if
a value of null
were specified.
Creates a deep copy of this ParGroup object minus its run-time state using the following procedure.
cloned timing.timingFunction
to
the result of calling
source.timingFunction.clone()
or null
if
source.timingFunction
is
null
.source.children
, append the result
of calling child.clone()
to cloned children.
ParGroup(cloned children,
cloned timing)
.Sequence timing groups run their children in turn following their order in the group. This ordering is achieved by adjusting the start time of each child in the group.
Since the start delay is added to the start time, it can be used to adjust the timing of the animation interval relative to calculated start time as shown in the following diagram.
A negative start delay can be used to cause the animation interval of two children to overlap. Note that the start delay affects the start time of subsequent children in the group.
The start time for the children of a sequence timing group is calculated according to the following procedure:
child.startTime
be
accumulated start time.
startTime
.
When activeDuration
is positive infinity the
behavior is defined by IEEE 754-2008.
As a result, if any of the children of a sequence timing group
has an infinite default active duration, any children that
occur later in the sequence will not play.
The instrinsic duration of a sequence timing group is equivalent to the start time of a hypothetical child appended to the group's children using the procedure described in .
SeqGroup
interface
Sequence timing groups are represented by SeqGroup
objects.
Creates a deep copy of this SeqGroup object minus its run-time state using the same procedure as defined for ParGroup.clone except that a new SeqGroup object is created instead of a ParGroup.
The Web Animations animation model takes the time fractions produced by the timing model for a given Animation and applies it as the input to the animation effect defined for the Animation object. The output of each animation effect is then combined using a global animation stack before being applied to the target property (see ).
The entry-point to the animation model is the AnimationEffect or CustomAnimationEffect object associated with each Animation. These objects describe how animation values should be calculated for the Animation for any given time. AnimationEffect serves as an abstract interface of which several concrete subclasses are provided.
AnimationEffect
interfaceThe operation used to composite this animation with the stack, as specified by one of the CompositeOperation enumeration values.
This value defaults to "replace"
The operation used to composite each iteration of this animation with the result of compositing the previous animation, as specified by one of the CompositeOperation constants defined in this interface.
This value defaults to "replace"
.
Creates and returns a new object of the same type as this object's most-derived interface such that it will produce the same output as this object.
We either need a more rigorous definition here or (probably better) a sets of steps on a per-subclass basis.
Creates an AnimationEffect representing the passed-in collection of properties.
Note that this method requires handling the passed in parameter in a manner not yet supported by Web IDL and hence this method is ECMAScript-specific.
Since accessing the properties of an ECMAScript user object can have side effects, the manner in which these properties is accessed is important. In light of this consideration the following procedure has the following properties:
The interpretation of the passed-in properties
object
can be described in three parts.
Part 1 – Determine the set of animation properties
properties
. For
each property in properties
, if
property also exists in supported
properties based on a case-sensitive comparison, append
property to animation properties.
Whilst the iteration order for properties of an ECMAScript object is implementation-dependent, the order here is not significant to the outcome as animation properties will be sorted before being iterated over.
How do we handle operation
and
compositeOperation
? We'd like to be able to
list them in the same property bag but that would mean we
could never animate properties of the same name.
Part 2 – Create the AnimationEffect objects
The AnimationEffect object produced depends on the length of animation properties as follows:
null
.
This behavior of returning null
allows
alternative animation effects to be provided based on the
capabilities of the user agent as follows:
elem.animate( AnimationEffect.createFromProperties({ transform: 'translate(-100px)' }) || AnimationEffect.createFromProperties({ top: '-100px' }), 3);
properties.name
.properties.name
.Part 3 – Create each KeyframeAnimationEffect object
Based on a given name and value, a new KeyframeAnimationEffect is created as follows:
(DOMString or
sequence<(KeyframeDictionary or
DOMString)>)
,TypeError
as defined by [[!ECMA-262]].
KeyframeAnimationEffect(name,
value)
.
any sample (double? timeFraction,
double currentIteration, AnimationTarget? target, any
underlyingValue)
so that the animation effects can be driven
apart from the timing model.
Also, doing so would allow us to do real native custom animation
effects if we decide to go in that direction
(see annotation in ).
CompositeOperation
enumerationAnimationTemplate
containing this
AnimationEffect
.
KeyframeAnimationEffect
interfaceCreates a new KeyframeAnimationEffect object for the specified property from the given list of keyframes.
The list of keyframes may be a sequence of
KeyframeDictionary dictionaries, a sequence of
DOMString
s, a combination of both, or
a single DOMString
.
DOMString
s are used to create keyframes with an
offset of 1.
When the list of keyframes is a sequence consisting entirely of
DOMString
s the offsets of the newly created
Keyframes are distributed evenly from 0 to 1.
The property, operation and compositeOperation arguments are assigned to the attributes of the same names.
The frames argument is processed as follows:
The processing of frames depends on its type as follows:
DOMString
,value
member is set to
frames and whose other members are set
to their default values.
effect.frames.add(frame)
.
(DOMString
or KeyframeDictionary)
,true
.DOMString
,value
member is set to
item and whose other members are
set to their default values.
Keyframe(item)
.
false
.
effect.frames.add(frame)
.
true
call
effect.frames.distribute()
.
KeyframeList
interfaceThe KeyframeList object is a collection of Keyframe objects sorted by the offset of each Keyframe.
index
if it exists or
null
otherwise.
Adds frame to the list such that the list remains sorted by the offset of the frames.
If frame is of type KeyframeDictionary then
a Keyframe object is first constructed by calling
Keyframe(frame)
before adding the
newly constructed Keyframe to the list.
If there already exists a frame in this list with offset
frame.offset
, the newly added
frame will appear in the list after the
already existing frames in the list with the same offset.
If frame is already part of another KeyframeList it is first removed from that list before being added to this list.
Exceptions:
IndexSizeError
null
is
returned.
-1
.
Adjusts the offsets of the frames in the list such that the offsets are spaced equidistantly whilst maintaining their current order and such that the first frame (when there are multiple frames) has offset 0 and the last frame (if any) has offset 1.
For frame at position i in the list where
0 ≤ i < length
, an offset will be
assigned equal to i / (length - 1)
unless
length
is 1 in which case it will be given offset
1.
After applying the changes, this list is returned.
The following changes for making keyframes easier to work with in future have been proposed:
// Currently you have to do this effect.frames.add({ property: 'left', offset: 0.3, value: '100px' }); // It would be nice if you could also do this effect.frames.add(0.3, 'left', '100px'); // Also, fetching by offset would be good // Returns the last frame with offset 0.3 if there is one. // If there is none, does the interpolation and returns a new frame? var frame = effect.frames['0.3'];
Keyframe
interfaceA Keyframe represents a moment within an animation that has a specified value to be applied to the target property or attribute. In between such moments values may be interpolated or filled based on the TimingFunction specified on the TimedItem where the Keyframe is used, or on the previous Keyframe.
Currently a Keyframe can only target a single property which is defined on the KeyframeAnimationEffect. This is different to CSS. Is this something we want to change? It would complicate the API, of course, but is it worth it?
Creates a new Keyframe object using the parameters specified in dictionary.
dictionary.offset
is clamped to the
range [0, 1] before setting.
A value between 0 and 1 inclusive representing the offset within the iteration duration of the animation where this value should appear.
If this keyframe belongs to a KeyframeList, changes to this value cause the KeyframeList to be immediately re-sorted using a stable sort such that all children are ordered by their offset but children with identical offsets retain their relative position in the list.
Exceptions:
IndexSizeError
The timing function to apply between this keyframe and the next keyframe in any KeyframeList in which this object appears.
May be null
in which case linear interpolation will
be used.
KeyframeDictionary
dictionary
To simplify creation of Keyframe objects
a KeyframeDictionary
can be used.
The members of the dictionary correspond to attributes in the Keyframe interface which provides a more complete description of their meaning and usage.
PathAnimationEffect
interfaceGroupedAnimationEffect
interface
The GroupedAnimationEffect
interface represents a
set of animation effects that share the same
AnimationTemplate
parent.
If the group contains multiple effects that target the same property
does order in the group matter? If so, we need to add a means for
re-ordering the group other than popping and pushing. For example,
insertBefore
.
TBD whilst we decide whether we need this interface or whether we can merge it with KeyframeAnimationEffect somehow.
null
otherwise.
Appends effect
to the end of the group such that
group.indexOf(effect)
equals
group.length - 1
.
I'm assuming that AnimationEffects can be shared amongst animations and groups. Or does effect need to be removed from any previous AnimationEffects or Animations first?
index
and returns it. If
index is outside the range [0, length
), then
null
is returned.
-1
.
null
AnimationEffect still fires events.
If the current iteration is zero, or the
accumulateOperation is
"replace"
, then the
animation value is simply the iteration value, as
defined below.
When the animation time equals the iteration duration for the first time for an animation, the current animation value should be retained as the end value for the animation.
Need to revise this definition since we can't assume the time at the end of the first iteration will be visited (we might be playing backwards, or have a iteration start greater than 1).
If the current iteration is not zero and the
accumulateOperation is
"accumulate"
then the
animation value is the end value accumulated
current iteration times, with the
iteration value accumulated on top.
If the current iteration is not zero and the
accumulateOperation is
"merge"
then the
animation balue is the end value merged
with the iteration value, with an interpolation
parameter equal to the current time fraction
Need to review the following algorithms to check they still make sense now that the iteration fraction is not necessarily in the range [0, 1].
KeyframeAnimationEffect
When an AnimationTemplate
contains a pointer to a
KeyframeAnimationEffect
keyframes, the
animation value for that animation at given current time
t is calculated according to the following steps:
Keyframe
objects contained within the
KeyframeList
object stored in keyframes.frames
by their offset.
If there are no frames in the sorted frame list
then no animation occurs and the iteration value is
just the base value of the property being animated.
Keyframe
with an offset
larger than the time fraction is
encountered.
Animation
interface's timing attribute.
PathAnimationEffect
When an AnimationTemplate
contains a pointer to a
PathAnimationEffect
and rotate is set
to false, the animation value for that animation at
given current time t is the transform defined by a
translation equal to the location on the path at t.
When rotate is set to true, the animation value is the transform defined by the above translation followed by a rotation defined by the following process:
GroupedAnimationEffect
When an AnimationTemplate
contains a pointer to a
GroupedAnimationEffect
, the animation
value for that animation at given current time t
is calculated by following the procedure outlined in
,
treating the list of AnimationEffect
objects
contained within the GroupedAnimationEffect
as
the animation stack and using an initial animation value
of 0 for all simple properties and id for transform.
In some situations the animation effects provided by Web Animations
may be insufficient.
For example, the animation effects defined here are only able to
target certain CSS properties and DOM attributes.
They are unable, therefore, to modify the currentScale
property of an SVG element to smoothly zoom the viewport without
affecting the document content.
In such cases where the provided animation effects do not provide needed functionality, an animation effect defined by script may be used. Such animation effects receive a time fraction from the timing model and are responsible for producing the animation effect corresponding to the specified time.
Using an animation effect defined in script it is possible to animate not only previously un-animatable attributes and properties, but potentially anything that is accessible via script, including even producing audio or creating vibrations.
For example, using an animation effect that draws to a canvas
element it is possible to produce a complex animated effect
featuring patterns that may be difficult to create using CSS or
SVG.
Compared to using the WindowAnimationTiming
interface, this approach ensures the animation is frame-rate
independent and can be paused, reversed, eased with timing effects,
accelerated, synchronized with other animations, and be controlled
in the same manner as any other Web Animations animation without any
additional programming.
I think we want two types of custom animation effects. Following is the general-purpose animate-anything kind of effect. The other type, which is not defined here, is the one that can participate in the animation sandwich just like any other.
This, second, native-like animation effect, would have the following features:
AnimationEffect.sample
That's a bit more difficult since you have to be careful that the custom effect doesn't do anything naughty while you're in the middle of compositing the animation sandwich. I think we should postpone this until Web Animations 2.
Custom animation effects allow authors to define animation effects using script. Such animation effects are not limited to a single CSS property or DOM attribute and therefore the steps for assessing their order of execution differs from regular animation effects.
Custom animation effects are executed after all other AnimationEffect objects have completed and applied their effects to their targets.
Need to define this more precisely. Are styles flushed? Presumably they are. Can we suspend reflow for the duration of executing the script-based animation effects and just do it once afterwards?
Within the set of custom animation effects, the order of execution
is mostly the same as that for other animation effects and is
defined in .
However, custom animation effects may also override this
ordering through the priority
attribute, which, if
defined, specifies the priority of the effects with lower numbers
are executed sooner.
In deciding which of two CustomAnimationEffect objects, A and B, should be executed the following rules are applied.
priority
such that lower priorities are sorted
first.
If either does not have a defined priority
,
then treat the priority as being positive infinity for the
purposes of sorting.startTime
s of the
TimedItems with which A and B are
associated such that earlier start times are sorted first.
Items sorted earlier, are executed first.
That last point is quite wrong. I don't think we've specified exactly what script order is in . But I wonder if it makes more sense to effectively enumerate all the script-based nodes in the animation tree using some well-defined order and use that index. Can that be done efficiently?
CustomAnimationEffect
callback interfaceCustom animation effects can be defined in script using the CustomAnimationEffect interface.
Should this be a dictionary?
I'd like to make clone
optional.
null
, the callback object SHOULD
remove the animation effect.
Animation.target
Do we need to pass in the TimedItem as well? If possible I'd prefer not to but it may necessary for some types of effect. Might be an additional parameter to add later if it proves necessary?
I think we will also need to pass the previous time fraction. Animations will often use this to check if they are paused, playing in reverse etc. They can track this themselves but then you need a separate object everywhere you use it.
Write me
Write me
Write me
Write me
When multiple in effect animations target the same element, the animations are ordered into a stack which resolves how those animations combine. This stack is sorted by animation start time. Where multiple animations have the same start time, those animations are sorted in document order (for animations from the DOM) and script order (for animations from the API). Script animations are always sorted after DOM animations.
Other operations also generate animation stacks - for example,
grouping multiple animations using a
GroupedAnimationEffect
.
An animation stack may be thought of as a single stack with all animations sorted into it, or a stack per animating element, as animations on one element cannot effect the course of animations on another element.
The start time of an animation refers to the time when the animation
is specified to begin as recorded in the
TimedItem.startTime
property, that is, before applying
any start delay.
The stacking order of animations is independent of the current play direction of individual animations and animation groups.
In order to resolve an animation stack, an initial animation
value is required for each element and property animated by the
stack.
To calculate a current animation value for elements and properties
from an animation stack, an animation value is generated for each
animation in the stack
(see ).
The cumulative animation result for each element and property is first
initialised to the relevant initial animation value.
Starting at the bottom of the stack (i.e. earliest start time) and
working up, as animation results are encountered for an element
and property, these are merged into the cumulative animation result
using the animation combinator stored in the
AnimationEffect
that generated the result.
Once all animation results in the stack are processed, the resulting
cumulative animation values are the current animation values for
each property of each element that is animated.
When an animation applies to a target and property that animations earlier in the animation stack have already applied to, the cumulative animation result from the stack is composited with the new animation to produce a new cumulative animation result. The possible combinators are defined by the CompositeOperation enumeration.
When an animation a
is composited over a cumulative
animation result c
using the REPLACE combinator, the
new cumulative animation result is always a
.
When a path animation a
is composited over a
cumulative animation result c
using the ACCUMULATE
combinator, the effective transform of a
is calculated.
This transform is then post-multiplied to the cumulative animation
result to generate a new cumulative animation result.
When a transform animation a
is composited over a
cumulative animation result c
using the ACCUMULATE
combinator, a
is post-multiplied to the cumulative
animation result to generate a new cumulative animation result.
When a simple animation (i.e. an animation which is not a path
animation nor a transform animation) a
is composited
over a cumulative animation result c
using the
ACCUMULATE combinator, the new cumulative animation result is the
sum of a
and c
, clipped if necessary to
the appropriate domain.
All MERGE operators are governed by an interpolation parameter
p
that is calculated as the ratio of
(currentTime - parent.startTime)
/ parent.animationDuration
, where parent
is the
AnimationTemplate
which references the
AnimationEffect
that is being composited.
When a path animation a
is composited over a
cumulative animation result c
using the MERGE
combinator, the effective transform of a
is
calculated.
This transform is then interpolated with c
using the
rules provided in [[!CSS3-2D-TRANSFORMS]] to provide the new
cumulative animation result.
When a transform animation a
is composited over a
cumulative animation result c
using the MERGE
combinator, a
is interpolated with c
using the rules provide in [[!CSS3-2D-TRANSFORMS]] to provide the
new cumulative animation result.
When a simple animation a
is composited over a
cumulative animation result c
using the MERGE
combinator, the new cumulative animation result is calculated as
the weighted sum of a
and c
, with weights
of (1-p)
and p
respectively.
The current value of a given property and object is the value generated for that property by computing a current style for that object without taking the override stylesheet into account.
The override stylesheet contains output animation values and acts with a higher priority than all other stylesheets. However, !important rules from all other stylesheets act with a higher priority than the override stylesheet. The override stylesheet is regenerated for each timepoint in a document using the process described below.
We already have a section with the heading "animation values". Ideally they should be unique so they're easy to target by named anchor.
Animation values for all animated properties are generated at each time point according to the following process, then inserted into the override stylesheet.
Do we need to work out how to use the override stylesheet for elements that don't have an id but are targetted for animations?
MediaItem
interfaceAs timed items play they report changes to their status through TimingEvents.
Relationship to CSS and SVG events
CSS defines AnimationEvent
s and
TransitionEvent
s and SVG defines TimeEvent
s.
The proposal here is to dispatch TimingEvents in parallel to
these events.
This has the following properties:
An alternative is to make CSS and SVG events subclass TimingEvent and just fire the one event. Two issues arrive with this. Firstly, the event type would change on the subclasses so it wouldn't be possible to catch all "start" events, for example. Secondly, the propagation path for SVG events is different since they are fired at the animation template element rather than the animation target element.
Nevertheless, there may be alternative approaches here such as augmenting CSS Animation events only and firing them from the API (although that may cause compatibility issues when SVG animations start reporting events to content expecting only CSS animations).
TimingEvent
interface
The currentTime
of the DocumentTimeSource with
which the TimedItem is associated when the event was
dispatched.
I think document time will be much more useful than global time. For the rarer case that you want to synchronise animations between documents using events, I anticipate DocumentTimeSource will have a method to convert a document to global time and vice-versa and/or convert times from one DocumentTimeSource to another DocumentTimeSource.
When we come to define animations that aren't necessarily linked to a document time source we'll need to make this nullable or redefine it to mean "root time source".
The output time value of the TimeSource with which
the TimedItem is associated when the event was dispatched.
For animations that do not belong to an TimingGroup this
will be equal to documentTime
.
If we allow animations to be associated with, for example, MediaControllers, we'll need to revise the above description to cover more than just TimingGroups.
If we rename item time to local time we'll need to rename
this.
Note that the time given here is in parent iteration
time.
This is the same time space used for startTime
and
endTime
.
This is the most useful time space if, for example, you receive
a timing event and want to add a new animation that synchronises
with the item that dispatched the event by adding it to the same
time source (e.g. group).
currentIteration
on the target
TimedItem when the event was dispatched.
Suggestions for naming? CSS has taken AnimationEvent
and
TransitionEvent
and SVG has TimeEvent
.
Call it SyncEvent
?
Or we could just augment AnimationEvent
since it has
a similar propagation path.
currentIteration
changes excluding changes to and from
null
.
TimedItem.cancel
is called.
In this case, a timingend event is not fired
(see the description of
TimedItem.cancel).
The naming here is not great.
Would syncstart
be any better?
If we augment AnimationEvent
then at least we could use
animationstart
etc.
Other potential event types for a later version:
paused
is newly true.currentTime
is set on the item or an
ancestor.iterationIndex
and
adjust.
Also, if you were implementing something like SVG's syncbase
timing with script you often need to do some bookkeeping on
a seek.
document.getCurrentAnimations
?)
you could build up a timegraph.
I think this sort of event and this sort of use case would fit the MutationObserver pattern well. We could introduce a whole class of mutation records targetted at debugging (e.g. timewindowchange)
Also, although this is a bit different to a regular event (since it's qualified by a parameter), it seems like it might be nice to be able to do something like:
anim.onprogress(0.8, function() { // Issue a request to prefetch the next episode });
It would be called whenever you got a sample where
iterationTime
≥ progress was newly true.
The propagation
path for all events depends on the TimedItem where the
status change took place.
If the item is an Animation with a target
that is
not null
, the propagation path follows the usual DOM event
flow from DefaultView
down the parent chain to
target
and finally to the
TimedItem where the status change took place.
Otherwise, the propagation path is simply the TimedItem
object itself.
Note that unlike AnimationEvent
s and
TransitionEvent
s in CSS, and TimeEvent
s in
SVG which target an Element, the target of a TimingEvent is
a TimedItem.
Since Web Animations makes no guarantees about the time between successive frames of animation, it is possible that the time when a change in state that should produce an event is scheduled to occur does not line up with a frame.
As such, the events that should be dispatched when sampling the model is based on the interval between samples as follows.
The events to be dispatched are therefore those whose document time lies within progress interval.
This description fails to account for newly-added animations, removed animations, and other tree surgery. For example, if I have a sample at t=3s, I add an animation whose animation interval starts at t=2s, and then conduct another sample at t=4s, the timingstart event should fire even though the event's document time lies outside the progress interval.
We could possibly fix this by making the previous sample times and current sample times per-TimedItem but that won't work for repeating groups. Needs work.
This last part here requires calculating document times (both for
testing against the progress interval and also for filling in the
documentTime
property of the event) and that's hard to
calculate since our current model allows non-invertible timing
functions on groups.
That means you can't easily just traverse the descendants (by child
and iteration) and generate a list of interval and iteration
boundaries then filter out the ones that don't fit in the interval
because you can't easily convert from local time to document time.
You can, of course, convert from document time to local time and while that's probably enough to tell if there was an interval or iteration edge or not, it's not enough to determine the exact time at which it occurred (to fill in the event parameters).
A complex example is where you have an group whose iteration time goes from 0 to 1.5 to 0.7 back to 1. Suppose you have a child animation that finishes its animation interval at 80% of the way through the parent's iteration interval. It would then finish, do nothing, then start reversing part-way, then finish again. In that case I think you expect to get: start, finish, start, finish.
It's probably not impossible, so long as we allow for the fact that the inverse may have several (even equal) roots but it sounds tricky and possibly expensive to calculate on each iteration.
I think we might need to re-consider allowing non-invertible timing functions on groups. Alternatively we could say that the children of a group with a non-interval timing function don't generate events. Or, alternatively again, that they occur on the non-transformed times.
This final options of making event times untransformed times is not such a bad option, especially if we split time transformations into a separate step and perhaps even a separate interface. It's similar to some vector graphics editing programs that provide bitmap filter effects—often the bounding box of the filtered shape reflects the dimensions of the vector shape and not that of the bitmapped result.
Also, in the case of an animation that bounces back and forth before resting at its final value, it's probably more useful to get just one end event when it finally rests than lots of little end/start events each time it crosses the 1.0 iteration time boundary.
One implementation issue with events is that sometimes you find you suddenly have a large backlog of events to dispatch and this may have an adverse impact on performance. Some such situations include:
It seems like the Mutation Observer pattern might help here except, firstly, that might prevent simple usage such as:
elem.animate({ opacity: '0%' }, 1).onend = function() { elem.parentNode.removeChild(elem); };
Secondly, even using the observer pattern doesn't relieve the implementation of having to walk through the model and determine all the mutations that have occured and their corresponding times and this could be expensive if the computer has been asleep for a few days.
Another option might be to set some implementation requirements that allow dropping events or automatic pausing when the time between samples is protracted (such as the device going to sleep).
The previous description of event dispatch applies when the timing
model is sampled during normal playback (whether forwards or
backwards).
However, when a seek is performed by setting the
currentTime
of a TimedItem,
a different behavior is employed.
In effect, event dispatch is supressed until the seek is completed.
After the seek has completed, events are dispatched based on comparing
the before and after state of each TimedItem affected by the
seek.
Iteration events are not dispatched.
Supressing events during seeking is necessary to provide performant seeking. It is also arguably the more intuitive behavior as, for example, when rewinding a cartoon one probably does not expect a bucketload of events to arrive as a result of traversing backwards over each TimedItem.
Should iteration events be dispatched?
The procedure is as follows:
timingend
event with
localTime set to endTime
and
documentTime set to the corresponding document
time.
(Obviously this depends on being able
to convert from a local time to a document time as
discussed in the previous section.)
timingstart
event with
localTime set to startTime
+ timing.startDelay + timeDrift
and
documentTime set to the corresponding document
time.
The sequence of dispatched events is identical to that of events dispatched during regular sampling (see ).
TimingEvents are categories as synchronous events whose sequence is as follows:
documentTime
documentTime
,
sort by event type from first to last as follows:
timingcancel, timingend,
timingiteration, timingstart.
Note that sorting end events before start events is consistent with the end-point exclusive nature of timing in Web Animations (see ). When animation A ends at the same time as animation B begins, we can imagine that animation A ends an incredibly short amount of time before animation B begins such that there is no overlap.
documentTime
and type,
if the target TimedItems participate in a tree made up of
TimingGroups then the events are dispatched as if the tree
was sampled breadth-first such that events attached to ancestor
TimedItems are dispatched first.
What if the order is still unresolved, is it okay to leave it undefined? Can we use document order or script order here? It's tempting to use the same order as we use for compositing but remember that not everything that is timed is composited (e.g. groups).
Note that the EventHandler callback interface type is defined in [[!HTML5]].
It is often useful to be able to access the animations currently in play or scheduled to play, that is current animations, including those which may have been defined declaratively rather than by using the API. To that end the following extensions are defined for the Document and Element interfaces defined in [[!DOM4]].
Note that animations for which the current time falls after the active interval but which are still in effect due to a fill mode are not returned by this method. This is because in order to return such animations, user agents would be required to maintain all animations with a forwards fill indefinitely. As a result the resources consumed by an animated document would steadily accumulate over time.
Returns all Animation objects that meet both of the following conditions:
target
property that is an Element whose
ownerDocument
is the Document on which this method
was called, or has a target
property that is
a PseudoElement whose element.ownerDocument
is
the Document on which this method was called, and
The returned sequence is a snapshot (i.e. not live) representing the state of animations that corresponds at the time when the method was called.
There are at least three ways to define which elements this covers in terms of their association with the Document object on which the method is called, document:
document.timeline
—means if you
associate an Animation with a custom time source like a scrollbar
(in a future version) it wouldn't be included. Again, not sure if
this is good or bad.target
or
target.element
(for PseudoElements) has an
ownerDocument
which is
document—means only stuff which is affecting this
document's DOM in the usual way is included (but including, if
it's even possible, animations from another document).I suspect option 2 is the best approach but I've specified 3 for now simply because it was easiest.
I'll probably specify 2 once we've cleared up some of the issues surrounding time sources.
Returns all Animation objects whose target
property is the Element on which this method was called and which
are current.
Note that this does not include PseudoElements whose
element
attribute refers to this Element.
The returned sequence is a snapshot (i.e. not live) representing the state of animations that corresponds at the time when the method was called.
The Web Animations model may be modified using the API defined in this specification. This iteraction between script execution and the regular operation of the model is defined as follows:
Changes made to the timing model are reflected immediately in the values returned by timing model interfaces. (Need to be more specific here. Once the section numbers have stabilized we could put section numbers here.) Similarly, timing model methods that operate on the current state of the model such as pausing or reversing are applied to a fully-updated timing model, that is, after all previous modifications have been incorporated.
For example, if an Animation's startTime
is
changed via the API, the value returned when querying the
animation's endTime
will reflect updated state of the
timing model immediately.
// Initially startTime is 3 and endTime is 5 anim.startTime = 4; alert(anim.endTime); // Displays '6'
The same concept applies to more complex modifications of the timing model such as adding and removing children from a TimingGroup.
Changes to the timing model other than seek operations do not cause timing events to be dispatched immediately. Instead, such events are dispatched upon the next sample as defined in .
The behavior of events with relation to seek operations is defined in .
For example, if a series of modifications to the timing model in a single script block causes a TimedItem's item time to jump from being outside the active interval, to inside the interval, and then outside again no events are fired as in the following example.
// document.timeline.currentTime is 3 var anim = new Animation(null, null, 2); anim.onstart = function() { alert("started"); }; anim.play(); // startTime = 3 anim.startTime = Infinity; // No alert is shown
Modifications to any part of the API (whether the timing model or animation model) are not reflected in the target element and nor is any CustomAnimationEffect called until the next sample has been performed at some time after the current script block completes execution.
For example, if a series of modifications to the timing model in a single script block causes a TimedItem's item time to jump from being outside the active interval, to inside the interval, and then outside again no events are fired as in the following example.
// Set opacity to 0 immediately var anim = new Animation(elem, { "opacity": 0 }); anim.play(); alert(window.getComputedStyle(elem).opacity); // Displays '1'
I'm unsure if this is the desired behavior. Gecko, for example, forces a synchronous sample when a seek is performed. This certainly makes testing simpler but I'm not sure if this is a good idea or not.
Do we need to put some minimum requirement on when samples are performed? By the time the next script block executes can I assume the model is up to date?
The value returned by
DocumentTimeSource.currentTime
will not change
within a script block unless explicitly set through the API.
For example, if a series of modifications to the timing model in a single script block causes a TimedItem's item time to jump from being outside the active interval, to inside the interval, and then outside again no events are fired as in the following example.
var a = document.timeline.currentTime; // ... many lines of code ... var b = document.timeline.currentTime; alert(b - a); // Displays 0
What about when play
is called and the
DocumentTimeSource is not already playing?
We said elsewhere that currentTime
immediately
becomes zero.
If we want that to be true we should probably force a synchronous
sample at that point.
The Media Fragments specification [[!MEDIA-FRAGMENTS]] defines a means for addressing a temporal range of a media resource. The application of media fragments depends on the MIME type of the resource on which they are specified. For resources with the SVG MIME type [[!SVG11]], the application of temporal parameters is defined in the Web Animations and SVG Integration specification.
Note that media fragments are defined to operate on resources based on their MIME type. As a result, temporal addressing may not be supported in all situations where Web Animations content is used.
What should be the behavior here? Should we pause on unload/pagehide? What does video do?
TBD. If we have triggers represented in the API, need a way to make these available to accessibility agents.
TBD. Need a way to expose speed control etc.?
TBD. Describe how to integrate with the Timed Text API and give examples of how to author content so it is accessible.
If implementations are required to preserve all state associated with animations then the resources required by an animation could continue to increase without limit over time. For long-running animations, and particularly those where animations are added dynamically, this could lead to degraded performance and eventual failure.
I'm not sure how to define this. We could say all animations with
end time converted to document time <
current document time - 5min
can be discarded.
That's fine, but what about crazy documents that put 1 million animations in a 5min span? Just leave that up to the browser. Also, what about mobile clients, is 5 min too long? Is this too prescriptive?
Maybe, just make some suggestions (e.g. 1 min for mobile clients, 5 min for desktop?) and then define an exception to throw if there is a seek to some time outside that window.
Also, note that defining this in terms of past intervals is direction-specific but that's probably ok since most long-running animations will run forwards.
Thank you to Michiel “Pomax” Kamermans for help with the equations for a proposed smooth timing function although this feature has been deferred to a subsequent specification.