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.
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 specification makes some additions to some interfaces defined in HTML5 [[HTML5]].
At a glance, Web Animations consists of two largely independent pieces, a timing model and an animation model. The role of these pieces is as follows:
Graphically, this flow can be represented as follows:
For example, consider an animation that:
The first three points apply to the timing model. At a time of 6 seconds, it will calculate that the animation should be half-way through its second iteration and produces the result 0.5. The animation model then uses that information to calculate a width for the rectangle of 75.
This specification begins with the timing model and then proceeds to the animation model.
Two features characterise the Web Animations timing model: it is stateless and it is hierarchical.
The Web Animations timing model operates by taking an input time and producing an output time fraction. Since the output is based solely on the input time and is entirely independent of previous inputs, the model may be described as stateless. This gives the model the following properties:
There are two apparent exceptions to the stateless behavior of the timing model.
Firstly, events are fired when, for example, one sample falls on the opposite side of an animation's interval boundary to the previous sample. This is certainly stative behavior. However, events should be considered as a layer added on top of the core timing model. In the case where no event listeners are registered, the model is stateless.
The other exception to this stateless behavior is that a number of
methods such as pause
and reverse
are
defined in terms of the time at which they are called and are
therefore stative.
These methods are provided primarily for convenience and are not part
of the core timing model but, like events, are layered on top.
Finally, each time the model is sampled, it can be considered to establish a temporary state. While this temporary state affects the values returned from the API, it has no influence on the subsequent samples and hence does not conflict with the stateless qualities described above.
The other characteristic feature of the Web Animations timing model is that time is inherited. In effect, time begins with real world time (a monotonically increasing time source) and cascades down a number of steps to each animation. At each step, time may be shifted backwards and forwards, scaled, reversed, paused, and repeated.
Need diagram here.
A consequence of this hierarchical arrangement is that complex animation arrangements can be reversed, schedule, accelerated and so on, as a whole since the manipulations applied to the parent cascade down to its descendants. Furthermore, since time has a common source, it is easy to synchronize otherwise independent animations.
Despite this notion of inherited time, some features are implemented outside of the time hierarchy. In particular, synchronisation with HTML's media controllers (see ) requires mirroring changes to pause state between items in different branches of the time hierarchy.
The first step in this hierarchy is the animation timeline.
Each document contains a timeline to which animations may be added. These animations have an interval during which they are scheduled to animate.
Animations in the timeline are represented by Animation
objects.
Animation
interfaceCreates a new Animation object.
Examples of the usage of this constructor are given in .
null
for animations that do not target
a specific element.
effect
property of the newly created Animation object.
If this parameter is an AnimationEffect object or CustomAnimationEffect object, it will shared with any other Animation objects referring to the same AnimationEffect or CustomAnimationEffect object. It will not be copied.
If this parameter is null
, the newly created
Animation will also have a null
animation
effect.
Otherwise, if this parameter is any other type of object, it
will be passed to AnimationEffect.createFromProperties
and the resulting AnimationEffect object used as the
animation effect.
If this parameter is a double
, then it
specifies the duration of a single iteration of the animation,
that is, the iteration duration, in seconds.
In this case, the timing
property of the new
Animation object is set to a new Timing object
constructed from a TimingDictionary object with the
duration
property set to the double
value specified here, and all other properties set to their
default values.
Similarly, if this parameter is null
, the
timing
property is set to a new Timing
object constructed from a TimingDictionary object
with all properties set to their default values.
If this parameter is of type TimingDictionary, then the
passed-in dictionary is used to construct a new Timing
object which is then assigned to the timing
property of the new Animation object.
If this parameter is a Timing object, it is directly
assigned to the timing
property of the new
Animation object.
It is not copied.
In this way Timing objects can be shared between
Animation objects.
Finally, if this parameter is not specified, it is as if
a value of null
were specified.
null
, the animation will not be appended to any
group.
If not specified, the current iteration time of the
AnimationGroup to which the animation is appended (as
determined by the parentGroup
parameter) will be
used. If parentGroup
is null
, and
the startTime
is not specified, a time of zero
will be used.
The animation effect to apply (see ).
May be null
in which case the animation will produce
no noticeable effect other than dispatching events.
Exceptions:
NoModificationAllowedError
template
is not null
).
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:
template
property.null
,
unlink()
.timing
to new
template.timing.clone()
.effect
to new
template.effect.clone()
.template
to new
template
.
The element being animated by this object.
This may be null
for animations that do not target
a Element
such as an animation that produces a sound
using an audio API.
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:
templatize
is called.source.template
is not
null
, return.template.timing
to
source.timing.clone()
.template.effect
to
source.effect.clone()
.source.template
to
template.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:
template
is null
, return
null
.template
.timing
to
template.timing.clone()
.effect
to
template.effect.clone()
.template
to null
(but do not
recursively call this function).Animation
objectThe Animation constructor offers a number of approaches to creating a new Animation object. At its simplest, an Animation object that changes the ‘left’ property of elem to 100 over three seconds can be achieved as follows:
var anim = new Animation(elem, { left: '100px' }, 3);
To specify further timing properties such as the playback rate, a TimingDictionary can be used as follows:
var anim = new Animation(elem, { left: '100px' }, { duration: 3, playbackRate: 2 });
To share or re-used timing properties with another animation, a Timing object can be directly passed in as follows:
// Share the timing properties of animB with animA var animA = new Animation(elem, { left: '100px' }, animB.timing); // Re-use the timing properties of animB in animC without sharing var animC = new Animation(elem, { left: '100px' }, animB.timing.clone());
The animation effect parameter may specify multiple properties, an AnimationEffect object, or even a callback object.
// Specify multiple properties at once var animA = new Animation(elem, { left: '100px', top: '300px' }, 5); // Share the animation effect of another animation var animB = new Animation(elem, animA.effect, 3); // Supply a specialized animation effect var animC = new Animation(elem, new PathAnimationEffect( ... ), 3); // Supply a custom script-based animation effect var animC = new Animation(elem, { sample: function(time) { if (time !== null) { document.documentElement.currentScale = 1.0 + time * 2.0; } else { document.documentElement.currentScale = 1.0; } }, clone: function { return this; } }, 3);
Fill in the parameters for PathAnimationEffect above once we have decided on them.
Add examples of specifying the parent group and start time once we resolve the behavior of those.
The period that an animation is animating is called the animation interval. Each animation has only one such interval.
The lower bound of the animation interval is determined by the start time of the animation but may be shifted by a start delay on the animation.
The upper bound of the interval is determined by the animation duration.
The relationship between the start time, start delay, and animation duration is illustrated below.
Outside of the animation interval, an animation may still affect its target depending on its fill mode. The different modes are as follows:
Some examples of the these fill modes are illustrated below.
At a given time, it is possible to describe an animation as in effect if either (a) the time falls within the animation's animation interval, or (b) the fill mode of the animation causes it to apply a fill mode to its target at the given time.
The normative definition of fill behavior is incorporated in the calculation of the animation time in .
Note that setting a fill mode has no bearing on the endpoints of the animation interval. However, the fill mode does have an effect on the calculation of the animation time and consequently the iteration time since these are defined only when the animation is in effect.
It is possible to specify that an animation's effect should repeat a fixed number of times or even indefinitely. This repetition occurs within the animation interval. The span of time during which a single repetition takes place is called an iteration interval.
Unlike the animation interval, an animation can have multiple iteration intervals although typically only the interval corresponding to the current iteration is of interest.
The length of a single iteration is called the iteration duration. Comparing the iteration duration and the animation duration we have:
The relationship between the iteration duration and animation duration is illustrated below.
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:
In addition to these time spaces, when animation groups are used (see ) 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.
Typically, item time space is used for operations confined to a single animation such as seeking and pausing whilst parent iteration time space is used to synchronize animations in a group and hence is the time space used to represent an animation's start and end time. Animation time space is not used directly in the Web Animations interfaces.
Some of these time spaces are illustrated below.
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, the procedure for calculating the animation time defined in will never return a negative value.
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 the intervals.
In the examples below, for the repeated animation, at animation time 1s, the iteration time is 0. For the sequenced animation, in the absence of any fill mode, at parent iteration time 1s, only animation B can affect its target; there is no overlap.
An exception to this behavior is that when performing a fill, if the fill begins at an interval endpoint, the endpoint is used. This behavior falls out of the algorithm given in and is illustrated below.
Timing properties are collected under the Timing
interface.
If this object is attached to a linked Animation or
AnimationGroup (see ) then it is readonly and attempting to set any
of its properties will result in a DOMException of type
NoModificationAllowedError
being thrown.
In order to modify the properties of a readonly object, it is
necessary to first call the unlink
method on the
corresponding Animation or AnimationGroup object.
When throwing a DOMException as a result of attempting to set a readonly property, user agents that provide debugging feedback SHOULD also provide feedback indicating the reason that this object is readonly and the remedy described above.
Timing
interfaceCreates a new Timing object using the supplied parameters.
In most cases the members of the params
TimingDictionary can be assigned directly to the property
in the Timing interface of the same name.
The one exception is the timingFunction
member
which in a TimingDictionary value can also be set to
a DOMString
.
In this case, the DOMString
is first passed to
TimingFunction.createFromString
and
the returned TimingFunction is assigned to the
timingFunction
property of the new Timing
object.
Should we make this fail silently here? Which is better from a forwards compatibility standpoint?
For example:
If any of the values specified onparams
is invalid such that setting the corresponding Timing property would trigger an exception, the property is set to the default value as specified on theTimingDictionary
definition. User agents that provide a debugging feedback SHOULD indicate that an unrecognized or invalid value was encountered.
Exceptions:
IndexSizeError
params
is out of range.
SyntaxError
params.timingFunction
is set to
a string that it not recognized by
Timing.createFromString
as
indicated by a null
return value.
The number of seconds which, when added to the timed item's
startTime
, defines the lower bound of the timed
item's animation interval.
A non-normative description of the effect of this property on timing is given in .
Exceptions:
NoModificationAllowedError
The duration in seconds of 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).
This property corresponds to the iteration duration defined in and described non-normatively in .
Exceptions:
NoModificationAllowedError
IndexSizeError
A real number greater than or equal to zero (including positive infinity) representing the number of times to repeat the animation.
Exceptions:
NoModificationAllowedError
IndexSizeError
A finite real number greater than or equal to zero representing the number of iterations into the animation at which to begin. For example, a value of 0.5 would cause the animation to begin half-way through the first iteration.
The iterationCount
is effectively added to
the iterationStart
such that an animation with an
iterationStart
of ‘0.5’ and
iterationCode
of ‘2’ would still
repeat twice however it would begin and end half-way through
the animation's iteration interval.
Setting the iterationStart
to a value greater than
or equal to one is typically only useful in combination with an
animation effect that has an accumulateOperation
other than ‘replace’.
Exceptions:
NoModificationAllowedError
IndexSizeError
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.
The playbackRate
is applied to the item's
animation time and hence has no effect on the start
time (see ).
Setting this attribute will affect the item's intrinsic animation duration.
Exceptions:
NoModificationAllowedError
Direction behavior as specified by one of the PlaybackDirection enumeration values.
Exceptions:
NoModificationAllowedError
The fill mode as specified by one of the FillMode enumeration values.
Exceptions:
NoModificationAllowedError
Creates a new Timing object with each of its properties
set to the same value as this object with the exception of the
timingFunction
property which is set to
timingFunction.clone()
.
FillMode
enumerationA non-normative description of these modes is given in .
PlaybackDirection
enumerationTimingDictionary
dictionary
To simplify creation of Timing objects
a TimingDictionary
can be used.
Except where otherwise noted, the acceptable values for each property and their meanings are defined in the Timing interface.
The number of seconds from a timed item's startTime
to the start of the animation interval.
See startDelay
on the Timing
interface.
The duration in seconds of a single iteration, that is, the
iteration duration.
See duration
on the Timing
interface.
The number of times to repeat the item.
See iterationCount
on the
Timing interface.
The number of iterations into the item at which to begin.
See iterationStart
on the
Timing interface.
A multiplier applied to the inherited time potentially causing
the item to run at a different rate to its natural speed.
See playbackRate
on the
Timing interface.
The direction in which animation proceeds, e.g. "reverse".
See direction
on the Timing
interface.
An optional timing function used to scale the time to produce easing effects and the like.
Unlike the Timing interface, the member here may be set
to either a TimingFunction object or
a DOMString
corresponding to one of the values
recognized by Timing.createFromString
.
The fill mode of the animation.
See fillMode
on the Timing
interface.
Note that in both CSS Animations [[CSS3-ANIMATIONS]] and SVG [[SVG112]] the default fill mode is "none". Web Animations differs in this regard since it was determined that when generating animations from script forwards filling is the more commonly-desired behavior.
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.
The timing parameters for this item.
Exceptions:
NoModificationAllowedError
Returns the effective item time of the timed item.
This differs from the definition of item time used
elsewhere in the model in that when the parent iteration time is
null
(e.g. because parentGroup
is
null
, or because the parent is not in effect)
a parent time of zero is used.
This allows the item to be seeked prior to being attached to
a group.
The value returned is calculated as follows:
currentTime =
effective parent time - startTime - timeDrift
Setting this value performs a seek operation according to the steps described in described in .
The length in seconds of the animation interval.
Initially, this attribute will reflect the intrinsic animation duration. Changes to the model that cause the intrinsic animation duration to change are reflected in the value returned here.
The instrinsic animation duration may be overridden by
setting this attribute to any real number.
Setting this attribute to undefined
has the effect of
clearing the override value so that the attribute returns to
reflecting the intrinsic animation duration.
Exceptions:
IndexSizeError
The time in seconds representing the offset into the iteration
duration using the steps described in .
As a result of that definition, this property will be
non-null
if and only if the animation is in
in effect.
This property corresponds to the iteration time described in .
This is not writeable since doing so would basically require doing
a reverse conversion from the value to set into to item time space
and then updating the currentTime
accordingly.
However, that's not possible since timing functions may be applied
and some of them are not invertible.
The iteration duration calculated for this item.
If timing.duration
is set and greater than or
equal to zero, this will match timing.duration
.
Otherwise, this will reflect the calculated intrinsic
duration.
This property corresponds to the iteration duration defined in and described non-normatively in .
The time which, when combined with the
timing.startDelay
, defines the lower bound of the
animation interval.
It is expressed in seconds in the iteration time space of
parentGroup
.
When the parent animation group is a sequence group, any
previously set value set for startTime
is ignored and
instead the value of this property is determined by the procedure
defined in .
Furthermore, attempts to set this property will raise an
DOMException of type InvalidStateError
.
If this item is later made the child of a parallel animation
group, any previously set value will be restored.
This property corresponds to the start time described in .
Exceptions:
InvalidStateError
parentGroup
is a sequence
animation group (see ).
The upper bound of the animation interval expressed in
seconds relative to the iteration time space of
parentGroup
.
The endTime
is calculated as follows:
locallyPaused
is true
,endTime
is positive infinity.
endTime
is the result of evaluating
startTime + timing.startDelay +
animationDuration + timeDrift
.
Note that while the endTime
is read-only, it can be
set indirectly by overriding the animationDuration
property.
For example, to set endTime
to time t in
item time space, set animationDuration
to
t - startTime - timing.startDelay
.
The number of seconds that the actual item time of this item lags behind its scheduled time as a result of pausing and seeking this item.
The timeDrift
property is both a stored and a
calculated value.
When this timed item is paused, the value is calculated from the
pause start time.
When the timed item is not paused, the value stored for the
property is used.
The stored value is initially zero, and is updated when the item
is unpaused or seeked.
The value returned is as follows:
locallyPaused
is false
,locallyPaused
is true
,effective parent time - startTime - pause start
time
.
The pause state of this timed item.
Initially false
except for the document timeline
where it is initially true (see ).
When setting the following procedure is used:
locallyPaused
,
return.
locallyPaused
as follows,
locallyPaused
is true,currentTime
as
pause start time.
locallyPaused
is false,timeDrift
to the result
of evaluating effective parent time - startTime
- pause start time
.
locallyPaused
to new value.
Should this be writeable? Once we integrate with media controllers we might want to make this writeable only when not associated with a media controller much like things currently work in HTML5.
true
if an only if the
locallyPaused
property of this object or one of its
ancestor animation groups is true
.
locallyPaused
property to true
by
following the procedure described for setting that property.
This method is intended to behave in a comparable manner to HTMLMediaElement's pause method.
Unpauses this item and, if the item is not currently animating, seeks to the beginning of the animation interval by taking the following steps.
currentTime > timing.startDelay
+ animationDuration
and
timing.playbackRate ≥ 0
, set
currentTime
to timing.startDelay
.locallyPaused
to false
.
To unpause the item without seeking, set
locallyPaused
to false
.
This method is intended to behave in a comparable manner to HTMLMediaElement's play method.
Updates the speed of the timed item such that it produces a smooth change.
In particular:
timing.fillMode
will be
inverted to prevent sudden jumps in output, andThe smooth change is achieved using the following steps:
timing.playbackRate
prior to updating it.timing.playbackRate
to the passed-in
playbackRate
parameter.timing.playbackRate
.
This isn't great. If you set the playback rate to zero at
a given time, you expect it to act as if paused at that
time.
Currently, the only way to get that behavior out of the
model is to set the iterationStart
and that
seems pretty intrusive.
timing.fillMode
is
"forwards"
,timing.fillMode
to
"backwards"
.
timing.fillMode
is
"backwards"
,timing.fillMode
to "forwards"
.
timing.fillMode
as is.null
, return.animationDuration
if
necessary (that is, if animationDuration
reflects
the intrinsic animation duration), applying the
updated value for timing.speed
.seek adjustment =
(item time - timing.startDelay
) *
(1 - previous rate / new rate)
.
animationDuration
.item time
- seek adjustment
.currentTime
to seek time.Exceptions:
NoModificationAllowedError
template
is not null
(see ).
Reverses this animation such that the reverse animation
immediately begins reversing from its current point.
Note that this means that an animation that has yet to begin,
calling reverse
will mean that it never starts.
currentTime
is null,currentTime < timing.startDelay
,timing.startDelay + animationDuration
.
currentTime >
timing.startDelay + animationDuration
,timing.startDelay
.
animationDuration - currentTime - startDelay
.
changePlaybackRate(-timing.playbackRate)
.
currentTime
to seek time.
Exceptions:
NoModificationAllowedError
template
is not null
(see ).
Removes the timed item from its parent group. As a result, the timed item will no longer affect its target.
Exceptions:
NoModificationAllowedError
template
is not null
(see ).
Is this exception necessary? It will depend how we approach group templates.
Do we need a means for getting the startTime etc. in document time (i.e. in terms of the root time container)?
In order to calculate various properties of the timing model the following common definitions are used.
parentGroup.iterationTime
unless
parentGroup
or parentGroup.iterationTime
is null
in which case it is zero.The iteration duration is calculated according to the following steps:
timing.duration
timing property is set
to a numerical value greater than or equal to zero (including
positive infinity),
timing.duration
.
The value of an item's intrinsic duration depends on the type of the item.
For animations the intrinsic duration is zero. The intrinsic duration for animation groups and media items is described under , , and .
Since the intrinsic duration of an animation is zero, and the
default fillMode
when constructing an
Animation
is forwards, it is possible to
create animations that simply set a property without any
interpolation as follows,
new Animation(elem, { display: 'none' });
This is particularly useful in combination with other animations or timed items. For example, fading an element before switching ‘display’ to ‘none’ can be achieved as follows,
new SeqGroup( [ new Animation(elem, { opacity: '0%' }, 1), new Animation(elem, { display: 'none' }) ] );
In order to calculated the intrinsic animation duration we first define the repeated duration as follows:
repeated duration =
iteration duration *
timing.iterationCount
The intrinsic animation duration is calculated according to the following steps:
timing.playbackRate
is zero, return
Infinity
.
repeated duration
/ abs(timing.playbackRate
)
.
The item time is calculated according to the following equation:
item time = parentGroup.iterationTime
- startTime - timeDrift
If either parentGroup
or
parentGroup.iterationTime
is null
, the
item time is null
.
Need to define this for the document timeline.
The animation time is based on the item time and start delay. It is defined only when the timed item is in effect and is calculated according to the following steps:
null
, return null
.
item time < timing.startDelay
the result depends on the fill mode as follows,
null
.
item time <
timing.startTime + animationDuration
, return
item time - timing.startDelay
.
animationDuration
.
null
.
The iteration time is calculated by first applying the animation's
playbackRate
and iterationStart
properties
to produce the adjusted animation time
The adjusted animation time is then divided into intervals
resulting in 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.
Before the animation time can be converted to an iteration time we must factor in the animation's playback rate and iteration start. This is called the adjusted animation time.
In order to calculate the adjusted animation time we first define the start offset as follows:
start offset =
iterationStart * iteration duration
The adjusted animation time is calculated according to the following steps:
null
, return
null
.
timing.playbackRate
as follows,
timing.playbackRate
is negative,(animation time -
animationDuration) * timing.playbackRate
+ start offset
.
animation time * timing.playbackRate
+ start offset
.
The unscaled iteration time is calculated according to the following steps:
null
,
return null
.
adjusted animation time - start
offset
is equal to the repeated duration,
timing.iterationCount
is not zero,
and (timing.iterationCount + timing.iterationStart)
% 1
is zero,
return the iteration duration.
adjusted animation time
% iteration duration
.
The unscaled iteration time is converted into the iteration time using the following steps:
null
,
return null
.
Let scaled iteration time be
unscaled iteration time *
timing.timingFunction.scaleTime(unscaled iteration
time / iteration duration)))
.
animationDuration
is a positive multiple of
the iteration duration increment d
by 1.
d % 2 == 0
, let the
current direction be forwards, otherwise let
the current direction be reverse.
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.
The time fraction is calculated according to the following steps:
the time fraction is as follows.
timing.startDelay
,
timing.iterationCount
and an
iteration duration of 1.
Since timing functions are allowed to produce output times outside the range [0,1] it is possible that the value calculated for a time fraction also lies outside this range.
The current iteration can be calculated from the following steps:
null
, return
null
.
floor(timing.iterationStart + timing.iterationCount)
.
timing.iterationStart
+ timing.iterationCount - 1
.
floor(adjusted animation time /
iteration duration)
.
If the iteration duration is infinite, the
result of floor(adjusted animation time /
iteration duration)
will be zero as defined by
IEEE 754-2008.
Timed items may be paused and resumed independently of the parent animation group they belong to. The effect is that the time of the item lags behind that of its parent. This lag is called the time drift and 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.
Pausing and seeking are realised using two additional inputs to the
timing model, the time drift which is exposed as the
timeDrift
property on the TimedItem interface, and
the pause start time which is internal to the model.
Both the time drift and pause start time are
initially zero.
The pausing behavior is realised through the specific steps defined
for calculating and updating the locallyPaused
and
timeDrift
properties of the TimedItem interface.
Seeking, like pausing, has the effect of causing a timed item's time to drift from that of its parent animation group. Consequently, it is also uses the time drift and pause start time concepts on which pausing is based.
Seeking is performed in response to a change to a timed item's
currentTime
property and is realised by adjusting the
timeDrift
property or the internal pause start
time of the item as follows:
currentTime
should be set.locallyPaused
property as follows:
locallyPaused
is true,locallyPaused
is false,timeDrift
to
the result of evaluating
effective parent time - startTime -
seek time
.
Note that the currentTime
property is not actually set
directly but is updated to the seek time as a result of the
calculation outlined in the decsription of the
currentTime
property on the TimedItem interface.
The animation events dispatched when a seek is performed are described in .
It is often desirable to control the rate at which an animation progresses. For example, easing the rate of animation can create a sense of momentum and produce a more natural effect. Conversely, in other situations such as when modelling a discrete change, a smooth transition is undesirable and instead it is necessary for the animation to progress in a series of distinct steps.
For such situations Web Animations provides a variety of timing functions that scale the progress of an animation. In situations where the timing functions defined here are insufficient, it is possible to provide a custom script-based timing function.
In all cases, timing functions take an input time and produce a scaled output time.
Diagram
Such timing functions can be applied to an iteration of an animation as a whole via the TimedItem interface or to a segment of a keyframe animation via the KeyframeAnimationEffect interface.
TimingFunction
interfaceThe timing functions provided by Web Animations share a common TimingFunction interface as defined below.
Takes an input time fraction in the range [0, 1] and applies some transformation on the value to produce an output time fraction (typically, but not necessarily, also in the range [0, 1]).
The TimedItem for which the time scaling operation is being performed.
Some timing functions, for example, may produce different results depending on the animation values involved to produce an even rate of change.
This may be null
, for example, when invoked
directly by user code for the purpose of testing or re-using
the scaling operation in another context.
Implementations of this interface for which there is no
meaningful result in the absence of a TimedItem will
simply return time
unchanged when
item
is null
.
For implementations of this interface that have local state, produces an identical but independent copy of this object. For implementations without local state, returns the same object.
Creates a new TimingFunction object based on a string-based specification (e.g. "ease-in").
The acceptable values and their meanings are those defined for the transition-timing-function property in CSS Transitions [[!CSS3-TRANSITIONS]].
In addition to the values defined in CSS Transitions, this method
extends the steps()
function notation to allow
‘middle’ as a transition point keyword (e.g.
steps(3, middle)
) corresponding to the
‘middle’ StepPosition value.
Similarly, the keyword ‘steps-middle’ is recognized by
and given the meaning steps(1, middle)
.
Strings that specify a cubic-bezier timing function result in a new SplineTimingFunction being returned. Strings that specify a stepping function produce a new StepTimingFunction.
If spec
is unrecognized, null
is
returned.
User agents that provide debugging feedback SHOULD report the
unrecognized value.
So the string cubic-bezier(...)
produces
a SplineTimingFunction.
That's a bit confusing.
But CubicBezierTimingFunction
is a lot to type and
not very user-friendly.
Should we make the ‘linear’ keyword return
null
as well?
If we do, we need to be careful to update the constructor for
Timing so that it doesn't throw an exception in that case
(or report anything to the console).
A common method of producing easing effects is to use a cubic Bézier curve to scale the time. The endpoints of the curve are fixed at (0,0) and (1,1) while two control points P1 and P2 define the shape of the curve. Provided the x values of P1 and P2 lie within the range [0,1] such a curve produces a function that is used to map input times (the x values) onto output times (the y values). This arrangement is illustrated below.
The curves produced by the keywords accepted by the
Timing.createFromString
method are illustrated below.
SplineTimingFunction
interface
Cubic bézier curve based timing functions are represented using
the SplineTimingFunction
interface defined below.
Creates a new SplineTimingFunction object and initializes
the points
member to the passed in list of
points
.
It would be more convenient for authors if the passed in list of points could be longer than four items and we simply read the first four items and ignored the rest. However, applications may begin to depend on that behavior and we could not easily allow this object to take longer lists (to represent more complex curves) in the future without adding a separate constructor for that purpose.
Exceptions:
IndexSizeError
points
is outside the range [0, 1]
or if the length of points
is not 4 items.
A sequence of four real numbers representing the coordinates of the two control points in the following sequence <p1-x> <p1-y> <p2-x> <p2-y>.
Each of the x values (i.e. p1-x and p2-x) must be in the range [0, 1].
Exceptions:
IndexSizeError
InvalidModificationError
points
.
points[0], points[1]
),
(points[2]
, points[3]
), (1, 1).
Returns the resulting y value when the x
value is max(0, min(1, time))
.
It is possible to scale an animation's timing so that the animation occurs in a series of discrete steps using a stepping function.
A stepping function divides the input time into a specified number of intervals that are equal in duration. The output time, starting at zero, rises by an amount equal to the interval duration once during each interval at the transition point which may be either the start, midpoint, or end of the interval.
In keeping with Web Animation's model for endpoint exclusive interval timing (see ), the output time at the transition point is the time after applying the increase (i.e. the top of the step).
Some example step timing functions are illustrated below.
StepPosition
enumerationThe point within a step interval at which the change in value occurs is specified using one of the StepPosition enumeration values.
StepTimingFunction
interfaceStep timing functions are represented by the StepTimingFunction interface.
Creates a new StepTimingFunction with the specified number of steps and transition point.
Exceptions:
IndexSizeError
numSteps
is zero.
A number greater than or equal to one representing the number of steps in the function.
Exceptions:
IndexSizeError
SmoothTimingFunction
interfaceNeed help from someone who knows math. We'd like to be able to have a function with many extrema. Ideally, something like Catmull-Rom curves would be great where you could just say:
[ 0: 0, 0.7: 1, 0.8: 0.9, 1: 1 ]
And you'd get a smooth curve that goes to the full value and bounces back a bit then finishes.
The trouble is that Catmull-Rom curves won't necessarily give you a function even if you sort the x values.
While it is possible to set the timing properties of animations individually, it is often useful to bundle animations together and control their timing as a group.
This can be used to share common timing properties as illustrated below:
As well as sharing timing information, by grouping animations together they can be seeked, paused, and stopped as a unit.
The timing of the children of a group is based on the timing of the group. Specifically, times for the children are based on the parent's iteration time. That is, the children animate inside an iteration of the parent.
As an example, consider repetition. If a group has an iteration count of 2, then the children of of the group will all play twice since they effectively play inside the group's iterations.
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.
A further result of the children of a group basing their timing on the group's iteration time is that they cannot animate outside of the group's animation interval. This is because the iteration time of a group will not change outside its animation interval. This allows groups to clip the playback of their children.
Some further consequences of group children basing their timing on their parent group's iteration time are:
Groups can be used to provide synchronization behavior for its children. For example, one type of group runs its children in parallel, whilst another type runs the children in sequence.
Compare the two arrangements illustrated below:
Groups can also contain other groups which allows for more sophisticated synchronization.
Web Animations defines two types of animation groups.
Move all this to its own section later on.
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 with the following special properties:
parentGroup
property returns null
.
cancel()
will result in a DOMException of type
HierarchyRequestError being thrown.
For properties that represent times in parent iteration time space a time space whose zero time is 00:00:00 UTC on 1 January 1970 is used.
As a result, the parent iteration time is the number of seconds from 00:00:00 UTC on 1 January 1970 to the current moment in UTC time.
We should use a monotonically increasing timestamp here. Not one that can potentially skip due to clock adjustments.
Move/copy exception behavior to the relevant properties / methods.
Unlike other timed items, the document timeline is initially paused. The moment at which the timeline becomes unpaused is defined in .
On creating an AnimationGroupInstance
corresponding to
the document timeline, the following steps are performed.
true
.
By setting the start time to the parent iteration time and then pausing the timeline, the start time is always well-defined. Furthermore, seeking the timeline before it is unpaused provides intuitive results without special casing the algorithm for seeking (see ).
The time at which the timeline will be automatically unpaused is
determined by the timelineStart
property on the
Document
object (see ). It can have the following values:
The moment immediately prior to dispatching the "load" event for the document.
For HTML documents, this is the moment after the current document readiness has changed to "complete" but before dispatching the load event. In terms of the timings defined in NAVIGATION-TIMING (Need to add NAVIGATION-TIMING to the biblio), this occurs between the domComplete and loadEventStart timings.
This is the default value.
The moment after the user agent stops parsing but before running scripts that are designated to execute when parsing finishes.
For HTML documents, this occurs after updating the current document readiness to "interactive" (see the specified behavior for the end of parsing), that is, after the domInteractive timing.
unpause
is called on the timeline.
For the onload and onstart values, it is not sufficient to simply the record the appropriate time and seek the animations accordingly at a later moment. Rather, the timeline must be actually unpaused at the appropriate moment such that scripts that execute during page loading (for example, in response to the load event) can make assumptions about the pause state of the timeline.
Any call to pause
or unpause
on the
timeline disables the automatic unpause behavior defined by the
timelineStart
property on the document.
It is as if the timelineStart
property were set to
manual.
The ability for script to override the timelineStart
property by calling pause
to indefinitely postpone
the start of animation is necessary for backwards compatibility
with SVG.
This override behavior applies even if the timeline has already
been unpaused since changing the timelineStart
property could return the timeline to a state where the automatic
unpause behavior would otherwise apply.
The timelineStart
property may be changed at any time.
The result of changing this property is as follows:
pause
or unpause
has been called on
the timeline, return immediately.
timelineStart
as defined in .
Document
interface
To provide access to the document timeline the following extensions
to the Document
interface are required.
Documents may include external animated resources, for example, via
the <object>
or <use>
elements. These external resources will often have their own
Document object and their own timeline. In order to synchronize
these timelines the same approaches can be applied as with described
under .
AnimationGroup
interfaceRepresents a list of timed items.
The AnimationGroup
interface supports indexed
properties with indices in the range 0 ≤ index <
group.size
.
For linked animation groups (see ), the AnimationGroupTemplate object
from which this object derives its values.
For animations that are not linked to a template, this property is
null
.
Make this writeable?
Removes all child timed items from the group.
Exceptions:
NoModificationAllowedError
template
is not null
(see ).
Returns the item at index
.
If index
is greater than or equal to
length
returns null
.
Replaces the item at index
with newItem
by calling splice(index, 1, newItem)
.
No attempt is made to check if the item at index
is
already newItem
. In such a case, newItem
will be removed from this group and re-added as per the usual
operation of slice
.
Returns newItem
.
Exceptions:
HierarchyRequestError
newItem
is the timeline for a document
(see ).
IndexSizeError
index
is outside of the range 0 ≤
index < group.length
.
NoModificationAllowedError
template
is not null
(see ).
Whilst splice
allows negative indices,
WebIDL requires index property setters to take an index of type
unsigned long
and hence index
is
restricted to the range 0 ≤ index <
group.length
.
Add newItem
and each otherItems
as the
last item(s) in the group by calling splice(group.length, 0,
newItem, otherItem1, ... otherItemN)
.
Returns a sequence containing the added items:
[newItem, otherItem1, ... otherItemN]
.
Exceptions:
HierarchyRequestError
newItem
objects will be added to the group.
NoModificationAllowedError
template
is not null
(see ).
Removes the item(s) at index
by calling
splice(index, count)
.
Returns the removed items.
Exceptions:
NoModificationAllowedError
template
is not null
(see ).
Modifies the list of children of this group by first removing
deleteCount
items from start
followed by
adding newItems
at the same point.
The operation of slice is based on ECMAScript 5's Array.prototype.splice.
Returns a sequence of the items removed from group during the removal step (regardless of whether these items were re-added during the addition step).
length
,
length
].
start
.
Negative values are clamped to zero, and all other values are
clamped such that
0 < start
+ deleteCount
≤
length.
start
.
Each item, if it already has a parent group (including this
group), is first removed from its parent group before being
added to this group.
Exceptions:
HierarchyRequestError
newItem
is the
timeline for a document (see ).
NoModificationAllowedError
template
is not null
(see ).
splice
to take a variadic list of items
rather than requiring a sequence.
The operation is identical to splice(unsigned long start,
unsigned long deleteCount, sequence<TimedItem>
newItems)
.
item
within the group.
If item
is not in the group, returns -1
.
Makes this group independent of the template with which it is associated if any. See . Does this recurse through children and unlink them too?
After this method returns, the template
property will be null
.
Returns the previous value of template
.
Animation
objects
that are in effect.
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
AnimationGroup
object when this method was
called.
Animation
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.
Parallel animation group run their children such they potentially play
simultaneously.
The start time of each child depends on its own startTime
property.
The intrinsic duration of a parallel animation group is
the maximum of the endTime
properties of each
child TimedItem
.
If the group has no children then the intrinsic duration is zero.
ParGroup
interface
Parallel animation groups are represented by ParGroup
objects.
A sequence of timed items to add as children of this group.
These children are appended in sequence using the semantics
as the AnimationGroup.add
method.
The timing properties of the new animation group.
If this parameter is a double
, then it
specifies the duration of a single iteration of the
animation group, that is, the iteration duration, in
seconds.
In this case, the timing
property of the new
ParGroup object is set to a new Timing
object constructed from a TimingDictionary object
with the duration
property set to the
double
value specified here, and all other
properties set to their default values.
Similarly, if this parameter is null
, the
timing
property is set to a new Timing
object constructed from a TimingDictionary object
with all properties set to their default values.
If this parameter is of type TimingDictionary, then
the passed-in dictionary is used to construct a new
Timing object which is then assigned to the
timing
property of the new ParGroup
object.
If this parameter is a Timing object, it is directly
assigned to the timing
property of the new
ParGroup object.
It is not copied.
In this way Timing objects can be shared between
TimedItem objects.
Finally, if this parameter is not specified, it is as if
a value of null
were specified.
null
, the animation group will not be appended
to any group.
The start time of the newly generated animation group expressed in seconds in the iteration time space of the AnimationGroup to which it is (or will be) appended.
If not specified, the current iteration time of the
AnimationGroup to which the animation is appended (as
determined by the parentGroup
parameter) will
be used.
If parentGroup
is null
, and
the startTime
is not specified, a time of zero
will be used.
Sequence animation groups run their children in turn following their order in the group. This ordering is achieved by adjusting the start time of each child in the group.
Since the start delay is added to the start time, it can be used to adjust the timing of the animation interval relative to calculated start time as shown in the following diagram.
A negative start delay can be used to cause the animation interval of two children to overlap. Note that the start delay affects the start time of subsequent children in the group.
The start time for the children of a sequence animation group is calculated according to the following procedure:
child.startTime
be
accumulated start time.
child.endTime
.
When animationDuration
is positive infinity the
behavior is defined by IEEE 754-2008.
As a result, if any of the children of a sequence animation group
has an infinite animation duration, any children that occur
later in the sequence will not play.
The instrinsic duration of a sequence animation group is equivalent to the start time of a hypothetical child appended to the group's children using the procedure described in .
SeqGroup
interface
Sequence animation groups are represented by SeqGroup
objects.
The Web Animations animation model takes the time fractions produced by the timing model for a given Animation and applies it as the input to the animation effect defined for the Animation object. The output of each animation effect is then combined using a global animation stack before being applied to the target property (see ).
The entry-point to the animation model is the AnimationEffect or CustomAnimationEffect object associated with each Animation. These objects describe how animation values should be calculated for the Animation for any given time. AnimationEffect serves as an abstract interface of which several concrete subclasses are provided.
AnimationEffect
interfaceThe operation used to composite this animation with the stack, as specified by one of the CompositeOperation enumeration values.
This value defaults to "replace"
The operation used to composite each iteration of this animation with the result of compositing the previous animation, as specified by one of the CompositeOperation constants defined in this interface.
This value defaults to "replace"
.
Define this here?
Potentially this should be an interface that script could
provide its own object for.
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.
timeFraction
would be a number typically in the
range [0.0, 1.0].
null
values indicate the function should no longer
affect the target.
Pass in isSeeking
as a flag?
Tempting to pass in aPreviousTimeFraction
but
I think functions that need that can track it themselves?
Although that requires a new instance of the function for each
target.
Creates and returns a new object of the same type as this object's most-derived interface such that it will produce the same output as this object.
We either need a more rigorous definition here or (probably better) a sets of steps on a per-subclass basis.
Creates an AnimationEffect representing the passed-in collection of properties.
Note that this method requires handling the passed in parameter in a manner not yet supported by Web IDL and hence this method is ECMAScript-specific.
Since accessing the properties of an ECMAScript user object can have side effects, the manner in which these properties is accessed is important. In light of this consideration the following procedure has the following properties:
The interpretation of the passed-in properties
object
can be described in three parts.
Part 1 – Determine the set of animation properties
properties
. For
each property in properties
, if
property also exists in supported
properties based on a case-sensitive comparison, append
property to animation properties.
Whilst the iteration order for properties of an ECMAScript object is implementation-dependent, the order here is not significant to the outcome as animation properties will be sorted before being iterated over.
How do we handle operation
and
compositeOperation
? We'd like to be able to
list them in the same property bag but that would mean we
could never animate properties of the same name.
Part 2 – Create the AnimationEffect objects
The AnimationEffect object produced depends on the length of animation properties as follows:
null
.
This behaviour of returning null
allows
alternative animation effects to be provided based on the
capabilities of the user agent as follows:
elem.animate( AnimationEffect.createFromProperties({ transform: 'translate(-100px)' }) || AnimationEffect.createFromProperties({ top: '-100px' }), 3);
properties.name
.properties.name
.Part 3 – Create each KeyframeAnimationEffect object
Based on a given name and value, a new KeyframeAnimationEffect is created as follows:
(DOMString or
sequence<(KeyframeDictionary or
DOMString)>)
,TypeError
as defined by [[!ECMA-262]].
KeyframeAnimationEffect(name,
value)
.
CompositeOperation
enumerationAnimationTemplate
containing this
AnimationEffect
.
KeyframeAnimationEffect
interface
Creates a new KeyframeAnimationEffect object for the specified property from the given list of keyframes.
The list of keyframes may be a sequence of
KeyframeDictionary dictionaries, a sequence of
DOMString
s, a combination of both, or
a single DOMString
.
DOMString
s are used to create keyframes with an
offset of 1.
When the list of keyframes is a sequence consisting entirely of
DOMString
s the offsets of the newly created
Keyframes are distributed evenly from 0 to 1.
The property, operation and compositeOperation arguments are assigned to the attributes of the same names.
The frames argument is processed as follows:
The processing of frames depends on its type as follows:
DOMString
,value
member is set to
frames and whose other members are set
to their default values.
effect.frames.add(frame)
.
(DOMString
or KeyframeDictionary)
,true
.DOMString
,value
member is set to
item and whose other members are
set to their default values.
Keyframe(item)
.
false
.
effect.frames.add(frame)
.
true
call
effect.frames.distribute()
.
KeyframeList
interfaceThe KeyframeList object is a collection of Keyframe objects sorted by the offset of each Keyframe.
index
if it exists or
null
otherwise.
Adds frame to the list such that the list remains sorted by the offset of the frames.
If frame is of type KeyframeDictionary then
a Keyframe object is first constructed by calling
Keyframe(frame)
before adding the
newly constructed Keyframe to the list.
If there already exists a frame in this list with offset
frame.offset
, the newly added
frame will appear in the list after the
already existing frames in the list with the same offset.
If frame is already part of another KeyframeList it is first removed from that list before being added to this list.
Exceptions:
IndexSizeError
null
is
returned.
-1
.
Adjusts the offsets of the frames in the list such that the offsets are spaced equidistantly whilst maintaining their current order and such that the first frame (when there are multiple frames) has offset 0 and the last frame (if any) has offset 1.
For frame at position i in the list where
0 ≤ i < length
, an offset will be
assigned equal to i / (length - 1)
unless
length
is 1 in which case it will be given offset
1.
After applying the changes, this list is returned.
Keyframe
interfaceA Keyframe represents a moment within an animation that has a specified value to be applied to the target property or attribute. In between such moments values may be interpolated or filled based on the TimingFunction specified on the TimedItem where the Keyframe is used, or on the previous Keyframe.
Currently a Keyframe can only target a single property which is defined on the KeyframeAnimationEffect. This is different to CSS. Is this something we want to change? It would complicate the API, of course, but is it worth it?
Creates a new Keyframe object using the parameters specified in dictionary.
dictionary.offset
is clamped to the
range [0, 1] before setting.
A value between 0 and 1 inclusive representing the offset within the iteration duration of the animation where this value should appear.
If this keyframe belongs to a KeyframeList, changes to this value cause the KeyframeList to be immediately re-sorted using a stable sort such that all children are ordered by their offset but children with identical offsets retain their relative position in the list.
Exceptions:
IndexSizeError
The timing function to apply between this keyframe and the next keyframe in any KeyframeList in which this object appears.
May be null
in which case linear interpolation will
be used.
KeyframeDictionary
dictionary
To simplify creation of Keyframe objects
a KeyframeDictionary
can be used.
The members of the dictionary correspond to attributes in the Keyframe interface which provides a more complete description of their meaning and usage.
PathAnimationEffect
interfaceGroupedAnimationEffect
interface
The GroupedAnimationEffect
interface represents a
set of animation effects that share the same
AnimationTemplate
parent.
If the group contains multiple effects that target the same property
does order in the group matter? If so, we need to add a means for
re-ordering the group other than popping and pushing. For example,
insertBefore
.
TBD whilst we decide whether we need this interface or whether we can merge it with KeyframeAnimationEffect somehow.
null
otherwise.
Appends effect
to the end of the group such that
group.indexOf(effect)
equals
group.length - 1
.
I'm assuming that AnimationEffects can be shared amongst animations and groups. Or does effect need to be removed from any previous AnimationEffects or Animations first?
index
and returns it. If
index is outside the range [0, length
), then
null
is returned.
-1
.
null
AnimationEffect still fires events.
If the current iteration is zero, or the
accumulateOperation is
"replace"
, then the
animation value is simply the iteration value, as
defined below.
When the animation time equals the iteration duration for the first time for an animation, the current animation value should be retained as the end value for the animation.
Need to revise this definition since we can't assume the time at the end of the first iteration will be visited (we might be playing backwards, or have a iteration start greater than 1).
If the current iteration is not zero and the
accumulateOperation is
"accumulate"
then the
animation value is the end value accumulated
current iteration times, with the
iteration value accumulated on top.
If the current iteration is not zero and the
accumulateOperation is
"merge"
then the
animation balue is the end value merged
with the iteration value, with an interpolation
parameter equal to the current time fraction
Need to review the following algorithms to check they still make sense now that the iteration fraction is not necessarily in the range [0, 1].
KeyframeAnimationEffect
When an AnimationTemplate
contains a pointer to a
KeyframeAnimationEffect
keyframes, the
animation value for that animation at given current time
t is calculated according to the following steps:
Keyframe
objects contained within the
KeyframeList
object stored in keyframes.frames
by their offset.
If there are no frames in the sorted frame list
then no animation occurs and the iteration value is
just the base value of the property being animated.
Keyframe
with an offset
larger than the time fraction is
encountered.
Animation
interface's timing attribute.
PathAnimationEffect
When an AnimationTemplate
contains a pointer to a
PathAnimationEffect
and rotate is set
to false, the animation value for that animation at
given current time t is the transform defined by a
translation equal to the location on the path at t.
When rotate is set to true, the animation value is the transform defined by the above translation followed by a rotation defined by the following process:
GroupedAnimationEffect
When an AnimationTemplate
contains a pointer to a
GroupedAnimationEffect
, the animation
value for that animation at given current time t
is calculated by following the procedure outlined in
,
treating the list of AnimationEffect
objects
contained within the GroupedAnimationEffect
as
the animation stack and using an initial animation value
of 0 for all simple properties and id for transform.
In some situations the animation effects provided by Web Animations
may be insufficient.
For example, the animation effects defined here are only able to
target certain CSS properties and DOM attributes.
They are unable, therefore, to modify the currentScale
property of an SVG element to smoothly zoom the viewport without
affecting the document content.
In such cases where the provided animation effects do not provided needed functionality, an animation effect defined by script may be used. Such animation effects receive a time fraction from the timing model and are responsible for producing the animation effect corresponding to the specified time.
Using an animation effect defined in script it is possible to animate not only previously un-animatable attributes and properties, but potentially anything that is accessible via script, including even producing audio or creating vibrations.
For example, using an animation effect that draws to a canvas
element it is possible to produce a complex animated effect
featuring patterns that may be difficult to create using CSS or
SVG.
Compared to using the WindowAnimationTiming
interface, this approach ensures the animation is frame-rate
independent and can be paused, reversed, eased with timing effects,
accelerated, synchronized with other animations, and be controlled
in the same manner as any other Web Animations animation without any
additional programming.
I think we want two types of custom animation effects. Following is the general-purpose animate-anything kind of effect. The other type, which is not defined here, is the one that can participate in the animation sandwich just like any other.
This, second, native-like animation effect, would have the following features:
AnimationEffect.sample
That's a bit more difficult since you have to be careful that the custom effect doesn't do anything naughty while you're in the middle of compositing the animation sandwich. I think we should postpone this until Web Animations 2.
Custom animation effects allow authors to define animation effects using script. Such animation effects are not limited to a single CSS property or DOM attribute and therefore the steps for assessing their order of execution differs from regular animation effects.
Custom animation effects are executed after all other AnimationEffect objects have completed and applied their effects to their targets.
Need to define this more precisely. Are styles flushed? Presumably they are. Can we suspend reflow for the duration of executing the script-based animation effects and just do it once afterwards?
Within the set of custom animation effects, the order of execution
is mostly the same as that for other animation effects and is
defined in .
However, custom animation effects may also override this
ordering through the priority
attribute, which, if
defined, specifies the priority of the effects with lower numbers
are executed sooner.
In deciding which of two CustomAnimationEffect objects, A and B, should be executed the following rules are applied.
priority
such that lower priorities are sorted
first.
If either does not have a defined priority
,
then treat the priority as being positive infinity for the
purposes of sorting.startTime
s of the
TimedItems with which A and B are
associated such that earlier start times are sorted first.
Items sorted earlier, are executed first.
That last point is quite wrong. I don't think we've specified exactly what script order is in . But I wonder if it makes more sense to effectively enumerate all the script-based nodes in the animation tree using some well-defined order and use that index. Can that be done efficiently?
CustomAnimationEffect
callback interfaceCustom animation effects can be defined in script using the CustomAnimationEffect interface.
Should this be a dictionary?
I'd like to make clone
optional.
null
, the callback object SHOULD
remove the animation effect.
Animation.targetElement
Do we need to pass in the TimedItem as well? If possible I'd prefer not to but it may necessary for some types of effect. Might be an additional parameter to add later if it proves necessary?
I think we will also need to pass the previous time fraction. Animations will often use this to check if they are paused, playing in reverse etc. They can track this themselves but then you need a separate object everywhere you use it.
Write me
Write me
Write me
Write me
When multiple in effect animations target the same element, the animations are ordered into a stack which resolves how those animations combine. This stack is sorted by animation start time. Where multiple animations have the same start time, those animations are sorted in document order (for animations from the DOM) and script order (for animations from the API). Script animations are always sorted after DOM animations.
Other operations also generate animation stacks - for example,
grouping multiple animations using a
GroupedAnimationEffect
.
An animation stack may be thought of as a single stack with all animations sorted into it, or a stack per animating element, as animations on one element cannot effect the course of animations on another element.
The start time of an animation refers to the time when the animation
is specified to begin as recorded in the
TimedItem.startTime
property, that is, before applying
any start delay.
The stacking order of animations is independent of the current play direction of individual animations and animation groups.
In order to resolve an animation stack, an initial animation
value is required for each element and property animated by the
stack.
To calculate a current animation value for elements and properties
from an animation stack, an animation value is generated for each
animation in the stack
(see ).
The cumulative animation result for each element and property is first
initialised to the relevant initial animation value.
Starting at the bottom of the stack (i.e. earliest start time) and
working up, as animation results are encountered for an element
and property, these are merged into the cumulative animation result
using the animation combinator stored in the
AnimationEffect
that generated the result.
Once all animation results in the stack are processed, the resulting
cumulative animation values are the current animation values for
each property of each element that is animated.
When an animation applies to a target and property that animations earlier in the animation stack have already applied to, the cumulative animation result from the stack is composited with the new animation to produce a new cumulative animation result. The possible combinators are defined by the CompositeOperation enumeration.
When an animation a
is composited over a cumulative
animation result c
using the REPLACE combinator, the
new cumulative animation result is always a
.
When a path animation a
is composited over a
cumulative animation result c
using the ACCUMULATE
combinator, the effective transform of a
is calculated.
This transform is then post-multiplied to the cumulative animation
result to generate a new cumulative animation result.
When a transform animation a
is composited over a
cumulative animation result c
using the ACCUMULATE
combinator, a
is post-multiplied to the cumulative
animation result to generate a new cumulative animation result.
When a simple animation (i.e. an animation which is not a path
animation nor a transform animation) a
is composited
over a cumulative animation result c
using the
ACCUMULATE combinator, the new cumulative animation result is the
sum of a
and c
, clipped if necessary to
the appropriate domain.
All MERGE operators are governed by an interpolation parameter
p
that is calculated as the ratio of
(currentTime - parent.startTime)
/ parent.animationDuration
, where parent
is the
AnimationTemplate
which references the
AnimationEffect
that is being composited.
When a path animation a
is composited over a
cumulative animation result c
using the MERGE
combinator, the effective transform of a
is
calculated.
This transform is then interpolated with c
using the
rules provided in [[!CSS3-2D-TRANSFORMS]] to provide the new
cumulative animation result.
When a transform animation a
is composited over a
cumulative animation result c
using the MERGE
combinator, a
is interpolated with c
using the rules provide in [[!CSS3-2D-TRANSFORMS]] to provide the
new cumulative animation result.
When a simple animation a
is composited over a
cumulative animation result c
using the MERGE
combinator, the new cumulative animation result is calculated as
the weighted sum of a
and c
, with weights
of (1-p)
and p
respectively.
The current value of a given property and object is the value generated for that property by computing a current style for that object without taking the override stylesheet into account.
The override stylesheet contains output animation values and acts with a higher priority than all other stylesheets. However, !important rules from all other stylesheets act with a higher priority than the override stylesheet. The override stylesheet is regenerated for each timepoint in a document using the process described below.
We already have a section with the heading "animation values". Ideally they should be unique so they're easy to target by named anchor.
Animation values for all animated properties are generated at each time point according to the following process, then inserted into the override stylesheet.
Do we need to work out how to use the override stylesheet for elements that don't have an id but are targetted for animations?
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:
templatize
to create a new
AnimationTemplate with properties set to reflect the current
state of the Animation object on which it is called.Provide some javascript sample here demonstrating?
AnimationTemplate
interface
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.?
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 .
Element
to be targetted.
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.
Creates a series of independent TimedItem
objects, one for each element in target
.
As with animate(Element 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 .
Node
s to be animated.
Any nodes in the sequence that are not of type
ELEMENT_NODE
will be ignored.
animate(Element target, optional double
startTime)
.
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.
animate
.
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.
animateWithParent(Element target,
AnimationGroup? parentGroup,
optional double startTime)
except
one TimedItem
is created for each
Node
in target
that is of type
ELEMENT_NODE
.
animate
with the exception that the
TimedItem
objects generated by this method are
live.
animate
with the exception that the
TimedItem
objects generated by this method are
live.
animateWithParent
with the exception that the
animations generated by this method are live.
animateWithParent
with the exception that the
animations generated by this method are live.
AnimationTemplate
The procedure for instantiating an AnimationTemplate
,
template, given a list of target elements and an optional
parent group, is as follows:
Animation
objects.
Animation
object, anim.
timing
and effect
properties of anim to copies
template.timing
and
template.effect
.
element.ownerDocument.animationTimeline
.
Animation
objects.
Animation
objects.
As with animations, templates can also be created for animation groups.
Lots of questions here about how this should work.
AnimationGroupTemplate
interfaceTBD
index
.
If index
is greater than or equal to
length
returns null
.
Replaces the item at index
with
newItem
by calling
splice(index, 1, newItem)
.
Returns newItem
.
The behavior of this method is identical to
the equivalent setter in AnimationGroup
except that DOMExceptions of type HierarchyRequestError are not
thrown.
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]
.
Removes the item(s) at index
by calling
splice(index, count)
.
Returns the removed items.
Modifies the list of children of this group by first removing
deleteCount
items from start
followed by
adding newItems
at the same point.
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.
length
,
length
].
start
.
Negative values are clamped to zero, and all other values are
clamped such that
0 < start
+ deleteCount
≤
length.
start
.
Each item, if it already has a parent group (including this
group), is first removed from its parent group before being
added to this group.
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)
.
item
within the group.
If item
is not in the group, returns -1
.
ParGroupTemplate
interfaceParGroup objects can be created from ParGroupTemplate objects.
SeqGroupTemplate
interfaceSeqGroup objects can be created from SeqGroupTemplate objects.
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.
AnimationGroup
objects.
AnimationGroup
object,
group.
timing
property of group to
a copy of template.timing
.
child.animateWithParent(element,
group, startTime
)
or
child.animateLiveWithParent(element,
group, startTime
)
depending on
whether this procedure was invoked with animate
or
animateLive
.
element.ownerDocument.animationTimeline
.
AnimationGroup
objects.
AnimationGroup
objects.
MediaItem
interface
Need to consider how to align with existing
TransitionEvent
s, AnimationEvent
s and
TimeEvent
s. 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. It's probably important that we don't dispatch intermediate events during a seek as then implementations will be required to step through each interval and it breaks the fact that skipping around the model is constant time. It's also not what SVG does, i.e. SVG skips events during a seek. I think you just want to record the animating vs non-animating state of each item before the seek, then compare to after the seek and fire the appropriate event. (Do groups fire events?)
The proposal to-date is as follows. Requirements for dispatch of animation events:
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:
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:
Need to describe how to interpolate/add both:
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.
The Media Fragments specification [[MEDIA-FRAGMENTS]] defines a means for addressing a temporal range of a media resource. For resources containing content animated with Web Animations the application of the temporal parameters is as follows:
If a begin time is specified, perform a seek on the document timeline passing in the specified begin time as the seek time (see ).
Note that seeking behavior is well-defined even when the document
has yet to start and hence the seek can be performed regardless of
the setting of the timelineStart
property (see ).
What should be the behavior here? Should we pause on unload/pagehide? What does video do?
TBD. If we have triggers represented in the API, need a way to make these available to accessibility agents.
TBD. Need a way to expose speed control etc.?
TBD. Describe how to integrate with the Timed Text API and give examples of how to author content so it is accessible.
If implementations are required to preserve all state associated with animations then the resources required by an animation could continue to increase without limit over time. For long-running animations, and particularly those where animations are added dynamically, this could lead to degraded performance and eventual failure.
I'm not sure how to define this. We could say all animations with
end time converted to document time <
current document time - 5min
can be discarded.
That's fine, but what about crazy documents that put 1 million animations in a 5min span? Just leave that up to the browser. Also, what about mobile clients, is 5 min too long? Is this too prescriptive?
Maybe, just make some suggestions (e.g. 1 min for mobile clients, 5 min for desktop?) and then define an exception to throw if there is a seek to some time outside that window.
Also, note that defining this in terms of past intervals is direction-specific but that's probably ok since most long-running animations will run forwards.