Web Animations 1.0

Unofficial Draft 08 May 2012

Editors:
Brian Birtles, Mozilla Japan, bbirtles@mozilla.com
Shane Stephens, Google, Inc, shans@google.com
Rik Cabanier, Adobe Systems, cabanier@adobe.com
Alex Danilo, Google, Inc, adanilo@google.com
Dmitry Baranovskiy, Adobe Systems, baranovs@adobe.com
Tab Atkins, Google, Inc, jackalmage@gmail.com

Abstract

This is the abstract for your specification.

Status of This Document

This document is merely a public working draft of a potential specification. It has no official standing of any kind and does not represent the support or consensus of any standards organisation.

Table of Contents

1. Introduction

This section is non-normative.

Web Animations defines features for supporting animation and synchronization on the Web platform by means of a programming interface. This interface is intended to be used both directly to easily produce animations using script, as well as a foundation for other specifications whose behavior can be defined in terms of these features.

1.1 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 similar functionality, the syntaxes are incompatible and the animations cannot be interchanged. Furthermore, the interfaces available for interacting with animations from script are largely general-purpose interfaces with few features tuned specifically to the creation and manipulation of animations.

This specification proposes an abstract animations model that encompasses the abilities of both CSS and SVG, and additionally provides a programming interface to expose these features to script.

This specification is accompanied by a CSS embedding specification, which describes how CSS features can be implemented in terms of Web Animations primitives, and an SVG embedding specification, which describes how SVG features can be implemented in terms of Web Animations primitives.

As a result, this specification does not directly alter the behavior of CSS Transitions, CSS Animations, or SVG. However, Web Animations is intended to replace the SMIL Animation [SMIL-ANIMATION] specification where it is currently used to define the behavior of SVG's animation features.

This specifications makes some additions to some interfaces defined in HTML5 [HTML5].

2. Web Animations overview

This section is non-normative.

At a glance, Web Animations can be considered in 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 Web Animations.
The current time is input to the timing model which produces a distance within an iteration.
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.

3. The animation timeline

This section is non-normative.

Each document contains a timeline to which animations may be added. Each animation has an interval during which it is scheduled to animate.

At time t animations A and B are animating. Animation C has finished animating. Animation D has yet to begin and is not animating.

3.1 Start of the timeline

Define when the timeline starts—is it document load, or can it start beforehand?
Time moments are defined in Navigation Timing spec. We should re-use one of those definitions here.

4. Animations in the timeline

Animations in the timeline are represented by AnimInstance objects.

4.1 The AnimInstance interface

interface AnimInstance : TimedItem {
             attribute AnimFunction function;
    readonly attribute Anim?        template;
    readonly attribute Element      targetElement;
    bool makeIndependent ();
};

4.1.1 Attributes

function of type AnimFunction
The animation function to apply (see section 9. Animation values).
template of type Anim, readonly, nullable
For live animation instances (see section 5. Creating animations), the Anim object from which this object was minted. For independent animation instances, this property is null.
targetElement of type Element, readonly
The element being animated by this object.

4.1.2 Methods

makeIndependent

Makes this object independent of the template from which it was created. See section 5. Creating animations.

After this method returns, the template property will be null.

Returns true if this object was previously a live animation instance, false if this object was already an independent animation instance.

No parameters.
Return type: bool

5. Creating animations

AnimInstance objects are not created directly, but via a separate Anim object. The Anim object acts a template allowing the same set of animation parameters to be used multiple times with multiple target elements.

An AnimInstance can be in one of two states with regards to the relationship between it and the Anim that produced it.

Live animation instance
Changes made to the Anim object are reflected in the AnimInstance object.
Independent animation instance
Changes made to the Anim object have no effect on the AnimInstance object.

Add a diagram which shows a live animation instance and an independent one.

Live animation instances can be converted to independent animation instances by one of two means:

  1. Modifying any of the members of the function or timing properties of the AnimInstance object. This includes redundant modifications, that is, setting a property to its current value.
  2. Calling the makeIndependent method of the AnimInstance object.

Once independent, animation instances cannot be made live again.

One implication of the fact that a live instance can be converted to an independent instance is that when a live instance is created, the object identity of its function and timing properties must differ from those of the animation from which it was created. This is because, for example, modifying the timing.duration property of a live animation instance and hence converting it to an independent animation instance, cannot at the same time change the object identity of the timing property.

// Create a new animation
var anim = new Anim();
anim.timing.duration = 1;

// Create a live instance
var instance = a.animateLive(document.getElementById('a'));
alert(instance.timing.duration); // Displays "1"

// Modify the animation
anim.timing.duration = 2;
alert(instance.timing.duration); // Displays "2"

// Modify both the animation and instance
instance.timing.duration = 3;    // instance is now independent
anim.timing.duration = 4;
alert(instance.timing.duration); // Displays "3"

5.1 The Anim interface

[Constructor]
interface Anim : TimedTemplate {
    attribute AnimFunction function;
};

5.1.1 Attributes

function of type AnimFunction
The animation function to apply.

5.2 The TimedTemplate interface

Both the timing of an Anim and the methods for creating an AnimInstance from an Anim are specified on the TimedTemplate since this behaviour is shared with animation groups (see section 8. Grouping and synchronization).

interface TimedTemplate {
    attribute Timing timing;
    TimedItem           animate (Element target, optional float startTime);
    sequence<TimedItem> animate (sequence<Node> targets, optional float startTime);
    TimedItem           animateWithParent (Element target, AnimGroupInstance? parentGroup, optional float startTime);
    sequence<TimedItem> animateWithParent (sequence<Node> targets, AnimGroupInstance? parentGroup, optional float startTime);
    TimedItem           animateLive (Element target, optional float startTime);
    sequence<TimedItem> animateLive (sequence<Node> targets, optional float startTime);
    TimedItem           animateLiveWithParent (Element target, AnimGroupInstance? parentGroup, optional float startTime);
    sequence<TimedItem> animateLiveWithParent (sequence<Node> targets, AnimGroupInstance? parentGroup, optional float startTime);
};

5.2.1 Attributes

timing of type Timing
The timing parameters to use for generated timed items.

5.2.2 Methods

animate

Creates an independent TimedItem and appends it to element.ownerDocument.animationTimeline.

This allows the following sort of usage:

anim.animate(document.getElementById("a"));

The specific steps for instantiating a TimedTemplate depends on its concrete type and is described in section 5.3 Instantiating an Anim and section 8.9 Instantiating an AnimGroup.

ParameterTypeNullableOptionalDescription
targetElement The Element to be targetted.
startTimefloat

The start time for the generated animation instances expressed in seconds in the iteration time space of the AnimGroupInstance to which it is appended (see section 6.3 Iteration time and animation time).

If this parameter is not specified it will default to the current iteration time of the AnimGroupInstance to which it is appended if it is not null, otherwise it will default to zero.

Return type: TimedItem
animate

Creates a series of independent TimedItem objects, one for each element in target. As with animate(Element target, float startTime) each such TimedItem object is appended to element.ownerDocument.animationTimeline.

This allows the following sort of usage:

anim.animate([document.getElementById("a"), document.getElementById("b")]);
anim.animate(document.querySelectorAll("div.warning"));
anim.animate(document.getElementsByTagName("button"));
anim.animate(document.getElementById("group").childNodes);

The specific steps for instantiating a TimedTemplate depends on its concrete type and is described in section 5.3 Instantiating an Anim and section 8.9 Instantiating an AnimGroup.

ParameterTypeNullableOptionalDescription
targetssequence<Node> An sequence of Nodes to be animated. Any nodes in the sequence that are not of type ELEMENT_NODE will be ignored.
startTimefloat As with animate(Element target, optional float startTime).
Return type: sequence<TimedItem>
animateWithParent

Similar to animate, this method creates independent TimedItem object(s) for the elements in target. However, the resulting items are appended to the given parentGroup, if provided. If parentGroup is null, the TimedItem objects will not be added to any group.

ParameterTypeNullableOptionalDescription
targetElement As with animate.
parentGroupAnimGroupInstance The animation group instance to which animation instances should be appended.
startTimefloat

The start time for the generated animation instances expressed in seconds in the iteration time space of the AnimGroupInstance to which it is appended (see section 6.3 Iteration time and animation time).

If this parameter is not specified it will default to the current iteration time of parentGroup. If parentGroup is null, this parameter will default to zero.

Return type: TimedItem
animateWithParent
As with animateWithParent(Element target, AnimGroupInstance? parentGroup, optional float startTime) except one TimedItem is created for each Node in target that is of type ELEMENT_NODE.
ParameterTypeNullableOptionalDescription
targetssequence<Node>
parentGroupAnimGroupInstance
startTimefloat
Return type: sequence<TimedItem>
animateLive
As with animate with the exception that the TimedItem objects generated by this method are live.
ParameterTypeNullableOptionalDescription
targetElement
startTimefloat
Return type: TimedItem
animateLive
As with animate with the exception that the TimedItem objects generated by this method are live.
ParameterTypeNullableOptionalDescription
targetssequence<Node>
startTimefloat
Return type: sequence<TimedItem>
animateLiveWithParent
As with animateWithParent with the exception that the animation instances generated by this method are live.
ParameterTypeNullableOptionalDescription
targetElement
parentGroupAnimGroupInstance
startTimefloat
Return type: TimedItem
animateLiveWithParent
As with animateWithParent with the exception that the animation instances generated by this method are live.
ParameterTypeNullableOptionalDescription
targetssequence<Node>
parentGroupAnimGroupInstance
startTimefloat
Return type: sequence<TimedItem>

5.3 Instantiating an Anim

The procedure for instantiating an Anim, template, given a list of target elements and an optional parent group, is as follows:

  1. Create an empty sequence of AnimInstance objects.
  2. For each element in the list of target elements:
    1. Create a new AnimInstance object, instance.
    2. Set the timing and function properties of instance to copies template.timing and template.function.
    3. If parentGroup is not specified, let parentGroup be element.ownerDocument.animationTimeline.
    4. Append group to parentGroup.
    5. Append group to the sequence of AnimInstance objects.
  3. Return the sequence of AnimInstance objects.

6. Timing animations

6.1 Animation states

This section is non-normative.

An individual animation instance can be in one of three possible states:

Animating (active)
The animation instance is executing, and will apply its effect to its target.
Filling (active)
The animation instance has yet to start executing or has already finished but is still effecting its target due to the fill mode specified (see section 6.2 Animation fill behavior).
Inactive
The animation instance has yet to start executing or has already finished and is not effecting its target. Animations that do not belong to a group and consequently have no notion of time are also considered inactive (see section 8. Grouping and synchronization).

An animation instance is considered active if it is in either the animating or filling state.

An animation may not enter all of these states depending on when it starts, its duration, and the fill behavior defined (see section 6.2 Animation fill behavior). An example sequence of states is illustrated below.

An example of the animation states for an animation with a forwards fill.
The animation is initially inactive. This may be due to a positive delay or positive start time (see section 6.1.1 The animation interval).
Then the animation begins executing and enters the animating state.
When the animation has completed, a forwards fill is applied and the animation enters the filling state.

6.1.1 The animation interval

The period that an animation instance is animating is called the animation interval. Each animation instance has only one such interval.

The start of the interval is determined by the start time of the animation instance but may be shifted by a start delay on the animation instance.

The end of the interval is determined by the animation duration (see section 6.7 Calculating the animation duration).

Some examples are illustrated below.

Examples of the effect of the start delay on endpoints of the animation interval.
(a) An animation instance with no delay; the start time and start of animation are coincident.
(b) An animation instance with a positive delay; the start of animation is deferred by the delay.
(c) An animation instance with a negative delay; the start of animation is brought forward by the delay.

6.2 Animation fill behavior

This section is non-normative.

Outside of the animation interval, an animation instance may still effect its target depending on its fill mode. The different modes are as follows:

none
The animation instance does not effect its target outside the active interval.
forwards
For times that occur later than the animation interval, the animation instance will continue to apply to its target the animation value that was used at the end of the animation interval. For times that occur before the animation interval, the animation instance will not effect its target.
backwards
For times that occur before the animation interval, the animation instance will apply the same animation value that will be used at the start of the animation interval. For times that occur later than the animation interval, the animation instance will not effect its target.
both
For times that occur before the animation interval, the backwards fill behavior is used. For times that occur after the animation interval, the forwards fill behavior is used.

Some examples of the these fill modes and the corresponding animation states are illustrated below.

Examples of various fill modes and the animation states produced.
(a) fill mode ‘none’. The animation instance never enters the filling state.
(b) fill mode ‘forwards’. The animation instance enters the filling state after the animation interval has complete.
(c) fill mode ‘backwards’. The animation instance enter the filling state until the start of the animation interval.
(d) fill mode ‘both’. Outside of the animation interval the animation instance is in the filling state.

The normative definition of fill behavior is incorporated in the calculation of the animation time in section 6.9 Calculating the animation time and the calculation of the iteration time in section 6.10 Calculating the iteration time.

6.3 Iteration time and animation time

This section is non-normative.

Times in Web Animations 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.

Within Web Animations, some of the common time spaces are:

Document time space
A time space whose zero time is the start time of the document timeline as defined in section 3.1 Start of the timeline.
Item time space
A time space whose zero time is the timed item's (e.g. an animation instance) start time.
Animation time space
A time space whose zero time is the beginning of a timed item's animation interval.
Iteration time space
A time space whose zero time is the beginning of the current iteration. When the animation instance is inactive it is set to the iteration time that will produce the appropriate fill value regardless of the setting of the fill mode.

In addition to these time spaces, when animation groups are used (see section 8. Grouping and synchronization) we can talk about the parent iteration time space. The zero time of parent iteration time space is the beginning of the parent animation group's current iteration. When animation groups are not used, this is equivalent to document time space.

Some of these time spaces are illustrated below.

A comparison of item time, animation 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 animation 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 animation time space, querying the animation time of an animation instance will never return a negative value due to the clamping performed when this value is calculated (see section 6.9 Calculating the animation time).

6.3.1 Iteration duration and animation duration

These time spaces can also be used to describe intervals and durations. The distinction between iteration duration and active duration is particularly common. The definition of these terms falls out of the definition of the time spaces and is summarized as follows:

Iteration duration
The time taken for a single iteration of the item to complete.
Animation duration
The time taken for the animation phase of the item to complete. This may be longer or shorter than the iteration duration.

This may be visualized as follows:

A comparison of the iteration and animation durations for an animation 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 animation duration.

6.3.2 Interval timing

For intervals of time, 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 intervals.

In the examples below, for the repeated animation, at animation time 1s, the iteration time is 0. For the sequenced animation, at parent iteration time 1s, animation A has become inactive, and animation B is now active; there is no overlap.

Illustration of end-point exclusive timing. For both repeated and sequenced animations 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 section 6.10.1 Calculating the unscaled iteration time and is demonstrated below.

After one iteration, the first frame of the animation is shown, but after two iterations (and thereonwards), the last frame is shown due to the special behavior defined when an animation fills.

6.4 Specifying timing properties

Timing properties are collected under the Timing interface. All properties have a default value associated with them with is the value used when a new Timing object is constructed.

6.4.1 The Timing interface

interface Timing {
    const unsigned short WEBA_FILL_NONE = 0;
    const unsigned short WEBA_FILL_FORWARDS = 1;
    const unsigned short WEBA_FILL_BACKWARDS = 2;
    const unsigned short WEBA_FILL_BOTH = 3;
    const unsigned short WEBA_DIRECTION_NORMAL = 0;
    const unsigned short WEBA_DIRECTION_REVERSE = 1;
    const unsigned short WEBA_DIRECTION_ALTERNATE = 2;
    const unsigned short WEBA_DIRECTION_ALTERNATE_REVERSE = 3;
    attribute float               startDelay;
    attribute unrestricted float? iterationDuration;
    attribute unrestricted float  iterationCount;
    attribute float               speed;
    attribute unsigned short      direction;
    attribute TimingFunction      timingFunction;
    attribute unsigned short      fill;
};
6.4.1.1 Attributes
startDelay of type float

The number of seconds from the timed item's startTime until the timed item should begin executing.

The default value is 0.

iterationDuration of type unrestricted float, nullable

The duration specified for a single iteration. This may be null in which case the intrinsic duration will be used. If set, it must be greater than or equal to zero (including positive infinity).

The default value is null.

iterationCount of type unrestricted float

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

The default value is 1.

speed of type float

A real number that acts as a multiplier on the item's rate of play. For example, a value of 2.0 will cause the item to run at twice its usual speed. A value of -1.0 will cause the item to play backwards.

Setting this attribute will affect the item's active duration as described in section 6.7 Calculating the animation duration.

The default value is 1.

direction of type unsigned short

Direction behavior as specified by one of the WEBA_DIRECTION_* constants defined in this interface.

The default value is WEBA_DIRECTION_NORMAL.

timingFunction of type TimingFunction
TBD.
fill of type unsigned short

Fill mode as specified by one of the WEBA_FILL_* constants defined in this interface.

The default value is WEBA_FILL_NONE.

6.4.1.2 Constants
WEBA_FILL_NONE of type unsigned short
No fill.
WEBA_FILL_FORWARDS of type unsigned short
Fill forwards (=fill="freeze" in SVG speak).
WEBA_FILL_BACKWARDS of type unsigned short
Fill backwards.
WEBA_FILL_BOTH of type unsigned short
Fill backwards and forwards.
WEBA_DIRECTION_NORMAL of type unsigned short
All iterations are played as specified.
WEBA_DIRECTION_REVERSE of type unsigned short
All iterations are played in the reverse direction from the way they were specified.
WEBA_DIRECTION_ALTERNATE of type unsigned short
Even iterations are played as specified, odd iterations are played in the reverse direction from the way they were specified.
WEBA_DIRECTION_ALTERNATE_REVERSE of type unsigned short
Even iterations are played in the reverse direction from the way they were specified, odd iterations are played as specified.

6.5 The TimedItem interface

The application of the timing properties specified in a Timing object to an actor in the animation timeline is represented by the TimedItem interface.

interface TimedItem {
    readonly attribute Timing              timing;
    readonly attribute unrestricted float  iterationDuration;
    readonly attribute float               animationDuration;
    readonly attribute unsigned long?      currentIteration;
    readonly attribute float               startTime;
    readonly attribute float               endTime;
    readonly attribute unrestricted float? iterationTime;
    readonly attribute float?              animationTime;
    readonly attribute AnimGroupInstance   parentGroup;
    readonly attribute float               startOffset;
    void setPauseState (bool pauseState);
    bool getPauseState ();
    bool isPaused ();
    void seek (unsigned long itemTime);
    void changeSpeed (float speed);
    void cancel ();
};

6.5.1 Attributes

timing of type Timing, readonly
The timing parameters for this item.
iterationDuration of type unrestricted float, readonly
The iteration duration calculated for this item. If timing.iterationDuration is set and greater than or equal to zero, this will match timing.iterationDuration. Otherwise, this will reflect the calculated intrinsic duration of the item as described in section 6.6 Calculating the iteration duration.
animationDuration of type float, readonly
The amount of time in seconds the animation is scheduled to animate as described in section 6.7 Calculating the animation duration.
currentIteration of type unsigned long, readonly, nullable
The number of iterations that have completed as described in section 6.10.3 Calculating the current iteration.
startTime of type float, readonly

The start of the animation interval before applying startDelay expressed in seconds in the iteration time space of parentGroup.

The value returned may not correspond to the value specified when creating the item (e.g. using Anim.animate). For example, when the parent animation group instance is a sequence group, the value will be determined by the procedure defined in section 8.7 Calculating the start time of children of a sequence group.

As with endTime and the proposed stop() method below, add start() to set this here? Throws exception for children of a sequence container?

endTime of type float, readonly

The time at which the animation ceases to be active expressed in seconds relative to the parent animation group.

I think we need a means to change this while leaving the animation in the tree, e.g. stop(optional timeFromNow = 0). For most interactive animations though cancel() (which removes the item from the tree) should be suitable.

iterationTime of type unrestricted float, readonly, nullable
The time in seconds representing the offset into the iteration duration using the steps described in section 6.10 Calculating the iteration time. As a result of that definition, this property will be non-null only when the item is in active state (see section 6.1 Animation states.
animationTime of type float, readonly, nullable
The time in seconds representing the offset into the animation phase. It is calculated using the steps described in section 6.9 Calculating the animation time. As a result of that definition, this property will be non-null if and only if the animation is in the active state (see section 6.1 Animation states).
parentGroup of type AnimGroupInstance, readonly
The parent animation group instance.
startOffset of type float, readonly
The number of seconds that the actual item time of this item lags behind the time given by parent iteration time - start time as a result of pausing and seeking this item.

6.5.2 Methods

setPauseState
Sets the pause state of this timed item.
ParameterTypeNullableOptionalDescription
pauseStatebool
Return type: void
getPauseState
Returns the pause state of this timed item. Note that even if this returns false, the timed item might still be effectively paused by an ancestor animation group.
No parameters.
Return type: bool
isPaused
Returns true if the pause state of this object or one of its ancestor animation groups is true.
No parameters.
Return type: bool
seek
Performs a seek operation on this timed item according to the steps described in section 6.12 Seeking a timed item. itemTime is a time in seconds in item time space (see section 6.3 Iteration time and animation time).
ParameterTypeNullableOptionalDescription
itemTimeunsigned long
Return type: void
changeSpeed
Updates the timing.speed property to match the speed parameter and performs a compensatory seek such that the animation time does not change. Need to write out the algorithm for this and clarify the meaning speed.
ParameterTypeNullableOptionalDescription
speedfloat
Return type: void
cancel
Removes the timed item from its parent group. As a result, the timed item will no longer affect its target.
No parameters.
Return type: void
How about a reverse method? What it does is call cancel on the timed item. Then it generates a clone with the startTime set to the current time (actually, we probably want to use the delay so the reverse animation has the same priority). Then it sets the direction to its opposite (i.e. normal↔reverse, alternate↔alternate-reverse) and returns the new timed item? (Need to make sure we avoid object-slicing here.)

Do we need a means for getting the startTime etc. in document time (i.e. in terms of the root time container)?

6.6 Calculating the iteration duration

The iteration duration is calculated according to the following steps:

If the duration timing property is set to a numerical value greater than or equal to zero (including positive infinity),
return duration.
Otherwise,
return the item's intrinsic duration.

The value of an item's intrinsic duration depends on the type of the item. For animation instances the intrinsic duration is Infinity.

The intrinsic duration for animation group instances and media items is described under section 8.6 Calculating the intrinsic duration of an animation group instance and section 11.1 The intrinsic duration of a media item respectively.

6.7 Calculating the animation duration

The animation duration is calculated according to the following steps:

  1. Let the repeated duration be iteration duration * iteration count.
  2. If speed is zero, return Infinity.
  3. Otherwise, return repeated duration / abs(speed).

6.8 Calculating the item time

The item time is calculated according to the following equation:

item time = parent iteration time - start time - start offset

where:

If the timed item is not the child of a parent animation group instance, the item time is null.

Need to define this for the document timeline.

6.9 Calculating the animation time

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

  1. If item time is null, return null.
  2. If item 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 item time < animation duration, return item time - start delay.
  4. Otherwise, the result depends on the fill mode as follows,
    If the fill mode is forwards or both,
    return animation duration.
    Otherwise,
    return null.

6.10 Calculating the iteration time

The iteration time is calculated by first dividing the animation time into intervals. This is called the unscaled iteration time. Following this, timing manipulations specified on the timed item are applied to the unscaled iteration time to produce the iteration time.

6.10.1 Calculating the unscaled iteration time

The unscaled iteration time is calculated according to the following steps:

  1. If the item time is null (i.e. the animation's start time is not defined), return null.
  2. If the iteration duration is zero, return zero.
  3. If the animation time equals the animation duration and the animation duration is a positive (non-zero) multiple of iteration duration, return the iteration duration.
  4. Otherwise, return animation time % iteration duration.

6.10.2 Applying time manipulations

The unscaled iteration time is converted into the iteration time using the following steps:

  1. If the unscaled iteration time is null, return null.
  2. Let scaled iteration time be the result of applying any timing function defined for this timed element to the unscaled iteration time. (Need more detail here, or a link to where this is defined.)
  3. Calculate the current direction using the first matching condition from the following list:
    If the direction is normal,
    Let the current direction be forwards.
    If the direction is reverse,
    Let the current direction be reverse.
    Otherwise,
    1. Let d be the current iteration (see section 6.10.3 Calculating the current iteration).
    2. If the direction is alternate-reverse increment d by 1.
    3. If the animation is not animating, and the animation duration is a positive multiple of the iteration duration increment d by 1.
    4. If d % 2 != 0, let the current direction be forwards, otherwise let the current direction be reverse.
  4. If the speed timing property is negative, reverse the current direction.
  5. If the current direction is forwards then return the scaled iteration time.

    Otherwise, return the iteration duration - scaled iteration time.

Applying the reverse behavior after applying the timing function means that ease-in becomes ease-out on reverse, however it avoids jumps in values when reversing part-way.

6.10.3 Calculating the current iteration

The current iteration can be calculated from the following steps:

  1. If the item time is null, return null.
  2. If the animation time is zero, return zero.
  3. If the iteration duration is zero, return floor(iteration count).
  4. Return floor(animation time / iteration duration).

    If the iteration duration is infinite, the result of floor(animation time / iteration duration) will be zero as defined by IEEE 754-2008.

6.11 Pausing a timed item

This section is non-normative.

Timed items may be paused and resumed independently of the parent animation group instance they belong to. The effect is that the time of the item drifts from that of its parent. This drift is called the start offset since it acts as an additional delay added to the start time of the item.

Timed elements that do not have a parent animation group may still be paused and resumed. However, except for the case of the document timeline, there will be no observable effect until the element is attached to a parent animation group instance.

The start offset produced by pausing can be calculated as follows:

  1. Let start offset be initially zero.
  2. When the pause state is newly true, record the parent iteration time as pause start time.
  3. On the next sample, increment start offset by parent iteration time - pause start time.
  4. Update pause start time to parent iteration time.
  5. Repeat steps 3 and 4 for each sample while paused and when the pause state is newly false.

An important characteristic of the above procedure is that the start offset continues to accumulate while the item is paused. As a result, the start offset must be calculated prior to calculating the item time.

6.12 Seeking a timed item

Seeking, like pausing, has the effect of causing a timed item's time to drift from that of its parent animation group instance.

When a seek operation is performed, the start offset is set according to the following formula:

start offset = parent iteration time - start time - seek time

where:

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

Note that seeking a timed item does not reset the state of the timed item or its children. For example, if an application is configured to produce new animations in response to animation end events, seeking the document timeline backwards will not remove the created animations. As a result, a naive application may create overlapping animations on subsequent replays. It is the application's responsibility to restore the original state of the document in such situations.

Is it worth adding "seek" events for this? Or the ability to mark animations as "remove-on-seek" (or something of that ilk).

I wonder if it makes sense to clear accumulated start offsets in some states. If you rewind a document to the start, then it's perhaps counter-intuitive if, on replay, the accumulated start offset due to pauses still takes effect. Or maybe it's ok? Bear in mind, that if we do add some feature here, it should ideally work the same when the document is played in reverse.

7. Controlling animation playback rate

TBD. Need to define timing functions and the ability to provide custom functions to scale a time. Not even sure what this section should be called or if it should be subsection of the previous section. “Scaling the time”?

The TimingFunction interface

TBD.

interface TimingFunction {
};

8. Grouping and synchronization

This section is non-normative.

While it is possible to set the timing properties of animations and their instances individually, it is often useful to bundle animations together and control their timing as a group.

This can be used to share common timing properties as illustrated below:

Using groups to share common timing properties.
(a) Shows setting a delay of 5 seconds on individual animations.
(b) Produces the same effect by setting the delay on the group.

As well as sharing timing information, by grouping animations together they can be seeked, paused, and stopped as a unit.

8.1 Relationship of group time to child time

This section is non-normative.

The timing of the children of a group is based on the timing of the group. Specifically, times for the children are based on the parent's iteration time. That is, the children animate inside an iteration of the parent.

As an example, consider repetition. If a group has an iteration count of 3, then the children of of the group will all play three times since they effectively play inside the group's iterations.

Since children of an animation group base their timing on the group's iteration time, when the group repeats, the children play again.

If an iteration count is specified for the children of a group as well as for the group the effect is as if the iteration count of the group was multiplied with the iteration count of the children.

Specifying an iteration count of 2 on an animation group and an iteration count of 3 on one of its children results in that child playing 6 times.

A further result of the children of a group basing their timing on the group's iteration time is that they cannot animate outside of the group's active interval. This is because the iteration 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, an animation has a negative delay and an infinite iteration count.
However, when a similar animation is placed inside a group with a specified duration it has the effect of clipping the animation's duration.

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

8.2 Types of groups

This section is non-normative.

Groups can be used to provide synchronization behavior for its children. For example, one type of group runs its children in parallel, whilst another type runs the children in sequence.

Compare the two arrangements illustrated below:

Two types of animation groups.
(a) is a parallel group where all the children run simultaneously.
(b) is a sequence group where the children run in turn.

Groups can also contain other groups which allows for more sophisticated synchronization.

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

Web Animations defines two types of animation groups.

Parallel groups
Children of the group play simultaneously. The start time of children is taken as relative to the start of the current iteration of the group, that is, it is in the group's iteration time space.
Sequence groups
Children of the group play in turn beginning with the first child and proceeding to the last. Any start time specified on children is ignored and replaced with the time calculated using the procedure in section 8.7 Calculating the start time of children of a sequence group.

Need to explain (with diagrams) that by using negative start delays on children of a sequence group you make the animation intervals of the children overlap.

8.3 The document timeline

We have previously referred to the document timeline as the context in which animation takes places. Likewise we have referred to the document time space as the master time space which contains other time spaces. The document timeline is, in fact, a parallel animation group instance with the following special properties:

Move/copy exception behavior to the relevant properties / methods.

8.4 Groups and liveness

As with animations, animation groups act as templates that can be instantiated (see section 5. Creating animations). The resulting groups can likewise be live or independent.

Live animation group instances can be made independent by the following means:

Note that making an animation group instance independent does NOT cause its child TimedTemplate objects to become independent.

Similarly, making a child of an animation group instance independent does NOT cause the animation group instance itself to become independent.

8.5 The AnimGroupInstance interface

Represents a list of timed items which may be animation instances or nested animation group instances. Order is significant only when type is WEBA_SYNC_SEQ.

For adding and removing items this interface relies on passing in actual TimedItem objects. This is similar to the Element interface but differs from many SVG interfaces which use numeric indices instead.

Using objects instead of indices is generally more useful since, even if you only know the index you can easily convert it to an object (e.g. remove(group.getItem(2))) but the reverse conversion is more difficult. However, there are performance costs associated with creating these objects in situations where indices are sufficient.

With the interface defined below, it's really hard to (a) remove and re-add an item in the same place, (b) move an item forward one place. DOM doesn't have this problem because it has nextSibling and co. The SVG interfaces also don't have this problem because they use indexes but they do have the problem that working with a passed in object is very difficult (because you don't know its index). Maybe we need nextSibling? Or something to take an item and find its index?

Also, the use of exceptions below is perhaps a little inconsistent. I've used exceptions for cases which are exceptional. For item I think it's reasonable to return null (like NodeList). Likewise, for remove I think it's useful to just return null rather than throwing an exception (e.g. if it has already been removed / not yet added). Maybe you really want to return null if it has no parent and throw an exception if it belongs to another group?

interface AnimGroupInstance : TimedItem {
    readonly attribute unsigned long  length;
             attribute unsigned short type;
    readonly attribute AnimGroup?     template;
    void                   clear ();
    TimedItem              append (TimedItem newItem);
    TimedItem              insertBefore (TimedItem newItem, TimedItem? refItem);
    TimedItem?             item (unsigned long index);
    TimedItem?             remove (TimedItem removedItem);
    bool                   makeIndependent ();
    sequence<AnimInstance> getActiveAnimations ();
    sequence<AnimInstance> getAnimationsForElement (Element elem);
};

8.5.1 Attributes

length of type unsigned long, readonly
The number of timed items in the group.
type of type unsigned short

The type of synchronization provided by this animation group instance as specified by one of the WEBA_SYNC_* constants defined on the AnimGroup interface.

Setting this value has the following effect:

  1. If type is set to the same value as the previous value return.
  2. Record the current container time.
  3. For each child in the group, call cancel.
  4. Seek the container time to zero.
  5. Calculate effective start time of each child in the animation group instance using the new synchronization type.
  6. Seek the container time to the previously recorded time.

Note that a consequence of the above is that, for example, if you change a container from type par to seq you could potentially generate a lot of end events followed by a lot of begin events despite the fact that the animation might not produce any different visual result. We could change the behavior so that it effectively records the play state of each item before and then after the "seek" dispatches the appropriate events.

Actually, I think we're just going to end up subclassing groups based on type. It solves lots of problems

template of type AnimGroup, readonly, nullable
For live animation group instances (see section 5. Creating animations), the AnimGroup object from which this object was minted. For independent animation group instances, this property is null.

8.5.2 Methods

clear
Removes all child timed items from the group.
No parameters.
Return type: void
append
Add newItem as the last item in the group. If newItem already belongs to a group (including this group) it will first be removed from that group before being added to this group. Returns newItem.
ParameterTypeNullableOptionalDescription
newItemTimedItem
Return type: TimedItem
insertBefore

Inserts newItem before the existing refItem. If refItem is null, newChild is added to the end of the list. If newItem already belongs to a group (including this group) it will first be removed from that group before being added to this group. If newItem and refItem are the same object then no change is made. Returns newItem.

What events are fired? If newChild is removed from a group then fire end event?

Exceptions:

  • HIERARCHY_REQUEST_ERR: Raised if newItem is the document timeline for a document (see section 8.3 The document timeline).
  • NOT_FOUND_ERR: Raised if refItem is not a child of this group.
ParameterTypeNullableOptionalDescription
newItemTimedItem
refItemTimedItem
Return type: TimedItem
item
Returns the item at index. If index is greater than or equal to length returns null. Equivalent to AnimGroupInstance[index]. (Need to work out how to define this behavior—i.e. that you can use the subscript operator)
ParameterTypeNullableOptionalDescription
indexunsigned long
Return type: TimedItem, nullable
remove
Removes removedItem from the group and returns it. If removedItem is not part of this group, returns null.
ParameterTypeNullableOptionalDescription
removedItemTimedItem
Return type: TimedItem, nullable
makeIndependent

Makes this object independent of the template from which it was created. See section 5. Creating animations.

After this method returns, the template property will be null.

Returns true if this object was previously a live animation group instance, false if this object was already an independent animation group instance.

No parameters.
Return type: bool
getActiveAnimations
Returns all descendent AnimInstance objects that are active. The returned sequence is a snapshot (i.e. not live) representing the state of animations that corresponds to the time returned by the iterationTime property of this AnimGroupInstance object when this method was called.
No parameters.
Return type: sequence<AnimInstance>
getAnimationsForElement
Returns all descendent AnimInstance objects whose targetElement is elem. As with getActiveAnimations, the returned sequence is a snapshot (i.e. not live) representing the state of animation when this method was called.
ParameterTypeNullableOptionalDescription
elemElement
Return type: sequence<AnimInstance>

8.6 Calculating the intrinsic duration of an animation group instance

TBD, but basically:

If the type is parallel,
Calc the endTime for each child and take the max
If the type is sequence
Sum all the animation durations + start delays (including negatives)

8.7 Calculating the start time of children of a sequence group

The start time for the children of a sequence animation group instance is calculated according to the following procedure:

  1. Let the accumulated start time be zero.
  2. Iterate over each child in the group beginning with the first item in the group and proceeding to the last. For each child perform the following steps:
    1. Let the start time of the child be accumulated start time
    2. Increment accumulated start time by start delay + animation duration

When animation duration is positive infinity the behavior is defined by IEEE 754-2008. In effect, if any of the children of a sequence animation group instance has an infinite animation duration, any children that occur later in the sequence will not play.

8.8 The AnimGroup interface

AnimGroupInstance objects are not created directly, but rather via a AnimGroup object that acts as a template for creating animation group instances that target different elements.

[Constructor]
interface AnimGroup : TimedTemplate {
    const unsigned short WEBA_SYNC_PAR = 0;
    const unsigned short WEBA_SYNC_SEQ = 1;
    attribute unsigned short type;
    void          clear ();
    TemplateItem  append (TemplateItem newItem);
    TemplateItem  insertBefore (TemplateItem newItem, TemplateItem? refItem);
    TemplateItem? item (unsigned long index);
    TemplateItem? remove (TemplateItem removedItem);
};

8.8.1 Attributes

type of type unsigned short

The type of synchronization provided by instances of this animation group as specified by one of the WEBA_SYNC_* constants defined in this interface.

8.8.2 Methods

clear
Removes all child templates from the group.
No parameters.
Return type: void
append
Add newItem as the last item in the group. If newItem already belongs to a group (including this group) it will first be removed from that group before being added to this group. Returns newItem. Is that necessary? For templates you could just make them share-able? But better to be consistent?
ParameterTypeNullableOptionalDescription
newItemTemplateItem
Return type: TemplateItem
insertBefore

Inserts newItem before the existing refItem. If refItem is null, newChild is added to the end of the list. If newItem already belongs to a group (including this group) it will first be removed from that group before being added to this group. If newItem and refItem are the same object then no change is made. Returns newItem.

Exceptions:

  • NOT_FOUND_ERR: Raised if refItem is not a child of this group.
ParameterTypeNullableOptionalDescription
newItemTemplateItem
refItemTemplateItem
Return type: TemplateItem
item
Returns the item at index. If index is greater than or equal to length returns null. Equivalent to AnimGroup[index]. (Need to work out how to define this behavior—i.e. that you can use the subscript operator)
ParameterTypeNullableOptionalDescription
indexunsigned long
Return type: TemplateItem, nullable
remove
Removes removedItem from the group and returns it. If removedItem is not part of this group, returns null.
ParameterTypeNullableOptionalDescription
removedItemTemplateItem
Return type: TemplateItem, nullable

8.8.3 Constants

WEBA_SYNC_PAR of type unsigned short
Parallel synchronization.
WEBA_SYNC_SEQ of type unsigned short
Sequence synchronization.

Need to think about the constructors for this. You really want to be able to just do something like var group = new par(anim1, anim2).

8.9 Instantiating an AnimGroup

The procedure for instantiating an AnimGroup, template, given a list of target elements, and optionally given a parent group, is as follows:

  1. Create an empty sequence of AnimGroupInstance objects.
  2. For each element in the list of target elements:
    1. Create a new AnimGroupInstance object, group.
    2. Set the timing property of group to a copy of template.timing.
    3. For each child in template, call child.animateWithParent(element, group, startTime)
    4. If parentGroup is not specified, let parentGroup be element.ownerDocument.animationTimeline.
    5. Append group to parentGroup.
    6. Append group to the sequence of AnimGroupInstance objects.
  3. Return the sequence of AnimGroupInstance objects.

9. Animation values

Each Anim object contains an object which subclasses AnimFunction. These objects describe how animation values should be calculated for the Anim for any given time.

This section is non-normative.

9.1 The AnimFunction interface

[Constructor]
interface AnimFunction {
    const unsigned short WEBA_OPERATION_REPLACE = 0;
    const unsigned short WEBA_OPERATION_ACCUMULATE = 1;
    const unsigned short WEBA_OPERATION_MERGE = 2;
    attribute unsigned short operation;
    attribute unsigned short accumulateOperation;
    attribute DOMString?     pace;
};

9.1.1 Attributes

operation of type unsigned short

The operation used to composite this animation with the stack, as specified by one of the WEBA_OPERATION_* constants defined in this interface.

This value defaults to WEBA_OPERATION_REPLACE

accumulateOperation of type unsigned short

The operation used to composite each iteration of this animation with the result of compositing the previous animation, as specified by one of the WEBA_OPERATION_* constants defined in this interface.

This value defaults to WEBA_OPERATION_REPLACE.

pace of type DOMString, nullable
This is just a placeholder for some kind of whole-animation-function timing control. For example, SVG has calcMode="paced" which looks at all the frames in the animation and adjusts the timing so the pace of change is constant (this is especially useful for motion on a path).
The example here could take values null (default), "auto", or something like "1px/s" (not sure what the datatype should be here).

9.1.2 Constants

WEBA_OPERATION_REPLACE of type unsigned short
This animation should replace the value it is composited with.
WEBA_OPERATION_ACCUMULATE of type unsigned short
This animation should add to the value it is composited with. The meaning of addition is dependent on the type of animation.
WEBA_OPERATION_MERGE of type unsigned short
This 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 Anim containing this AnimFunction.

9.2 The KeyframesAnimFunction interface

It seems like it might even be useful to allow script to provide its own function. I'm not sure how that works exactly, but if we were to support that, then the interface would be something like:

  • Sample(float iterationProgress, Element target, any underlyingValue) (where iterationProgress is the ratio of iteration time to iteration duration)

Implementations of the function would be expected to produce the same result given the same parameters so the implementation could cache the result and not call the function when the parameters were the same.

That would potentially let you animate anything. Anything! (And, that could be bad. The callback could rewrite the entire document every sample. After returning from the callback you'd have to do a lot of checks regarding the state of the world before continuing.)

I think most of the members of the interface, perhaps all, should be readonly when this object is returned from an AnimInstance object. Objects returned from an Anim, however, are live.

[Constructor]
interface KeyframesAnimFunction : AnimFunction {
    attribute DOMString     property;
    attribute AnimFrameList frames;
};

9.2.1 Attributes

property of type DOMString
The target attribute / property. property is probably a reserved word we should avoid though.
frames of type AnimFrameList
The series of values that make up this function sorted by their offset within the iteration duration of the animation.

9.3 The AnimFrame interface

[Constructor]
interface AnimFrame {
    attribute any            value;
    attribute float          offset;
    attribute TimingFunction timingFunction;
};

9.3.1 Attributes

value of type any
The attribute / property value for the given frame
offset of type float
A value between 0 and 1 (inclusive) representing the offset within the iteration duration of the animation where this value should appear. Ignored if we're using paced timing?
What happens if you change it? The array returned by the AnimFunction is updated? I guess that's fine if it's just an array. Any already-returned arrays wouldn't need to be updated.
What happens if you have two values with the same offset? It would be nice if the result was deterministic rather than just left up in the air (since the more you leave undefined, the more you let the Web define it for you).
timingFunction of type TimingFunction
The function to use for this segment. Would a string do here? E.g. "ease" or "cubic-bezier(1 0 0.3 1)"? Probably not. It's a little too CSS-specific for a start, and we want to allow this to potentially be a function pointer.
Ignored if we're using paced timing

9.4 The AnimFrameList interface

interface AnimFrameList {
    readonly attribute unsigned long length;
    AnimFrameList? item (unsigned long index);
    AnimFrameList? add (AnimFrame frame);
    AnimFrameList? remove (unsigned long index);
};

9.4.1 Attributes

length of type unsigned long, readonly
The number of frames in the list.

9.4.2 Methods

item
Returns the frame at index if it exists or null.
ParameterTypeNullableOptionalDescription
indexunsigned long
Return type: AnimFrameList, nullable
add
Adds frame to the list such that the list is sorted by the offset of the frames. If there's already a frame with the same offset? This one comes after?
ParameterTypeNullableOptionalDescription
frameAnimFrame
Return type: AnimFrameList, nullable
remove
Adds frame to the list such that the list is sorted by the offset of the frames. If there's already a frame with the same offset? This one comes after?
ParameterTypeNullableOptionalDescription
indexunsigned long
Return type: AnimFrameList, nullable

Not sure about the remove method. Do we want:

9.5 The PathAnimFunction interface

[Constructor]
interface PathAnimFunction : AnimFunction {
    attribute SVGPathSegList segments;
    attribute boolean        rotate;
};

9.5.1 Attributes

segments of type SVGPathSegList
The list of segments that make up this path.
rotate of type boolean
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.

9.6 The GroupedAnimFunction interface

The GroupedAnimFunction interface represents a set of animation functions that share the same Anim parent.

[Constructor]
interface GroupedAnimFunction : AnimFunction {
    readonly attribute unsigned long length;
    AnimFunction? item (unsigned long index);
    void          add (AnimFunction function);
    void          remove (unsigned long index);
};

9.6.1 Attributes

length of type unsigned long, readonly
The number of animation functions in the group.

9.6.2 Methods

item
Returns the function at index if it exists, or null.
ParameterTypeNullableOptionalDescription
indexunsigned long
Return type: AnimFunction, nullable
add
Adds function to the group.
ParameterTypeNullableOptionalDescription
functionAnimFunction
Return type: void
remove
Removes the function at index if it exists.
ParameterTypeNullableOptionalDescription
indexunsigned long
Return type: void

9.7 Calculating the current time fraction

This is just iteration time (as defined in section 6.10 Calculating the iteration time) divided by iteration duration (as defined in section 6.6 Calculating the iteration duration). This isn't the job of the animation model to calculate, this is the output of the timing model. I've called this the iteration distance elsewhere.

To be more specific, the two pieces of output needed from the timing model are the iteration distance (iteration time / iteration duration) and the current iteration (needed for accumulate behavior etc., see definition in section 6.10.3 Calculating the current iteration).

9.8 Calculating animation values

9.8.1 Calculating the animation value from an iteration value

If the current iteration is zero, or the accumulateOperation is WEBA_OPERATION_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.

If the current iteration is not zero and the accumulateOperation is WEBA_OPERATION_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 WEBA_OPERATION_MERGE then the animation balue is the end value merged with the iteration value, with an interpolation parameter equal to the current time fraction

9.8.2 Calculating the iteration value for a KeyframesAnimFunction

When an Anim contains a pointer to a KeyframesAnimFunction, the animation value for that animation at given current time t is calculated according to the following steps:

  1. Convert t to the current time fraction.
  2. Generate a sorted frame list by sorting the list of AnimFrame objects contained within the AnimFrameList object by their offset. If there are no frames in the sorted frame list then no animation occurs and the animation value is just the baseVal of the property being animated.

    Need to define the "AnimFrame objects contained within the AnimFrameList object" better.

  3. Find the after frame by iterating through the sorted frame list until the last AnimFrame with an offset larger than the current 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 baseVal of the property being animated.

    Need to define baseVal and animVal somewhere.

  5. If the after frame does not exist (i.e. all frames have an offset less than the current time fraction) then construct an after frame with an offset of 1 and a value equal to the baseVal 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) ste 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 AnimInstance interface's timing attribute.
  8. Find the local time fraction by subtracting the offset of the before frame from the current 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.

    Need a rigorous definition of linear interpolation - could just reference the relevant specifications.

9.8.3 Calculating the iteration value for a PathAnimFunction

When an Anim contains a pointer to a PathAnimFunction 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.
  2. define how to calculate the normal

  3. Determine the normal rotation, which is given by atan2(normal.y, normal.x).
  4. The rotation is the transform that rotates by the normal rotation.

9.8.4 Calculating the iteration value for a GroupedAnimFunction

When an Anim contains a pointer to a GroupedAnimFunction, the animation value for that animation at given current time t is calculated by following the procedure outlined in section 10.2 Resolving Animation Stacks, treating the list of AnimFunction objects contained within the GroupedAnimFunction as the animation stack and using an initial animation value of 0 for all simple properties and id for transform.

Need to add:

Somewhere we need to describe how animated values get stored in the DOM. i.e. can you access them at all? what's the interaction with various override stylesheets etc.

Need to add a way for getting animation values of motion-on-a-path animations

10. Combining animations

10.1 The Global Animation Stack

When multiple active 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 GroupedAnimFunction.

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 recorded or calculated start time (i.e. the point where an animation changes phase from Inactive to Delayed), not the point at which the animation actually starts (i.e. the point where an animation changes phase from Delayed to Animating).

The stacking order of animations is independent of the current play direction of individual animations and animation groups.

10.2 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 section 9.8 Calculating animation values). 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 AnimFunction 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.

10.3 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 as WEBA_OPERATION_* constants in the AnimFunction interface.

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

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

10.3.3 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 Anim which references the AnimFunction 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.

10.4 Computing 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 following process:

  1. A global animation stack is generated as described in section 10.1 The Global Animation Stack.
  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 section 10.2 Resolving Animation Stacks.
  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?

11. Synchronizing with media

We want to model synchronizing with media as forcing the pause/play and current time state of content to be synchronized to match the state of the media. There are two basic possibilities here:

  1. Create a container type that causes all contained items to have synchronized pause/play and current time state. The pause state of such containers would be the union of pause states of children, and calls to set the current time of any child would be diverted to modify the current time of the parent instead.

    Note that this could be a completely new container type (e.g. a "synchronized" or "media" container) or an optional parameter modifying existing container types.

  2. Add an option to the children that, when set, causes them to be synchronized to the parent state. The pause state of the parent would need to take the pause state of synchronized children into account. Calls to set the current time of synchronized children would be diverted to modify the current time of the parent instead.

    If this model is adopted, media can be synchronized to the parent state, causing network-derived delays to propagate to the parent container and hence all children of that container; however other children of the container could still be independently played/paused/seeked.

Which model is better? Are there alternatives we should consider too?

11.1 The intrinsic duration of a media item

Basically just take the HTMLMediaElement.duration property. Possibly convert NaN values to positive infinity.

11.2 The MediaItem interface

interface MediaItem : TimedItem {
    attribute HTMLMediaElement element;
};

11.2.1 Attributes

element of type HTMLMediaElement
A pointer to the element?

12. Animation events

Need to consider how to align with existing TransitionEvents, AnimationEvents and TimeEvents. It might make sense to have our events inherit from one or more of these interfaces or have implementations dispatch both (especially if their synchronization properties differ).

I think we might also want a seek event? It seems like a lot of applications will want to do bookkeeping when there's a seek—especially a backwards seek. It might also be necessary for the SVG bindings so you can clear certain events on a backwards seek as required by SMIL.

Talk about what events are dispatched on a seek.

12.1 Synchronizing event handlers

The proposal to-date is as follows. Requirements for dispatch of animation events:

  1. Animations triggered by an event handler appear to have started at the time the event was scheduled to be triggered.
  2. No effect of an animation for any time 't' can be reflected in style, DOM or animation object until any animation event handlers associated with dispatch times at or before 't' have returned from execution.

In effect this is roughly the same as saying:

When event handlers due to animation events run, the state of the world (DOM properties etc.) so far as it is influenced by animation, is as if the current time was the time when the event was scheduled to be triggered.

There may be many ways to realise this, including:

  • dispatching animation events synchronously
  • careful updating of the animation time (after waiting for corresponding animation events to return)
  • pausing the world

Shane and Tab feel that this approach is not very webby, and might be going to too much effort in order to support a programming model that should be discouraged.

In the current model, the animation timeline is essentially static unless modified by script, which means that interrogating this timeline for information should be safe even in the absence of enforced synchronization. So if correct handling of an event requires precise knowledge about which animations have started or finished, then the timeline can be inspected at the time given in the event.

Furthermore, triggering animations off complex functions involving current animation state is probably not the right way to go - instead some kind of model of the state should be used and the animations derived from that.

In short, in the absence of strong use cases for synchronous events we feel we should go with a simpler model.

Some alternative approaches are:

  • Standard asynchronous events with no guarantees about the state of the world when event handlers are called.
  • Event logs like those proposed to replace DOM events.

13. Extensions to Document interfaces

Since the document acts as a root animation group user agents must add an animationTimeline member to the document interface for each document type where animation is supported. For example, HTMLDocument and SVGDocument.

The definition of this member is given below.

partial interface HTMLDocument {
    readonly attribute AnimGroupInstance animationTimeline;
};

13.1 Attributes

animationTimeline of type AnimGroupInstance, readonly
The animation group instance corresponding to the root of the document. This group instance exhibits the special behavior described for the root animation group in section 8.3 The document timeline.

14. Animation of common data types

Need to describe how to interpolate/add both:

  1. Integers, floats, strings etc.
  2. CSS and SVG types (colors etc.)

For transforms I think it's defined elsewhere so we can either just point to that spec, or just refer to the fact that other specs should define how their types should be supported (or not) for animation.

Need to provide facility for smooth interpolation of arbitrary paths.

15. Implementation requirements

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

16. Making animation accessible

TBD. Describe how to integrate with the Timed Text API and give examples of how to author content so it is accessible.

A. Acknowledgements

B. References

B.1 Normative references

[CSS3-2D-TRANSFORMS]
Simon Fraser; Dean Jackson; David Hyatt; Chris Marrin; Edward O'Connor. CSS 2D Transforms Module Level 3. URL: http://www.w3.org/TR/css3-2d-transforms/

B.2 Informative references

[CSS3-ANIMATIONS]
Dean Jackson (Apple Inc); David Hyatt (Apple Inc); Chris Marrin (Apple Inc). CSS Animations Module Level 3. URL: http://www.w3.org/TR/css3-animations/
[CSS3-TRANSITIONS]
Dean Jackson; David Hyatt; Chris Marrin. CSS Transitions Module Level 3. 20 March 2009. W3C Working Draft. (Work in progress.) URL: http://www.w3.org/TR/2009/WD-css3-transitions-20090320
[HTML5]
Ian Hickson; David Hyatt. HTML5. 25 May 2011. W3C Working Draft. (Work in progress.) URL: http://www.w3.org/TR/html5
[SMIL-ANIMATION]
Patrick Schmitz; Aaron Cohen. SMIL Animation. 4 September 2001. W3C Recommendation. URL: http://www.w3.org/TR/2001/REC-smil-animation-20010904
[SVG112]
Erik Dahlström; et al. Scalable Vector Graphics (SVG) 1.1 (Second Edition). 22 June 2010. W3C Working Draft. (Work in progress). URL: http://www.w3.org/TR/2010/WD-SVG11-20100622