This is the abstract for your specification.

The Timeline interface

double? fromTimelineTime (double timelineTime, TimedItem target)

This is basically supposed to be a method that answers the question, "If the time of this timeline is A (timelineTime), what will the local time be at timed item B (target)?"

Returns the corresponding time value for a given timed item, target, for a time value relative to this timeline using the following procedure.

All the following needs to be updated since it refers to time sources etc.

  1. Define the parent time source of time source A as being the value of a timeline property on A if such a property exists and refers to a TimeSource object.
  2. Define an ancestor time source of time source A as any time source that appears in the chain of parent time sources constructed starting with the parent time source of A and continuing with each subsequent parent up to and including a document time source or until there are no further parent time sources.
  3. Define a time source A as being derived from another time source B if B is an ancestor time source of A.
  4. Determine local document time as follows:
    If other is not derived from this document time source,
    1. Let remote document be the document time source that is an ancestor time source of other if such a document time source exists.
    2. If there is no such remote document raise a DOMException of type HierarchyRequestError.
    3. Let local document time be the result of calling toDocumentTime(documentTime, remote document) on this object.
    Otherwise,
    Let local document time be documentTime.
  5. Calculate the currentTime of other by following the steps described somewhere...

Exceptions:

DOMException of type HierarchyRequestError
Raised if other does not have an ancestor time source that is a document time source object.

Is this needed? And is the automatic cross-document time source negotation required?

Scaling the time

The TimingFunctionCallback callback

In addition to the timing functions provided by Web Animations, authors may also provide their own time scaling function. The behavior of this function is defined by the TimingFunctionCallback type below.

A TimingFunctionCallback function has the same behavior as defined for the scaleTime method on the TimingFunction interface. That is, it takes an input time fraction, applies some scaling operation to it, and returns an output time fraction.

For the same input time and item, this function MUST return the same output.

As a result, a user agent MAY cache the results returned by a TimingFunctionCallback as an optimization. Note, however, that if the item object is changed, this consitutes different input to the function and hence the user agent MUST NOT cache the results of the callback if the item object changes. Given the requirement that callback functions do not change item, a user agent can expect that it is safe to cache the output of a TimingFunctionCallback for at least the duration of a sample.

Is it important to be able to differentiate between when the scaling is being applied to an iteration as a whole or simply to a segment of a keyframe animation?

I suspect it probably is and that that would consitute different input to the function.

double time
The input time fraction in the range [0.0, 1.0].
TimedItem? item
The TimedItem for which the scaling is being performed. This object MUST NOT be modified.

Applying time manipulations

Needs to be adjusted as follows:

  1. Scale the time as follows:
    If timing.timingFunction is a TimingFunction object,

    Let scaled iteration time be unscaled iteration time * timing.timingFunction.scaleTime(unscaled iteration time / iteration duration))).

    If timing.timingFunction is a TimingFunctionCallback function,

    Let scaled iteration time be unscaled iteration time * timing.timingFunction(unscaled iteration time / iteration duration))).

    If calling timing.timingFunction results in an exception being thrown, let scaled iteration time be unscaled iteration time.

    Need to consider:

    • Is there any way to lock down the model during this step? i.e. make everything read-only? (Of course it's not fully possible, but can we do enough that it's meaningful?) Maybe implementations can just display something in the console: "If you're changing stuff in a timing function you're going to have a bad time"
    • Can we cache some values before calling to ensure that at least this procedure will complete as expected?
    • Need to define exactly when this procedure is performed since both the timing and the number of times it is called could produce different results if the script has side effects. For example, does querying TimedItem.iterationTime result in the script being executed every time?

      Possibly could define things such that iteration time is evaluated on each sample (need to define sampling) and then only if iterationTime is explicitly requested (i.e. no caching there)... what about other operations that rely on iteration time being up to date? what are they?

    • At points where this is called we need to check the state of the model after calling.
    • Do we need some disclaimer saying, "if the script has side effects all bets are off"
    • Also need to be careful that the callback doesn't call anything that triggers calculating the iteration time (e.g. TimedItem.iterationTime should use a cached value in that case)
    • I think script-defined timing functions might get punted to v2
    Otherwise,
    Let scaled iteration time be unscaled iteration time.

Animation templates

It is sometimes necessary to apply the same animation effect to a series of targets. Animation templates provide a means for the same set of animation properties to be applied repeatedly to a number of targets whilst maintaining a link such that changes to the template are reflected at each place where the template is applied.

In concrete terms, an AnimationTemplate object is used to create multiple Animation objects each of which maintains a link back to the template via its template property. Such Animation objects are said to be linked to a template.

The timing and animation parameters of linked animations cannot be modified directly. Rather, changes are made to the template which is then echoed to all animations linked to the same template. In order to modify the timing and animation parameters of a linked animation directly, it must first be unlinked using the unlink method.

Note that run-time properties of a linked animation such as its start time and time drift can still be modified. Only those properties attached to the timing and effect properties of a linked Animation object are read-only.

Unlinked animations can be linked to a template by:

Provide some javascript sample here demonstrating?

Animation interface members

attribute AnimationTemplate? template

For linked animations (see ), the AnimationTemplate object from which this object derives its values. For animations that are not linked to a template, this property is null.

Setting this property has the following effect:

  1. Let new template be the value to assign to the template property.
  2. If new template is null,
    1. Call unlink().
  3. Otherwise,
    1. Set timing to new template.timing.clone().
    2. Set effect to new template.effect.clone().
    3. Set template to new template.
AnimationTemplate templatize()

If this object is not already linked to a template, creates a new AnimationTemplate object based on this object and links this object to it (see ).

What is the more useful behavior? To always create a template? Or to only create one if it doesn't already have one?

The effect is equivalent to the following steps:

  1. Let source be the object on which templatize is called.
  2. If source.template is not null, return.
  3. Create a new AnimationTemplate object, template.
  4. Set template.timing to source.timing.clone().
  5. Set template.effect to source.effect.clone().
  6. Set source.template to template.
  7. Return template.
AnimationTemplate? unlink()

Makes this animation independent of the template with which it is associated if any (see )

After setting template to null the previous value of template is returned.

The effect is equivalent to the following steps:

  1. If template is null, return null.
  2. Let old template be template.
  3. Set timing to template.timing.clone().
  4. Set effect to template.effect.clone().
  5. Set template to null (but do not recursively call this function).
  6. Return old template.

The AnimationTemplate interface

attribute (AnimationEffect or CustomAnimationEffect) effect
The animation effect to apply.

The TimedTemplate interface

Both the timing of an AnimationTemplate and the methods for creating an Animation from an AnimationTemplate are specified on the TimedTemplate since this behavior is shared with animation groups (see ).

Should we allow live lists to be passed in? i.e. selectors etc.?

attribute Timing timing
The timing parameters to use for generated timed items.
TimedItem 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 and .

AnimationTarget target
The element or pseudo-element to be targetted.
optional double startTime

The start time for the generated animations expressed in seconds in the iteration time space of the AnimationGroup to which it is appended (see ).

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

sequence<TimedItem> animate ()

Creates a series of independent TimedItem objects, one for each element in target. As with animate(AnimationTarget target, double 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 and .

sequence<Node> targets
An sequence of Nodes to be animated. Any nodes in the sequence that are not of type ELEMENT_NODE will be ignored.
optional double startTime
As with animate(AnimationTarget target, optional double startTime).
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.

AnimationTarget target
As with animate.
AnimationGroup? parentGroup
The animation group to which animations should be appended.
optional double startTime

The start time for the generated animations expressed in seconds in the iteration time space of the AnimationGroup to which it is appended (see ).

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.

sequence<TimedItem> animateWithParent ( sequence<Node> targets, AnimationGroup? parentGroup, optional double startTime)
As with animateWithParent(AnimationTarget target, AnimationGroup? parentGroup, optional double startTime) except one TimedItem is created for each Node in target that is of type ELEMENT_NODE.
TimedItem animateLive (AnimationTarget target, optional double startTime)
As with animate with the exception that the TimedItem objects generated by this method are live.
sequence<TimedItem> animateLive ( sequence<Node> targets, optional double startTime)
As with animate with the exception that the TimedItem objects generated by this method are live.
TimedItem animateLiveWithParent (AnimationTarget target, AnimationGroup? parentGroup, optional double startTime)
As with animateWithParent with the exception that the animations generated by this method are live.
sequence<TimedItem> animateLiveWithParent (sequence<Node> targets, AnimationGroup? parentGroup, optional double startTime)
As with animateWithParent with the exception that the animations generated by this method are live.

Instantiating an AnimationTemplate

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

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

Group templates

As with animations, templates can also be created for animation groups.

Lots of questions here about how this should work.

The AnimationGroupTemplate interface

TBD

void clear ()
Removes all child items from the group.
getter TimedTemplate? (unsigned long index)
Returns the template item at index. If index is greater than or equal to length returns null.
setter TimedTemplate (unsigned long index, TimedTemplate newItem)

Replaces the item at index with newItem by calling splice(index, 1, newItem).

Returns newItem.

The behavior of this method is identical to the equivalent setter in AnimationGroup except that DOMExceptions of type HierarchyRequestError are not thrown.

sequence<TimedTemplate> add ( TimedTemplate newItem, TimedTemplate... otherItems)

Add newItem and each otherItems as the last item(s) in the group by calling splice(group.length, 0, newItem, otherItem1, ... otherItemN).

Returns a sequence containing the added items: [newItem, otherItem1, ... otherItemN].

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

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

Returns the removed items.

sequence<TimedTemplate> splice ()

Modifies the list of children of this group by first removing deleteCount items from start followed by adding newItems at the same point.

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

As with AnimationGroup.slice the operation of slice is based on ECMAScript 5's Array.prototype.splice.

The operation of this method is identical to that of AnimationGroup.slice with the notable difference that DOMExceptions of type HierarchyRequestError are not thrown since there is no AnimationGroupTemplate corresponding to a document timeline.

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<TimedTemplate> 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<TimedTemplate> splice (long start, unsigned long deleteCount, TimedTemplate... newItem)
An overload of splice to take a variadic list of items rather than requiring a sequence. The operation is identical to splice(unsigned long start, unsigned long deleteCount, sequence<TimedTemplate> newItems).
long indexOf (TimedTemplate item)
Returns the index of item within the group. If item is not in the group, returns -1.

The ParGroupTemplate interface

ParGroup objects can be created from ParGroupTemplate objects.

The SeqGroupTemplate interface

SeqGroup objects can be created from SeqGroupTemplate objects.

Instantiating an AnimationGroupTemplate

TBD. This is probably all wrong.

The procedure for creating an AnimationGroup from an AnimationGroupTemplate, template, given a list of target elements, and optionally given a parent group follows.

Note that ParGroupTemplate objects produce ParGroup objects and likewise SeqGroupTemplate objects produce SeqGroup objects. In the following description the AnimationGroupTemplate and AnimationGroup types should be substituted with the concrete types in use.

  1. Create an empty sequence to hold the generated AnimationGroup objects.
  2. For each element in the list of target elements:
    1. Create a new AnimationGroup 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) or child.animateLiveWithParent(element, group, startTime) depending on whether this procedure was invoked with animate or animateLive.
    4. If parentGroup is not specified, let parentGroup be element.ownerDocument.animationTimeline.
    5. Append group to parentGroup.
    6. Append group to the sequence of AnimationGroup objects.
  3. Return the sequence of AnimationGroup objects.

Custom effects

I think we want two types of custom 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:

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.

Script interface

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

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

The CubicBezierTimingFunction interface

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

Constructor (sequence<double> points)

Creates a new CubicBezierTimingFunction 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 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

An individual key frame is represented by the Keyframe interface.

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 DOMString? timingFunction

An optional timing function to apply between this keyframe and the next keyframe in any KeyframeList in which this object appears.

The acceptable values and the handling of unrecognized values is identical to the behavior described for the timingFunction member of TimingDictionary.

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.
DOMString? timingFunction = null

A string specifying the timing function to apply between this keyframe and the next keyframe in any KeyframeList in which this object appears.

The acceptable values and the handling of unrecognized values is identical to the behavior described for the timingFunction member of TimingDictionary.