This is the abstract for your specification.

Introduction

Web Animations defines a model for supporting animation and synchronization on the Web platform. It is intended that other specifications will build on this model and expose its features through declarative means. In addition, this specification also defines a programming interface to the model that may be implemented by user agents that provide support for scripting.

Use cases

The Web Animations model aims at three broad areas of application:

User interface effects

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).

Storytelling and visualisation

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:

  • animated effects for moving characters along a path as well as warping a path (the rainbow trail),
  • synchronization that allows some actions to happen simultaneously (the two cats moving) and others in sequence (the change of scene),
  • play control to allow rewinding the cartoon, or changing its playback rate to accommodate particular learning or accessibility needs (particularly if audio or textual captions are included as described in the next use case category),
  • the ability to trigger alternate animations at different points in the overall story.

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 is used to present the time-based component of the data.

Synchronizing media and animated effects

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.

Relationship to other specifications

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, they are described in different terms. This specification proposes an abstract animation model that encompasses the common features of all three specifications. This model is backwards-compatible with the current behavior of these specifications such that they can be defined in terms of this model without any observable change.

Defining CSS Transitions, CSS Animations and SVG's animation features in terms of a common model opens up possibilities of synchronizing animations, interchanging their definitions, and manipulating them using a common programming interface regardless of their markup. To facilitate this process of redefining these features in terms of a common model, 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.

The animation features in SVG 1.1 are defined in terms of SMIL Animation [[SMIL-ANIMATION]]. It is intended that by defining SVG's animation features in terms of the Web Animations model, the dependency between SVG and SMIL Animation can be removed. To this end, features not present in Web Animations that are part of SVG are described in full in the SVG and Web Animations integration document.

The programming interface component of this specification makes some additions to interfaces defined in HTML5 [[HTML5]].

Overview of this specification

This specification begins by defining an abstract model for animation. This is followed by a programming interface defined in terms of the abstract model. The programming interface is defined in terms of the abstract model and is only relevant to user agents that provide scripting support.

Web Animations model overview

At a glance, the Web Animations model consists of two largely independent pieces, a timing model and an animation model. The role of these pieces is as follows:

Timing model
Takes a moment in time and converts it to a proportional distance within a single iteration of an animation called the time fraction.
Animation model
Takes the time fractions produced by the timing model and converts them into a series of values to apply to the target properties and attributes.

Graphically, this flow can be represented as follows:

Overview of the operation of the Web Animations model.
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.

The timing model at a glance

Two features characterise the Web Animations timing model: it is stateless and it is hierarchical.

Stateless

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 independent of previous inputs, the model may be described as stateless. This gives the model the following properties:

Frame-rate independent
Since the output is independent of previous inputs, the rate at which the model is sampled will not affect its progress. Provided the input times are proportional to the progress of real-world time, animations will progress at an identical rate regardless of the capabilities of the device running them.
Direction agnostic
Since the sequence of inputs is insignificant, the model is directionless. This means that the model can be sampled in reverse or even in a backwards and forwards pattern without requiring any specialized handling.
Constant-time seeking
Since each input is independent of the previous input, the processing required to perform a seek operation, even far into the future, is at least potentially constant.

There are a few 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. When no event listeners are registered, the model is stateless.

The other exception to this stateless behavior is that a number of methods defined in the programming interface to the model provide play control such as pausing and reversing an item. These methods are defined in terms of the time at which they are called and are therefore stative. These methods are provided primarily for convenience and are not part of the core timing model but, 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 programming interface, it has no influence on the subsequent samples and hence does not conflict with the stateless qualities described above.

Hierarchical

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 timing nodes. Each node in the tree derives its time from its parent node. At the root of the tree is the global clock.

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.

Timing model concepts

In Web Animations timing is based on a hierarchy of time relationships between timing nodes. Parent nodes provide timing information to their child nodes in the form of time values. A time value is a real number which nominally represents a number of seconds from some moment. The connection between time values and wall-clock seconds may be obscured by any number of transformations applied to the value as it passes through the time hierarchy.

In the future we may have timelines that are based on UI gestures in which case the connection between time values and seconds will be weakened even further.

Periodically, the user agent will trigger an update to the timing model in a process called sampling. On each sample the time values of each timing node are updated.

A more precise definition of when the model is updated when scripting is involved is provided in .

The global clock

At the root of the Web Animations timing hierarchy is the global clock.

The global clock is a source of monotonically increasing time values unaffected by adjustments to the system clock. The time values produced by the global clock represent wall-clock seconds from an unspecified historical moment. The absolute value of the time values produced by the global clock are insignificant, only their rate of change.

Note that the global clock is not exposed in the programming interface and nor is it expected to be exposed by markup. As a result the moment from which global clock time values are measured, that is, the zero time of the clock, is allowed to be implementation-dependent. One user agent may measure the number of seconds since the the user agent was loaded whilst another may use the time when the device was started. Both approaches are acceptable and produce no observable difference in the output of the model.

Timelines

A timeline provides a source of time values for the purpose of synchronization.

Typically, a timeline is tied to the global clock such that its absolute time is calculated as a fixed offset from the time of the global clock. This offset is established by designating some moment as the timeline's zero time and recording the time value of the global clock at that moment. At subsequent moments, the time value of the timeline is calculated as the difference between the current time value of the global clock and the value recorded at the zero time.

Note that we anticipate that other types of timelines may be introduced in the future that are not tied to the global clock. For example, a timeline whose time values correspond to UI gestures.

Since a timeline may be defined to relative a moment that has yet to occur, it may not always be able to return a meaningful time value. A timeline is considered to be not started when it is in such a state that it cannot produce a time value.

The document timeline

Each document has a timeline called the document timeline whose time value at a given moment is calculated as a fixed offset from the global clock such that its zero time corresponds to the moment immediately prior to dispatching the load event of the document. Prior to this moment, the document timeline is not started.

For documents that support the concept of current document readiness, this is the moment after the current document readiness has changed to "complete" but before dispatching the load event. For user agents that support Navigation Timing [[NAVIGATION-TIMING]], this occurs between the domComplete and loadEventStart timings.

Since the document timeline is tied to the global clock by a fixed offset, time values reported by the document timeline increase monotonically. Furthermore, since no scaling is applied, these time values represent wall-clock seconds.

Players

The children of a timeline are called players. A player takes a timed item which is a static description of some timed behavior and binds it to a timeline so that it runs. A player also allows run-time control of the connection between the timed item and its timeline by providing pausing, seeking, and speed control. The relationship between a player and a timed item is analogous to that of a DVD player and a DVD.

A player connects a single timed item, called its source content, to a timeline and provides playback control.

A player records the time value of its timeline at which its source content is scheduled to begin as the start time.

The current time of a player

Players also provide a time value to their source content called the player's current time. The calculation of the current time is as follows:

current time = (timeline time - start time) * playback rate - time drift

Where:

If the timeline with which is player is not started then the current time is null.

It is often useful to treat the current time as zero when it would otherwise be null. Hence we define the effective current time as being equal to current time unless it is null, in which case the effective current time be zero.

The procedure for performing manual updates to the current time is defined in .

Seeking and pausing

Seeking and pausing a player are closely related and are described here together.

Introduction to seeking

Changing the current playback position of a player can be used to rewind its source content to its start point, fast-forward to a point in the future, or to provide ad-hoc synchronization between timed items.

However, in Web Animations, the start time of a player has special significance in determining the priority of animations (see ) and so we cannot simply adjust the start time. Instead, an additional offset is introduced called the time drift that further offsets a player's current time from its timeline. The effect of the time drift when seeking is illustrated below.

At time t, a seek is performed on the player changing its current time from 1.5s to 2s.
As a result, the time drift is set to -0.5s.
Note that the start time indicated by a red star does not change.

It is possible to seek a player even if its timeline is not started. Once the timeline begins, the player will being playback from the seeked time.

Introduction to pausing

Pausing can be used to temporarily suspend a player. Like seeking, pausing effectively causes the current time of a player to be offset from its timeline by means of setting the time drift.

Pausing before a player's start time will cause the player's source content to begin in a paused state but does not delay its start as is illustrated below.

The effect of pausing a player at different moments.
In case A, the player is paused and unpaused before its start time. The pause has no effect.

In case B, the player is paused before the start time and remains paused. At the scheduled start time the source content begins playing in a paused state. When the player is unpaused it continues playing. The resulting player has a time drift equivalent to the interval from the start time to when it was unpaused.

In case C, the player is paused and unpaused during the active interval of the source content. After unpausing, the player has a time drift equivalent to the duration of the pause interval.

I suspect we don't want this behavior now that player is a separate object.

Seeking and pausing properties

Players track three properties related to seeking and pausing.

time drift
The offset from a player's scheduled current time as defined by its start time, and its actual current time after accounting for the effects of seeking and pausing. The time drift is initially zero and is updated as per the definition in .
paused state
A boolean value that is true if the player is currently paused. The paused state is initially false.
pause start time
The player's effective current time at the time when the paused state was last newly true. The pause start time is initially zero.

A number of calculations for performing seeking and pausing are defined to operate even when the associated timeline is not started. For such situations we define the effective timeline time as the current time value of the timeline associated with a player; if the timeline is not started, then the effective timeline time is zero.

Calculating the time drift

The time drift value is both a stored and a calculated value. When a player is paused, the value is calculated from the pause start time. When a player is not paused, the stored value is used. The stored value is initially zero, and is updated when the player is unpaused or seeked.

Since pausing does not take effect prior to when a player's source content begins, we first define the beginning of a player's source content for the purposes of pausing as a player's earliest moment. The calculation is as follows:

earliest moment = min(0, start delay of source content)

I'm pretty sure we don't want this behavior now that the player is separate to the source content.

Using this definition we can define the value of time drift at a given moment as follows:

If the paused state is true,
Return the result of (effective timeline time - start time) * playback rate - max(pause start time, earliest moment).
Otherwise,
Return the stored value for this property.

Updating the paused state

The procedure for updating the paused state is as follows:

  1. Let new value be the new paused state to set.
  2. If new value equals the current paused state, return.
  3. The next step depends on the current paused state as follows,
    If paused state is true,
    Set the stored value of time drift to the current calculated value of time drift as defined in .
    Otherwise,
    Record the current value of the effective current time as pause start time.
  4. Update paused state to new value.

Performing a seek

Seeking is the process of updating a player's current time to a desired value. It is achieved using the following procedure:

  1. Let seek time be the desired time value for the player's current time.
  2. The steps for adjusting the current time depend on the paused state as follows:
    If the paused state is true,
    Set pause start time to seek time.
    If the paused state is false,
    Set the stored value for the time drift to the result of evaluating (effective timeline time - start time) * playback rate - seek time.

The animation events dispatched when a seek is performed are described in .

Speed control

The rate of play of a player can be controlled by setting its playback rate. For example, setting a playback rate of 2 will cause the player's current time to increase at twice the rate of its timeline. Similarly, a playback rate of -1 will cause the player's current time to decrease at the same rate as the time values from its timeline increase.

Note that timed items also have a playback rate associated with them that behaves differently to that defined here.

Players have a playback rate that provides a scaling factor from the rate of change of the associated timeline's time values to the player's current time. The playback rate is initially 1.

Setting a player's playback rate to zero effectively pauses the player but without affecting the player's paused state.

Updating the playback rate

Changes to the playback rate also trigger a compensatory seek so that that the player's current time is unaffected by the change to the playback rate.

The procedure is as follows:

  1. Let previous time be the value of the effective current time before updating the playback rate.
  2. Update the playback rate to the new value.
  3. Seek to previous time using the procedure defined in .

Cancelling

I think we should call this ejecting. (And possibly name the API method Unicode codepoint 0x23CF, i.e. ⏏ 😉)

Cancelling a player breaks the association between the player and its source content.

Should this reset the time drift? And what about other properties? If the method is called eject, then it probably should reset everything, or at least the time drift.

Timed items

The source content of a player, if set, is a type of timed item. There are three types of timed item:

At a given moment, a timed item can be associated with at most one player.

Timed items can be chained together into a hierarchy using timings groups in which case a timed item may be indirectly associated with a player via an ancestor timing group. Only the root timed item of such a hierarchy can be associated directly with a timed item. Attempting to associate a timed item that has a parent timing group with a player results in the timed item being removed from the parent timing group.

All types of timed item define a number of common properties.

The active interval

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 beginning of the active interval are coincident.
(b) A timed item with a positive delay; the beginning of the active interval is deferred by the delay.
(c) A timed item with a negative delay; the beginning of the active interval is brought forward by the delay.

Timed items define an active interval which is the period of time during which the item is scheduled to produce its effect (with the exception of fill modes which apply outside the active interval).

The lower bound of the active interval is defined by the combination of the timed item's start time and start delay

A timed item's start time is the moment at which the parent timing group, if any, has scheduled the timed item to begin. It is expressed in inherited time. In most cases, including the case when the timed item has no parent timing group, the start time is zero. The singular exception is sequence timing groups which set the start times of their children as described in .

In addition to the start time, a timed item also has a start delay which is an offset from the start time. Unlike the start time which is determined by the parent timing group, the start delay is a property of the timed item itself.

The lower bound of the active interval of a timed item, expressed in inherited time space, is the sum of the start time and the start delay.

These definitions are incorporated in the calculation of the local time (see ) and active time.

The length of the active interval is called the active duration, the calculation of which is defined in .

Local time and inherited time

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.

Just as with coordinate spaces, time spaces can also be nested. Timing groups typically perform some transformations on the time values they receive from their parent or player before passing on the transformed time values to their children. Child timed items then operate within that transformed time space.

Children take the transformed time values from their parent—called the inherited time— and add their start time to establish their own local time space as illustrated below.

Inherited time and local time.
At time t, the inherited time is 2.5.
For timed item (a) which has a start time of 1, the local time is 1.5.
For timed item (b) which has a start time of 1 and a start delay of 1, 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.

For a timed item, the inherited time at a given moment is based on the first matching condition from the following:

If the timed item has a parent timing group,
the inherited time is the parent timing group's current transformed time value.
If the timed item is directly associated with a player,
the inherited time is the current time of the player.
Otherwise,
the inherited time is null.

The local time of a timed item is the sum of the timed item's inherited time and its start time. If the inherited time is null then the local time is also null.

Timed item states

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 describing stative parts of the model such as event dispatch.

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:

scheduled
The timed item's local time falls before the item's active interval. This state is only used in conjunction with the active state to define the current state.
active
The timed item's local time falls inside the item's active interval. Transitions to and from the active state trigger timing events as defined in .
current

The timed item's local time falls either before or inside the item's active interval, or the local time of an ancestor timing group falls before or inside its active interval thereby opening up the possibility that this timed item might play again (e.g. due to repeating).

This state is used in the programming interface to identify all animations and players that are likely to be of interest.

Furthermore, the current state provides an important definition for managing the amount of memory required by implementations. Assuming a monotonically increasing timeline, in the absence of dynamic changes to the model, an implementation can safely discard all timed items that are not current and not referenced elsewhere provided they take care to preserve any fill values since they will not have any dynamic effect.

in effect
The timed item's local time falls either inside the item's active interval or outside the interval but at a time where the item's fill mode (see ) causes it to affect its target. Timed items only produce a transformed time when the item is in effect. At all other times, the transformed time of a timed item is null.

The normative definition of each of these states follows.

A timed item is scheduled if either of the following conditions is true:

  1. the timed item's local time is not null and is less than the item's startDelay, or
  2. the timed item has a parent timing group which is a scheduled timed item.

A timed item is active if all of the following conditions are met:

  1. the timed item's local time is not null, and
  2. the timed item's local time is greater than or equal to its start delay, and
  3. the timed item's local time is less than the sum of its start delay and active duration.

A timed item is current if it is either scheduled or active or it has a parent timing group and the parent timing group is current.

A timed item is in effect if its active time as calculated according to the procedure in is not null.

Fill behavior

The effect of a timed item outside its active interval is determined by its fill mode.

The possible fill modes are:

The normative definition of these modes is incorporated in the calculation of the active time in .

Fill modes

The effect of each fill mode is as follows:

none
The timed item has no effect outside of the active interval.
forwards
For times that occur later than the active interval, the timed item will continue to produce the same transformed time value that was used at the end of the active interval. For times that occur before the active interval, the timed item will have no effect.
backwards
For times that occur before the active interval, the timed item will produce the same transformed time value that will be used at the start of the active interval. For times that occur later than the active interval, the timed item will have no effect.
both
For times that occur before the active interval, the backwards fill behavior is used. For times that occur after the active interval, the forwards fill behavior is used.

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.

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.

Repeating

Iteration intervals

It is possible to specify that a timed item should repeat a fixed number of times or 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. The initial iteration duration of a timed item 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 iteration duration of a timed item may be set by the author to represent a value other than the intrinsic iteration duration.

Comparing the iteration duration and the active duration we have:

Iteration duration
The time taken for a single iteration of the timed item to complete.
Active duration
The time taken for the entire timed item to complete, including repetitions. This may be longer or shorter than the iteration duration.

The relationship between the iteration duration and active duration is illustrated below.

A comparison of the iteration duration and active duration of 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.

Controlling iteration

The number of times a timed item repeats is called its iteration count. The iteration count is a real number greater than or equal to zero. The iteration count may also be positive infinity to represent that the timed item repeats indefinitely.

In addition to the iteration count, timed items also have an iteration start property which specifies an offset into the series of iterations at which the timed item should begin. The iteration start is a finite real number greater than or equal to zero.

The behavior of these parameters is defined in the calculations in .

The effect of the iteration count and iteration start parameters is illustrated below.

The effect of the iteration count and iteration start parameters.
In the first case the iteration count is 2.5 resulting in the third iteration being cut-off half way through its iteration interval.
The second case is the same but with an iteration start of 0.5. This causes the timed item to begin half way through the first iteration.

Unlike the iteration count parameter, the iteration start parameter does not effect the length of the active duration.

Note that values of iteration start greater than or equal to one are generally not useful unless used in combination with an animation effect that has an accumulate operation other than replace.

Need a reference here to accumlate operations once we've rewritten that part.

Iteration time space

We have already encountered different time spaces in describing local time and inherited time (see ). 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 internal to the model and not exposed in the script interface or in markup.

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 time space of the time values of the document timeline of the active document.

Interval timing

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.

Timed item speed control

Like players, timed items also have a playback rate parameter. The playback rate of a timed item is a finite real number that acts as a multiplier when calculating the timed item's transformed time from its local time.

The effect of setting the playback rate of a timed item differs from the setting the playback rate on a player. Its behavior is defined in the timing calculations given in .

In summary, the behaviour of the playback rate of a timed item is as follows:

Core timed item calculations

Overview

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 transformed time.

The first step in this process is to calculate the boundary when the timed 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 in the following sections. The first step, calculating the local time is described in . Steps 5 and 6 are described in and respectively.

Calculating the active duration

In order to calculate the active duration we first define the repeated duration as follows:

repeated duration = iteration duration * iteration count

The active duration is calculated according to the following steps:

  1. If the playback rate is zero, return Infinity.
  2. Otherwise, return repeated duration / abs(playback rate).

Transforming the local time

Calculating the active time

The active time is based on the local time and start delay. It is defined only when the timed item is in effect and is calculated according to the following steps:

  1. If local time is null, return null.
  2. If local time < start delay the result depends on the fill mode as follows,
    If the fill mode is backwards or both,
    return zero.
    Otherwise,
    return null.
  3. If local time < start time + active duration, return local time - start delay.
  4. Otherwise, the result depends on the fill mode as follows,
    If the fill mode is forwards or both,
    return the active duration.
    Otherwise,
    return null.

Calculating the scaled active time

Before the active time can be converted to an iteration time we must factor in the timed item's playback rate and iteration start. The return is called the scaled active time.

In order to calculate the scaled active time we first define the start offset as follows:

start offset = iteration start * iteration duration

The scaled active time is calculated according to the following steps:

  1. If the active time is null, return null.
  2. Return the scaled active time based on the playback rate as follows,
    If the playback rate is negative,
    Return (active time - active duration) * playback rate + start offset.
    Otherwise,
    Return active time * playback rate + start offset.

Calculating the iteration time

The iteration time is calculated according to the following steps:

  1. If the local time is null, return null.
  2. If the iteration duration is zero, return zero.
  3. If scaled active time - start offset is equal to the repeated duration, and iteration count is not zero, and (iteration count + iteration start) % 1 is zero, return the iteration duration.
  4. Otherwise, return scaled active time % iteration duration.

Calculating the current iteration

The current iteration can be calculated using the following steps:

  1. If the item time is null, return null.
  2. If the scaled active time is zero, return zero.
  3. If the iteration duration is zero, return floor(iteration start + iteration count).
  4. If the iteration time equals the iteration duration, return iteration start + iteration count - 1.
  5. Return 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.

Direction control

Timed items may also be configured to run iterations in alternative directions using direction control. For this purpose, timed items have a playback direction parameter which takes one of the following values:

The semantics of these values are incorporated into the calculation of the directed time which follows.

A non-normative definition of these values is as follows:

normal
All iterations are played as specified.
reverse
All iterations are played in the reverse direction from the way they are specified.
alternate
Even iterations are played as specified, odd iterations are played in the reverse direction from the way they are specified.
alternate-reverse
Even iterations are played in the reverse direction from the way they are specified, odd iterations are played as specified.

Calculating the directed time

The directed time is calculated from the iteration time using the following steps:

  1. Calculate the current direction using the first matching condition from the following list:
    If playback direction is normal,
    Let the current direction be forwards.
    If playback direction is reverse,
    Let the current direction be reverse.
    Otherwise,
    1. Let d be the current iteration.
    2. If playback direction is alternate-reverse increment d by 1.
    3. 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?

    4. If d % 2 == 0, let the current direction be forwards, otherwise let the current direction be reverse.
  2. If the current direction is forwards then return the iteration time.

    Otherwise, return the iteration duration - iteration time.

Time transformations

Scaling the time

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 and produce a scaled output time fraction.

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 or to a segment of a keyframe animation.

Timing functions

A timing function takes an input time fraction in the range [0, 1] and produces and output time fraction whose range is unbounded (i.e. positive and negative infinity are permitted).

Timed items may have at most one timing function associated with them.

Scaling using a cubic Bézier curve

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.

Some example cubic Bézier timing functions are illustrated below.

The timing functions produced by each of the keyword values associated with cubic Bézier timing functions accepted by the TimingFunction.createFromString method from the script interface.

A cubic Bézier timing function is a type of timing function defined by four real numbers that specify the two control points, P1 and P2, of a cubic Bézier curve whose end points are fixed at (0,0) and (1,1). The 𝓍 coordinates of P1 and P2 are restricted to the range [0,1].

The evaluation of this curve is convered in many sources such as [[FUND-COMP-GRAPHICS]].

Timing in discrete steps

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.

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.

A step timing function is a type of timing function that 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).

Calculating the transformed time

The transformed time is calculated from the directed time using the following steps:

  1. If the directed time is null, return null.
  2. Scale the directed time as follows:
    If the timed item has a timing function associated with it,
    1. Let iteration fraction be the result of evaluating directed time / iteration duration unless iteration duration is zero, in which case let iteration fraction be zero.
    2. Let scaled fraction be the result of evaluating the timed item's timing function with iteration fraction as the input time fraction.
    3. Return the result of evaluating directed time * scaled fraction.
    Otherwise,
    Return the directed time.

Grouping and synchronization

While it is possible to set the timing properties of timed items individually, it is often useful to synchronize timed items so that they share common timing properties and maintain their temporal relationship. This is achieved using a timing group.

A simple example is 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.

When a timing group is directly associated with a player, the timed items associated with the timing group can be seeked, paused, and stopped as a unit.

A timing group is a type of timed item that contains an ordered sequence of zero or more timed items known as child timed items. At a given moment, a timed item may be a child timed item of at most one timing group known as the parent timing group. Furthermore, the parent timing group cannot be the same timed item as the child timed item itself.

The temporal relationship between a child timed item and its parent timing group is incorporated in the definition of inherited time (see ).

Relationship of group time to child time

The timing of the children of a timing group is based on the timing of the group. Specifically, times for the children are based on the parent's transformed time. With regards to repetition, this means the children operate inside an iteration of the parent.

For example, if a timing 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 transformed time, when the group repeats, the children play again.

Note that even in this case, the child timed items still have only one active interval. However, as a result of the parent's timing, the active interval is played twice.

If an iteration count is specified for the children of a group as well as for the group itself, 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 timing group basing their timing on the group's transformed time is that they cannot animate outside of the group's active interval. This is because the transformed time of a group will not change outside its active interval. This allows groups to clip the playback of their children.

In the first instance, a timed item has a negative delay and an infinite iteration count.
However, when a similar timed item is placed inside a timing group with a specified iteration duration it has the effect of clipping the child timed item's active interval.

Some further consequences of timing group children basing their timing on their parent group's transformed time are:

Types of timing groups

Timing groups can be used to provide different kinds of synchronization behavior for their children. For example, one type of timing 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 timing group where all the children run simultaneously.
(b) is a sequence timing group where the children run in turn.

Timing groups can also contain other timing groups which allows for more sophisticated synchronization. An example is illustrated below.

A sequence timing group that contains a parallel timing group as a child.
The parallel timing group waits for the previous child of the sequence timing group to finish, and then the children of the parallel timing group play simultaneously. After they have finished the next child of the sequence timing group plays.

Web Animations defines two types of timing groups.

Parallel timing groups
Children of the group play simultaneously. The start time of each child coincides with the beginning of the group's iteration interval.
Sequence timing groups
Children of the group play in turn beginning with the first child and proceeding to the last. The start time of each child is set to the end of the active interval of the previous sibling.

Parallel timing groups

A parallel timing group is a type of timing group that schedules its child timed items such that they play simultaneously.

The start time of a child timed item of a parallel timing group is zero.

The intrinsic iteration duration of a parallel timing group

The intrinsic iteration duration of a parallel timing group is based on the time when the last child timed item completes its active interval and is calculated using the following procedure.

  1. Define the end time of a timed item as :

    end time = start time + start delay + active duration
  2. The intrinsic iteration duration depends on the number of child timed items as follows,

    If the group has no child timed items,
    the intrinsic iteration duration is zero.
    Otherwise,
    1. Let maximum end time be the maximum value after calculating the end time of each child timed item in the group.
    2. The intrinsic iteration duration is the result of evaluating max(0, maximum end time).

Note that for children of a parallel timing group, the start time will always be zero but it is included in the definition of end time here since the end time is also used to define the intrinsic iteration duration of a sequence timing group (see ).

Sequence timing groups

A sequence timing group is a type of timing group that schedules its child timed items such that they play in turn following their order in the group. This ordering is achieved by adjusting the start time of each child timed item in the group.

The start time of children of a sequence timing group

The start time of the child timed items of a sequence timing group is established according to the following procedure.

  1. Let the accumulated start time be zero.
  2. Iterate over each child timed item in the group beginning with the first item in the group and proceed to the last. For each child perform the following steps:

    1. Let child be the current child.
    2. Let the start time of child be accumulated start time.
    3. Let accumulated start time be the end time of child after updating its start time.

When the active duration is positive infinity the behavior for calculating the end time of an timed item and the start time of subsequent children follows the usual behavior defined by IEEE 754-2008. As a result, if any of the children of a sequence timing group has an infinite active duration, any children that occur later in the sequence will not play.

Similarly, the above procedure does not restrict start times to positive values and hence some children may not play due to a negative start delay on children that occur earlier in the group since their active interval may end before the group's start time.

Need to define if events fire in this case.

Because the start of the active interval is based on the sum of a timed item's start time and start delay, the active intervals of children of a sequence timing group need not run in strict sequence but can be shifted back and forth by using the start delay as shown in the following diagram.

A negative start delay can be used to cause the active interval of two children to overlap. Note that the start delay affects the start time of subsequent children in the group.

The intrinsic iteration duration of a sequence timing group

The intrinsic iteration duration of a sequence timing group is equivalent to the start time of a hypothetical child timed item appended to the group's children calculated according to the procedure described in unless that produces a negative value, in which case the intrinsic iteration duration is zero.

As a result, if the sequence timing group has no child timed items the intrinsic iteration duration will be zero.

Animations

Animations are a kind of timed item that apply an animation effect to a target element or pseudo-element such as ::before and ::first-line [[!SELECT]].

Calculating the time fraction

Before passing the transformed time of an animation to its animation effect we must convert it to a time fraction. The time fraction of a timed item is calculated according to the following steps:

If the iteration duration is zero,

the time fraction is as follows,

If local time < start delay,
Return the result of recalculating the transformed time using a local time of zero and an iteration duration of 1.
Otherwise,
Return the result of recalculating the transformed time using a local time of iteration count and an iteration duration of 1.
Otherwise,
Return transformed time / iteration duration.

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.

Animation values

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.

The AnimationEffect interface

attribute CompositeOperation operation

The operation used to composite this animation with the stack, as specified by one of the CompositeOperation enumeration values.

This value defaults to "replace"

attribute CompositeOperation accumulateOperation

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".

AnimationEffect clone ()

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.

static AnimationEffect? createFromProperties ()

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:

  • Every property is read only once.
  • Properties are read in a well-defined order.
  • Properties corresponding to unsupported target properties or attributes are not read.

The interpretation of the passed-in properties object can be described in three parts.

Part 1 – Determine the set of animation properties

  1. Create a list, supported properties, of property names and attribute names that can be animated by the implementation.
  2. Let animation properties be an empty sequence.
  3. Iterate through the properties of 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:

If animation properties is of zero length,
return 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);
                  
If animation properties has only one element,
  1. Let name be the value of the element in animation properties.
  2. Let value be the value of properties.name.
  3. Return a new KeyframeAnimationEffect object according to the steps in part 3 below based on name and value.
Otherwise,
  1. Let group be a newly constructed GroupedAnimationEffect.
  2. Sort animation properties lexicographically by the Unicode codepoints that define each property name.
  3. For user agents that support both a prefixed and an unprefixed version of some CSS properties, remove all prefixed properties from animation properties where the corresponding unprefixed version is also present.
  4. Iterate through animation properties. For each name in animation properties:
    1. Let value be the value of properties.name.
    2. Create a new KeyframeAnimationEffect, effect object according to the steps in part 3 below based on name and value.
    3. Append effect to group.
  5. Return group.

Part 3 – Create each KeyframeAnimationEffect object

Based on a given name and value, a new KeyframeAnimationEffect is created as follows:

If value is not of type (DOMString or sequence<(KeyframeDictionary or DOMString)>),
Throw a TypeError as defined by [[!ECMA-262]].
Otherwise,
Construct a new KeyframeAnimationEffect by calling KeyframeAnimationEffect(name, value).
object properties
An object whose object properties represent the CSS properties or element attributes to be animated. The values corresponding to these properties are the animation values to be applied as described above.
In future, we may expose 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 ).

The CompositeOperation enumeration

replace
The animation should replace the value it is composited with.
accumulate
The animation should add to the value it is composited with. The meaning of addition is dependent on the type of animation.
merge
The animation should merge with the value it is composited with. The meaning of merge is dependent on the type of animation. The duration of the merge is the calculated animation duration of the AnimationTemplate containing this AnimationEffect.

The KeyframeAnimationEffect interface

Constructor (DOMString property, (DOMString or sequence<(KeyframeDictionary or DOMString)>) frames, optional CompositeOperation operation = "replace", optional CompositeOperation compositeOperation = "replace")

Creates 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 DOMStrings, a combination of both, or a single DOMString.

DOMStrings are used to create keyframes with an offset of 1. When the list of keyframes is a sequence consisting entirely of DOMStrings 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:

  1. Let effect be the KeyframeAnimationEffect currently under construction.
  2. The processing of frames depends on its type as follows:

    If frames is a DOMString,
    1. Let frame be a new Keyframe constructed from a KeyframeDictionary whose value member is set to frames and whose other members are set to their default values.
    2. Call effect.frames.add(frame).
    If frames is a sequence of (DOMString or KeyframeDictionary),
    1. Set a flag all strings to true.
    2. For each item in frames:
      1. If item is a DOMString,
        Let frame be a new Keyframe constructed from a KeyframeDictionary whose value member is set to item and whose other members are set to their default values.
        Otherwise (item is a KeyframeDictionary),
        1. Let frame be a new Keyframe constructed by calling Keyframe(item).
        2. Set all strings to false.
      2. Call effect.frames.add(frame).
    3. If flag all strings is true call effect.frames.distribute().
attribute DOMString property
The name of the target property or attribute.
readonly attribute KeyframeList frames
The series of values that make up this effect sorted by their offset within the iteration duration of the animation.

The KeyframeList interface

The KeyframeList object is a collection of Keyframe objects sorted by the offset of each Keyframe.

readonly attribute unsigned long length
The number of frames in the list.
void clear ()
Removes all frames from this list.
getter Keyframe? (unsigned long index)
Returns the frame at index if it exists or null otherwise.
Keyframe add((Keyframe or KeyframeDictionary) frame)

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:

DOMException of type IndexSizeError
Raised if frame is a KeyframeDictionary whose offset is outside the range [0,1] or missing.
Keyframe? remove(unsigned long index)
Removes the frame at position index and returns it. If index is outside the range [0, length), then null is returned.
long indexOf(Keyframe frame)
Returns the index of frame within the list. If frame is not a member of the list, returns -1.
KeyframeList distribute()

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']; 
          

The Keyframe interface

A 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?

Constructor (KeyframeDictionary dictionary)

Creates a new Keyframe object using the parameters specified in dictionary.

dictionary.offset is clamped to the range [0, 1] before setting.

attribute DOMString value
The value to assign to the target attribute or property at the given offset.
attribute double offset

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:

DOMException of type IndexSizeError
Raised on setting a value outside the range [0,1].
attribute TimingFunction? timingFunction

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.

The 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.

DOMString value = ""
The value to assign to the target attribute or property at the given offset.
double offset = 1
A value between 0 and 1 (inclusive) representing the offset within the iteration duration of the animation where this value should appear.
TimingFunction? timingFunction = null
The timing function to apply between this keyframe and the next keyframe in any KeyframeList in which this object appears.

The PathAnimationEffect interface

attribute SVGPathSegList segments
The list of segments that make up this path.
attribute boolean rotate
True if objects animating along this path should be rotated such that their positive x axis is aligned with the direction of movement along the path.

The GroupedAnimationEffect 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.

Constructor (object properties)

TBD whilst we decide whether we need this interface or whether we can merge it with KeyframeAnimationEffect somehow.

readonly attribute unsigned long length
The number of animation effects in the group.
void clear()
Removes all effects from this group.
getter AnimationEffect? (unsigned long index)
Returns the effect at index if it exists, or null otherwise.
AnimationEffect add(AnimationEffect effect)

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?

AnimationEffect? remove(unsigned long index)
Removes the effect at index and returns it. If index is outside the range [0, length), then null is returned.
long indexOf(AnimationEffect effect)
Returns the index of effect within the group. If effect is not a member of the group, returns -1.

Calculating animation values

This section needs more detail on when sampling is done. It also needs to be specified that an Animation with a null AnimationEffect still fires events.

Calculating the animation value from an iteration value

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].

Calculating the iteration value for a 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:

  1. Convert t to the time fraction (see ).
  2. Generate a sorted frame list by sorting the list of 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.
  3. Otherwise, find the after frame by iterating through the sorted frame list until the last Keyframe with an offset larger than the time fraction is encountered.
  4. If the after frame is the first frame in the sorted frame list, then construct a before frame with an offset of 0 and a value equal to the base value of the property being animated.
  5. If the after frame does not exist (i.e. all frames have an offset less than the time fraction) then construct an after frame with an offset of 1 and a value equal to the base value of the property being animated, and Set the before frame to the last frame in the sorted frame list.
  6. Otherwise (the after frame exists and is not the first frame in the sorted frame list) set the before frame to the frame immediately prior to the after frame in the sorted frame list.
  7. The local time function is the timingFunction of the before frame, if it exists; otherwise the timingFunction of the Animation interface's timing attribute.
  8. Find the local time fraction by subtracting the offset of the before frame from the time fraction, then dividing the result by the difference between the offset of the after frame and the offset of the before frame.
  9. Calculate the effective time fraction by applying the local time function to the local time fraction.
  10. Calculate the animation value by linearly interpolating the values of the before frame and after frame, using the effective time fraction as interpolation parameter (see ).

Calculating the iteration value for a 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:

  1. Calculate the normal to the path at t (see ).
  2. Determine the normal rotation, which is given by atan2(normal.y, normal.x).
  3. The rotation is the transform that rotates by the normal rotation.

Calculating the iteration value for a 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.

Custom animation effects

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:

  • Shares the same function signature as AnimationEffect.sample
  • Gets passed the underlying value and passes out the animated value.
  • Has a target attribute and is sorted against other AnimationEffect objects (including platform objects)
  • Can be added as a child of a GroupedAnimationEffect.

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.

Execution order of custom animation effects

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.

  1. Sort A and B based on their 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.
  2. If A and B have the same priority, sort according to the startTimes of the TimedItems with which A and B are associated such that earlier start times are sorted first.
  3. If A and B have the same priority and start time, sort by the position of the corresponding Animation objects occur within the animation tree (see issue below).

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?

The CustomAnimationEffect callback interface

Custom animation effects can be defined in script using the CustomAnimationEffect interface.

Should this be a dictionary? I'd like to make clone optional.

attribute unsigned integer priority
The order in which this animation effect will be executed in relation to other custom animation effects such that items with a lower priority are executed earlier as defined in .
void sample ()
The callback used to produce the animation effect corresponding to the sample time determined by the timing model.
double? timeFraction
The distance within a single iteration of the animation effect of the current sample. When this is null, the callback object SHOULD remove the animation effect.
double currentIteration
The current iteration index beginning with zero for the first iteration.
AnimationTarget? target
The element to which the effect should be applied, if any. When this method is called as a result of this object being associated with an Animation object, it will be Animation.target
CustomAnimationEffect clone ()
Creates an independent copy of this object.

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.

Primitive animation operations

Linear interpolation

Linear interpolation of paths

Write me

Linear interpolation of transforms

Write me

Linear interpolation of primitive values

Write me

Calculating the normal to a path

Write me

Combining animations

The Global Animation Stack

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 attribute, 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.

Resolving Animation Stacks

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.

Animation combinators

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.

The REPLACE combinator

When an animation a is composited over a cumulative animation result c using the REPLACE combinator, the new cumulative animation result is always a.

The ACCUMULATE combinator

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.

The MERGE combinator

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.

Current values, animation values, and the override stylesheet

Current values

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

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.

Animation values

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.

  1. A global animation stack is generated as described in .
  2. An initial animation value is generated by taking the current style for each object (ignoring the override stylesheet) and extracting a value for each animated property.
  3. The global animation stack is resolved using the initial animation value and the process in .
  4. The current animation values generated by this process are inserted into the override stylesheet.
  5. 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?

Synchronizing with media

Currently investigating integration with HTML5's MediaController object.

The MediaItem interface

attribute HTMLMediaElement element
A pointer to the element?

Script interface

In addition to the abstract model described above, Web Animations also defines 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 Timeline interface

Timelines, including the document timeline are represented in the Web Animations API by the Timeline interface.

readonly attribute double? currentTime

Returns the time value for this timeline or null if this timeline is not started.

For a document timeline this will never be negative and represents the number of seconds since the document with which this timeline is associated was ready to fire its load event.

Player play()

Creates a new Player object associated with this timeline that is scheduled to start at currentTime.

The timeline attribute of the newly-created Player object will be set to this object.

Similarly, the startTime attribute will be set to the value of this object's currentTime attribute at the moment the method was called, or, if currentTime is null, zero.

The setting of the source attribute is described below under the description of the source parameter.

The currentTime attribute of the Player object is a calculated value described in .

The playbackRate and paused attributes take on their default values as described in the definitions of the playback rate and paused state properties of player objects.

optional TimedItem? source = null

The source content to assign to the newly-created Player object.

The source attribute is set by following the procedure defined for updating that attribute. As a result, if source is already associated with a player, it will be disassociated first before being associated with the new Player object.

double? toTimelineTime (double otherTime, Timeline other)

Returns the time value, otherTime, from another Timeline also tied to the global clock, other, converted to a time value relative to this timeline's zero time.

Returns null if:

Note that unlike currentTime, this method may return a negative time value if otherTime occurred prior to this timeline's zero time.

Furthermore, negative values for otherTime are also allowed.

If this timeline records the time value of the global clock at its zero time as global clock offset, and so does other as other global clock offset, then the result of this method is simply:

other global clock offset + otherTime - global clock offset

Exceptions:

DOMException of type InvalidNodeTypeError
Raised if other is a timeline that is not tied to the global clock.

The reason for choosing InvalidNodeTypeError here is that DOM4 describes it as meaning, "The supplied node is incorrect or has an incorrect ancestor for this operation." In this case the error is because other does not have the global clock as an ancestor so it seems appropriate.

double? toTimelineTime (Event event)

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 the time value of the global clock when the event is dispatched so that it can be converted to an accurate 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.

The Player interface

Players are represented in the Web Animations API by the Player interface.

attribute TimedItem? source

The source content associated with this player.

A player can only be associated with at most one timed item, and likewise, a timed item can only be associated—either directly or via an ancestor—with at most one player. In order to maintain these invariants, on setting this value, the following procedure is performed:

  1. Let old value be the current value of the source attribute.
  2. Let new value be the value to set.
  3. If new value is the same object as old value, return.
  4. If old value is not null, disassociate old value from this player.
  5. If new value is not null, perform the steps associated with the first matching condition of the following:
    If new value has no parent group and is associated with a player,
    disassociate new value from its player.
    If new value has a parent group,
    remove new value from its parent group by calling new value.parentGroup.remove(new value.parentGroup.indexOf(new value)).
    Otherwise,
    do nothing.
  6. Associate new value with this player.
  7. Set the source attribute to new value.
readonly attribute Timeline timeline
The timeline associated with this player.
attribute double startTime
The start time of this player.
attribute double currentTime
The effective current time of this player. Setting this attribute follows the procedure defined in .
attribute double playbackRate
The playback rate of this player. Setting this attribute follows the procedure defined in .
attribute boolean paused
The paused state of this player. Setting this attribute follows the procedure defined in .
void reverse()
Sets the playbackRate to the negative complement of its current value by following the procedure defined in .

The TimedItem interface

Timed items are represented in the Web Animations API by the TimedItem interface.

// Playback state
readonly attribute double? localTime

The local time of this timed item in seconds.

localTime will be null if this timed item is not directly or indirectly associated with a player or if it has a parent timing group that is not in effect.

readonly attribute unsigned long? currentIteration
The current iteration index beginning with zero for the first iteration as described in .
// Specified timing
readonly attribute TimingDictionary specified

The timing parameters for this TimedItem.

Note that while the TimingDictionary object itself is readonly, its members are writeable. For example:

anim.specified.startDelay = 3; // OK
anim.specified = { startDelay: 3 }; // Not OK--'specified' is read-only
              
// Calculated timing
readonly attribute double startTime

The start time of this timed item in seconds. This is the time at which the parent timing group, if any, has scheduled this child to run within its transformed time space, that is, the timed item's inherited time space.

The start of the active interval is based on the sum of the startTime and specified.startDelay.

readonly attribute unrestricted double iterationDuration

The iteration duration of this timed item.

Unlike specified.iterationDuration, this attribute never returns the string auto. Instead, such values are expanded to the current calculated value of the intrinsic iteration duration.

This attribute cannot be set. To set the timed item's iteration duration use specified.iterationDuration.

readonly attribute unrestricted double activeDuration

The calculated length, in seconds, of the active interval.

This value may be overridden by setting specified.activeDuration.

readonly attribute unrestricted double endTime

The upper bound of the active interval expressed in seconds in inherited time space.

endTime is calculated as start time + start delay + active duration

Note that while the endTime is read-only, it can be set indirectly by setting specified.activeDuration. For example, to set endTime to time t in inherited time, set specified.activeDuration to t - startTime - specified.startDelay.

// Associated player
Player? getPlayer()

The player associated with this timed item either directly or indirectly. This object can be used to perform play control such as pausing or rewinding on this timed item and all other timed items in the same hierarchy.

Since players are only associated with the root timed item in a hierarchy, this method will look up the parent timing group of this timed item, if any, and continue all the way to the root of the hierarchy and then return the player associated with the root item, if any.

This method will return null if this timed item is not associated—directly or indirectly—with a player.

In future we may introduce iterationTime which corresponds to the iteration time. 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 TimingDictionary dictionary

Timing parameters for a TimedItem are collected together under the TimingDictionary type.

double startDelay = 0

The start delay which represents the number of seconds from a timed item's startTime to the start of the active interval.

FillMode fillMode = "forwards"

The fill mode as specified by one of the FillMode enumeration values.

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".

double iterationStart = 0.0

The timed item's iteration start property.

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 iterationCount 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’.

Values less than zero are clamped to zero for the purpose of timing model calculations.

unrestricted double iterationCount = 1.0

The timed item's iteration count property.

A real number greater than or equal to zero (including positive infinity) representing the number of times to repeat the timed item.

Values less than zero are treated as the value 1.0 for the purpose of timing model calculations.

(unrestricted double or DOMString) iterationDuration = "auto"

A real number greater than or equal to zero (including positive infinity) representing the number of times to repeat the timed item, that is, the iteration duration.

The string value auto is used to indicated that the iteration duration reflects the timed item's intrinsic iteration duration.

Real numbers less than zero and strings other than the value auto (in lowercase) are treated are treated the same as auto, that is, the intrinsic iteration duration for the purpose of timing model calculations.

double playbackRate = 1.0

The timed item's playback rate property.

A multiplier applied to the local time potentially causing the item to run at a different rate to its natural speed.

PlaybackDirection direction = "normal"

The playback direction of the timed item as specified by one of the PlaybackDirection enumeration values.

(DOMString or TimingFunction)? timingFunction = null

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 TimingFunction.createFromString.

The FillMode enumeration

none
No fill.
forwards
Fill forwards.
backwards
Fill backwards.
both
Fill backwards and forwards.

The PlaybackDirection enumeration

normal
All iterations are played as specified.
reverse
All iterations are played in the reverse direction from the way they are specified.
alternate
Even iterations are played as specified, odd iterations are played in the reverse direction from the way they are specified.
alternate-reverse
Even iterations are played in the reverse direction from the way they are specified, odd iterations are played as specified.

The TimingFunction interface

The timing functions provided by Web Animations share a common TimingFunction interface as defined below.

double scaleTime()

Takes an input time fraction in the range [0, 1] and applies some transformation on the value to produce an output time fraction.

double time
The input time fraction.
TimedItem? item

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.

Exceptions:

DOMException of type IndexSizeError
Raised if time is outside the range [0, 1].

Should be just clamp x values to the range [0, 1] ?

TimingFunction clone()

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.

static TimingFunction? createFromString(DOMString spec)

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 steps() 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.
Update: I think, you won't often type out the interface name so it's ok to make it CubicBezierTimingFunction.

Should we make the ‘linear’ keyword return null or new SplineTimingFunction([0, 0, 1, 1])?
I think null except that this may mark it harder to detect error cases.

The SplineTimingFunction interface

Cubic Bézier timing functions are represented using the SplineTimingFunction interface defined below.

Constructor (sequence<double> points)

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:

DOMException of type IndexSizeError
Raised if any of the x values in points is outside the range [0, 1] or if the length of points is not 4 items.
attribute sequence<double> points

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:

DOMException of type IndexSizeError
Raised on setting if an x value is outside the range [0, 1].
DOMException of type InvalidModificationError
Raised on attempting to alter the length of points.
double scaleTime(double time, TimedItem? item)
Applies the timing function produced by the cubic Bézier curve with points (0,0), (points[0], points[1]), (points[2], points[3]), (1, 1).
TimingFunction clone()
Returns a copy of this object.

The StepTimingFunction interface

Step timing functions are represented by the StepTimingFunction interface.

Constructor (unsigned integer numSteps, optional StepPosition position = 'end')

Creates a new StepTimingFunction with the specified number of steps and transition point.

Exceptions:

DOMException of type IndexSizeError
Raised if numSteps is zero.
attribute unsigned integer numSteps

A number greater than or equal to one representing the number of steps in the function.

Exceptions:

DOMException of type IndexSizeError
Raised on setting if the number of steps is zero.
attribute StepPosition position
The point within each interval at which the change in value occurs.
double scaleTime(double time, TimedItem? item)
Returns the value of applying the step function defined by numSteps and position with input time. The behavior of the step function is described in .
TimingFunction clone()
Returns a copy of this object.

The StepPosition enumeration

The point within a step interval at which the change in value occurs is specified using one of the StepPosition enumeration values.

start
The change in value occurs at the beginning of the interval.
middle
The change in value occurs at the midpoint of the interval.
end
The change in value occurs at the end of the interval.

The TimingGroup interface

The different types of timing groups defined by Web Animations share a common TimingGroup interface as defined below.

The TimingGroup interface supports indexed properties with indices in the range 0 ≤ index < group.size.

readonly attribute unsigned long length
The number of child timed items in the group.
void clear ()

Removes all child timed items from the group.

getter TimedItem? (unsigned long index)

Returns the child timed item at index. If index is greater than or equal to length returns null.

setter TimedItem (unsigned long index, TimedItem newItem)

Replaces the child timed 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:

DOMException of type IndexSizeError
Raised if index is outside of the range 0 ≤ index < 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.
sequence<TimedItem> add (TimedItem newItem, TimedItem... otherItems)

Adds 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].

sequence<TimedItem> remove ( long index, optional unsigned long count = 1)

Removes the item(s) at index by calling splice(index, count).

Returns the removed items.

sequence<TimedItem> splice ()

Modifies the sequence of child timed items 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).

long start
The index at which items should be removed and inserted. Negative indices represent an offset from the end of the list of items. This value is clamped to the range [-length, length].
unsigned long deleteCount
The number of items to remove from the group beginning at start. Negative values are clamped to zero, and all other values are clamped such that 0 < start + deleteCount ≤ length.
sequence<TimedItem> newItems
The items to be added at 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.
sequence<TimedItem> splice (long start, unsigned long deleteCount, TimedItem... newItem)
An overload of splice that takes 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).
long indexOf (TimedItem item)
Returns the index of item within the group. If item is not in the group, returns -1.

The ParGroup interface

Parallel timing groups are represented by the ParGroup interface.

Constructor ()

Creates a new ParGroup object using the following procedure:

  1. Create a new ParGroup object, group.
  2. Establish a temporary TimingDictionary, timing dictionary, based on the type of timing as follows,
    If timing is a TimingDictionary object,
    Let timing dictionary refer to timing.
    If timing is a double,
    Let timing dictionary be a new TimingDictionary object with all properties set to their default values and iterationDuration set to timing.
    Otherwise (timing is null),
    Let timing dictionary be a new TimingDictionary object with all properties set to their default values.
  3. We want to clone timing dictionary to the specified member of group but it seems like WebIDL doesn't allow live dictionaries. Also, we need to decide if we expand timing functions defined by strings, e.g. ease-in, into real TimingFunction objects using TimingFunction.createFromString.

  4. Add children to the group by calling group.splice(0, 0, children).
sequence<TimedItem>? children

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.

optional (double or TimingDictionary)? timing = null

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.

ParGroup clone ()

Creates a deep copy of this ParGroup object using the following procedure.

  1. Let source be this ParGroup object, the object to be cloned.
  2. Create a new TimingDictionary, cloned timing with all members set to their default values (see ).
  3. Set cloned timing.timingFunction based on the type of source.specified.timingFunction as follows:

    If source.specified.timingFunction is a TimingFunction object,
    Set cloned timing.timingFunction to the result of calling source.timingFunction.clone().
    If source.specified.timingFunction is a DOMString,
    Set cloned timing.timingFunction to source.specified.timingFunction.
    Otherwise source.specified.timingFunction (null),
    Set cloned timing.timingFunction to null.
  4. Set the remainder of the members of cloned timing to the properties of the same name on source.
  5. Let cloned children be an empty sequence of TimedItem objects.
  6. For each child in source.children, append the result of calling child.clone() to cloned children.
  7. Return a new ParGroup object created by calling the ParGroup constructor with parameters ParGroup(cloned children, cloned timing).

The SeqGroup interface

Sequence timing groups are represented by the SeqGroup interface.

Constructor (sequence<TimedItem>? children, optional (double or TimingDictionary)? timing = null)
The meaning and handling of each of the parameters in this constructor is identical to the constructor for ParGroup.
SeqGroup clone ()

Creates a deep copy of this SeqGroup object using the same procedure as defined for ParGroup.clone except that a new SeqGroup object is created instead of a ParGroup.

The Animation interface

Animations are represented by the Animation interface.

Constructor ()

Creates a new Animation object using the following procedure:

  1. Create a new Animation object, animation.
  2. Establish a temporary TimingDictionary, timing dictionary, based on the type of timing as follows,
    If timing is a TimingDictionary object,
    Let timing dictionary refer to timing.
    If timing is a double,
    Let timing dictionary be a new timing dictionary object with all properties set to their default values and iterationDuration set to timing.
    Otherwise (timing is null or undefined),
    Let timing dictionary be a new timing dictionary object with all properties set to their default values.
  3. As with ParGroup we want to clone timing dictionary here but that depends on us working out what the type of specified actually is (or if it still even exists).

  4. Assign the animation effect based on the type of effect as follows,
    If effect is an AnimationEffect object or a CustomAnimationEffect object,
    Assign animation.effect to effect.
    If effect is null,
    Set animation.effect to null.
    Otherwise,
    Set animation.effect to the return value of calling AnimationEffect.createFromProperties(effect).

Examples of the usage of this constructor are given in .

AnimationTarget? element
The target element or pseudo-element. This may be null for animations that do not target a specific element.
object? effect
The animation effect used to set the effect attribute 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.

optional (double or TimingDictionary)? timing = null

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.

attribute (AnimationEffect or CustomAnimationEffect)? effect

The animation effect to apply (see ). May be null in which case the animation will produce no noticeable effect other than dispatching events (see ).

readonly attribute AnimationTarget? target

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.

If SVG is extended to allow multiple targets (using, e.g., select="rect") then it might be most natural to represent that in the API but allowing the target to refer to multiple elements. It's something that deserves attention for version 1.

Animation clone ()

Creates a copy of this Animation object using the following procedure.

  1. Let source be a the Animation object to clone, that is, this object.
  2. Create a new TimingDictionary, cloned timing with all members set to their default values (see ).
  3. Set cloned timing.timingFunction to the result of calling source.timingFunction.clone() or null if source.timingFunction is null.
  4. Need to update this if we continue allowing timingFunction to be a string.

  5. Set the remainder of the members of cloned timing to the properties of the same name on source.
  6. The AnimationEffect is cloned depending on the type of source.effect as follows,
    If source.effect is an Animation object,
    Let cloned effect be the result of calling source.effect.clone().
    If source.effect is a CustomAnimationEffect object,
    If source.effect has a method called clone let cloned effect be the result of calling that method, otherwise let cloned effect be source.effect.
    Otherwise,
    Let cloned effect be null.
  7. Return a new Animation object created by calling the Animation constructor with parameters Animation(source.target, cloned effect, cloned timing).

Creating a new Animation object

The 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);
          

The second parameter representing the animation effect 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' }, 3);

// 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.

The third parameter representing the animation's timing, may simply be a number representing the iteration duration as above, or, 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 });
          

It is also possible to omit the timing parameter altogether in which case default timing values will be used. 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' })
  ]
);
          

Having created an Animation it can be played using document.timeline.play(anim). For simple effects the Element.animate shortcut is more convenient since it performs this last step automatically.

The PseudoElement interface

Since animations may also target pseudo-elements, Web Animations API introduces the PseudoElement interface to represent such targets.

Constructor (Element element, DOMString pseudoElement)
Creates a new pseudo-element using element as the subject and pseudoElement as the pseudo-element to match (e.g. ‘::first-line’).
attribute Element element
The element used as the subject for matching a pseudo-element.

Exceptions:

DOMException of type NoModificationAllowedError
Raised on setting if this object is a readonly object.
attribute DOMString pseudoElement
The pseudo-element including the initial colon(s). For example, ‘::after’.

Exceptions:

DOMException of type NoModificationAllowedError
Raised on setting if this object is a readonly object.

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?

  • While we're at it, should we enforce using double-colons somehow?

The AnimationTarget typedef

For simplicity, throughout this specification AnimationTarget is used wherever either an Element or a PseudoElement can be used.

Extensions to the Document interface

The following extensions are made to the Document interface defined in [[!DOM4]].

readonly attribute Timeline timeline
The Timeline object representing the document timeline.

Extensions to the Element interface

To simplify the creation of Animation objects for a given Element, the Element interface [[!DOM4]] is extended as follows:

Animation animate()

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.

object? effect
The animation effect to apply. This value is passed to the Animation constructor as the effect parameter and has the same interpretation as defined for that constructor.
optional (double or TimingDictionary)? timing
The timing parameters of the animation. This value is passed to the Animation constructor as the timing parameter and has the same interpretation as defined for that constructor.

Timing events

As timed items play they report changes to their status through TimingEvents.

Relationship to CSS and SVG events

CSS defines AnimationEvents and TransitionEvents and SVG defines TimeEvents. 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).

The TimingEvent interface

Constructor (DOMString type, optional TimingEventInit eventInitDict)
Constructs a new TimingEvent object as described in Constructing events in [[!DOM4]].
attribute double documentTime

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".

attribute double localTime

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).

attribute unsigned long? iterationIndex
The value of 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.

double documentTime = 0
double localTime = 0
double iterationIndex = 0

Types of TimingEvent

timingstart
Occurs at the moment when an item enters an animation interval (from either direction).
  • Bubbles: yes
  • Cancelable: no
timingiteration
Occurs at the moment when a repeating item's currentIteration changes excluding changes to and from null.
  • Bubbles: yes
  • Cancelable: no
timingend
Occurs at the moment when an item leaves an animation interval (from either direction).
  • Bubbles: yes
  • Cancelable: no
timingcancel
Occurs when TimedItem.cancel is called. In this case, a timingend event is not fired (see the description of TimedItem.cancel).
  • Bubbles: yes
  • Cancelable: no

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:

  • pause — fires whenever paused is newly true.
    Use case: show a graphic overlay to mark a cartoon as paused.
    Likewise unpause.
  • seek — fires whenever currentTime is set on the item or an ancestor.
    Use case: Acts as a signal that intermediate events may have been dropped (see ). For example, if you have an iteration event handler that assumes it will get one call per iteration, then a seek event would be a cue that you need to check 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.
  • directionchange — fires whenever the effective direction of the item changes (due to the direction attribute, changes to playbackRate, calling reverse etc. on either the item or an ancestor)
    Use case: update UI to represent rewinding; stop audio when playing backwards to prevent secretly encoded messages being revealed.
  • newanimation — fires whenever a new Animation object is created. Use case: a timeline debugger. You want to show all animations that are playing or yet-to-play. Using this event (and 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 iterationTimeprogress was newly true.

Event dispatch

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 AnimationEvents and TransitionEvents in CSS, and TimeEvents 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.

I think we will need to make sample times per-timed item. Then, if we have a timed item that has a previous sample, but can't get the current time (because it has no timeline), then we fire a timingcancel event.

That way, you can switch players and their source content around and you'll only get a cancel event if on the next sample the timed item is not connected to a player.

The time for the cancel event would be the time of the previous sample.

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 attribute 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:

  • You have a short iteration duration, e.g. 50ms, then you get put in a background tab and throttled really low. On the next sample, you might have hundreds of iteration events to dispatch.
  • You have an arrangement of animations such that they keep spawning new ones for ever. You can easily get this in SVG using syncbase timing or in the model here by using repeating groups. In this case, you can have a lot of events to dispatch if you get throttled low. Once you have a global clock, you also encounter this situation when the computer goes to sleep since when it wakes up it will have a lot of start/end/iteration events to catch up on.

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).

Event dispatch during seeking

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:

  1. Prior to performing the seek on item, record whether it is active or not.
  2. If item is an TimingGroup record whether each descendent TimedItem is active or not.
  3. Perform the seek as described in .
  4. For each TimedItem considered in steps 2 and 3, compare whether the item was active before the seek and whether it is active now and dispatch events as follows:
    If the item was previously active but, after seeking, is no longer active,
    Dispatch a new 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.)
    If the item was previously not active but, after seeking, is now active,
    Dispatch a new 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 ).

Sequence of events

TimingEvents are categories as synchronous events whose sequence is as follows:

  1. Events are ordered by documentTime
  2. For events with the same 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.

  3. For events with the same 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).

TimedItem events interface members

// Event callbacks
attribute EventHandler onstart
The event handler for the timingstart event (see ).
attribute EventHandler oniteration
The event handler for the timingiteration event (see ).
attribute EventHandler onend
The event handler for the timingend event (see ).
attribute EventHandler oncancel
The event handler for the timingcancel event (see ).

Note that the EventHandler callback interface type is defined in [[!HTML5]].

TimedItem also implements the EventTarget interface defined in [[!DOM4]].

Querying current animations

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.

sequence<Animation> getCurrentAnimations()

Returns all Animation objects that meet both of the following conditions:

  1. has a target attribute that is an Element whose ownerDocument is the Document on which this method was called, or has a target attribute that is a PseudoElement whose element.ownerDocument is the Document on which this method was called, and
  2. is current.

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:

  1. All Animations created using document.createAnimation—means even Animations that don't target an element (e.g. they do some audio, or target a scrollbar) get included. Not sure if this is good or bad.
  2. All Animations which, if you trace their chain of time sources you eventually arrive at 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.
  3. All Animations whose 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.

sequence<Animation> getCurrentAnimations()

Returns all Animation objects whose target attribute 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.

Interaction with script

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:

Integration with Media Fragments

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.

Interaction with page display

What should be the behavior here? Should we pause on unload/pagehide? What does video do?

Making animation accessible

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.

Implementation requirements

Discarding past animations

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.

Conformance criteria

Basically, for non-scripted user agents, there are none, For scripted user agents, do the API.

Interface summary

Acknowledgements

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.