Documentation and design notes for transitionAnimation.js

The documentation is in a separate file because we ship transitionAnimation.js as part of the SDK, so any comments there are visible to the public.

Transitions vs Animations

Animations do not affect the persistent properties of an object. When the animation completes, the object's properties return to their original values. Some animations base their initial properties on the object's current properties. For example, enterPage animates an element's position by first displacing it by an offset, and then gradually moving it to its normal position. To avoid discontinuity, you should set the object's final properties to match the final properties of the animation.

Transitions change an object's properties from an old value to a new value. For example, a reposition transition visually moves an object from one position to another.

Animations temporarily alter an object's current properties, and eventually return them to their original values. For example, an enterPage animation takes an element on the screen, temporarily offsets its position, then gradually animates the element back to its original position.

The distinction between animations and transition is that animations could in theory all be removed from the application without any change to the logical behavior of the program. (The object still appears; it just doesn't look as pretty.) On the other hand, transitions represent state changes and would have to be replaced with some other way of changing the visual state. (The object needs to move from one place to another regardless.)

A large drawback with animations are that they require keyframes defined in css. For animations which have static keyframes we shipped them hard coded in css. However other ones we create by creating a style tag. In Windows Blue the performance hit of creating a style tag is much larger because it is impacted by the number of elements and in Blue the apps are more complex and also the ListView control does not virtualize the win-container elements. We added an options argument to the showEdgeUI and hideEdgeUI animations so that we can use transitions instead of animations due to this performance impact.

Characteristics common to both transitions and animations

From a programmatic standpoint, transitions and animations are very similar. For the purpose of this document, we will use the word action to mean "transition or animation".

Characteristic Transition Animation
Lifetime -ms-transition-property -ms-animation-name
Target property -ms-transition-property @keyframe
Target values implied @keyframe
Initial delay -ms-transition-delay -ms-animation-delay
Duration -ms-transition-duration -ms-animation-duration
Curve -ms-transition-timing-function -ms-animation-timing-function
Direction always animates forward -ms-animation-direction
Iteration Executes only once -ms-animation-iteration-count
Persistence always persistent -ms-animation-fill-mode (sort-of)
Completion event transitionend animationend

All of the properties are comma-separated lists, corresponding to respective actions. For example, to apply three actions to an element, set each of the properties to three values, separated by commas. Rules exist if one list is shorter than another; we will not take advantage of those rules, although we need to guard against the possibility that the host application may have.

(Note that the items in the list are reported by Internet Explorer as separated by a comma and a space. This is important to bear in mind when trying to parse the list back out.)

For both transitions and animations, the properties other than the lifetime property are captured at the moment the action becomes active. After the action becomes active, changes to properties other than the lifetime property are ignored. This capture behavior is essential to our design.

The W3C specifies that script-applied actions become active when the style is resolved. (Earlier versions of the specification were unclear.) See Resolving styles for further discussion of style resolution.

Our overall algorithm for applying actions is as follows. Detailed discussion of each step follows the outline.

It is important to append the new values to the existing properties, because the W3C says that in case of conflict, the last one wins. We set the new values via the shorthand property, because that solves multiple problems:

Note that you cannot read the action shorthand property: In the case where the non-lifetime action properties have the wrong number of commas, the shorthand property reads back as an empty string. (This behavior is required by the CSS specification.) Instead, we read the action-lifetime property and use just the names without any of the additional properties. This works because of the capture behavior described above: The parameters of the existing actions were already captured, so the values we provide here are irrelevant.

For example, consider the following properties. Observe that although there are two elements in the lifetime property, there are three in the duration (too many), only one in the timing-function (too few), and two in the delay property (just right).

Property Value
lifetime "a, b"
duration "0ms, 300ms, 500ms"
timing-function "ease-in"
delay "0ms, 0ms"

The CSS specification describes what happens when there are missing or extra parameters. The details do not affect us.

Suppose we want to start a "c" action with 100ms duration, linear timing, and no delay; and we also want to start a "d" action with 100ms duration, linear timing, and 100ms delay. We first read the lifetime property, then append the shorthand description of the new action:

style.lifetime = "a, b, c 100ms linear 0ms, d 100ms linear 100ms"

The browser parses this and sets multiple properties based on the shorthand:

Property Value
lifetime "a, b, c, d"
duration "0ms, 0ms, 100ms, 100ms"
timing-function "ease, ease, linear, linear"
delay "0ms, 0ms, 0ms, 100ms"

Providing a name with no auxiliary properties is legal shorthand; it means that all other properties receive default values. (Which are ignored, since the action is proceeding with the captured values.)

The first animation to complete is "c", and we clean it up by deleting its name from the lifetime property.

Property Value
lifetime "a, b, d"
duration "0ms, 0ms, 100ms, 100ms"
timing-function "ease, ease, linear, linear"
delay "0ms, 0ms, 0ms, 100ms"

Note that the other properties remain unchanged, but that's okay, because their values were captured when the animation started.

When the "d" animation completes, we realize that this is the last PVL animation, so we restore all the properties to their original values.

Property Value
lifetime "a, b"
duration "0ms, 300ms, 500ms"
timing-function "ease-in"
delay "0ms, 0ms"

Ignoring the non-lifetime properties during the action is done as a performance optimization. Their values have no effect (since the original values were captured when the action started), so the work of restoring them is delayed until the animation completes so that the initial animation can get off the ground faster. It does however create the new rule that the application may not alter animation or transition properties while a WinJS animation is in progress.

When removing transitions, we remove the last matching item, so that we do not accidentally damage any values set by the application, because we always append our values.

For all actions, we listen on the completion event to know when that action has completed, and also create a watchdog timer to fire shortly after the expected completion of the action to handle the case where the action is abandoned without firing a completion. See Watchdog timeouts for further discussion.

All actions register their presence in the activeActions array, which is an array indexed by the combination of the element's uniqueID and the the name of the property being animated. (Separated by a |, because that character is illegal in property names, so it can safely be used as a separator.)

An action registers with a callback function to call to stop the action. This is called when a conflicting action is scheduled, so that the previous action can clean up and get out of the way before the new action starts modifying element properties. (Two actions are said to conflict if they attempt to animate the same property on the same element.) See Finishing due to interruption for further discussion.

If an array of actions or an array of elements is passed, then each combination of action+element gets its own listener and watchdog. All the corresponding Promises are joined together to become the final Promise of the combined action. Using the Promise.join method allows us to simplify our logic by allowing us to focus on the one action + one element scenario.

Forcing layout

In Internet Explorer, layout is the act of applying the effects of all changes to the DOM that have taken place.

When you change the DOM, such as inserting or deleting an element or changing an element property, Internet Explorer flags the DOM as "dirty". When control returns to the Internet Explorer event loop, Internet Explorer checks if the DOM is dirty, and if so, it performs a layout pass, which takes all the changes and makes them take effect.

Forcing layout means forcing Internet Explorer to perform a layout pass outside of the event loop.

The PVL needs a layout pass to take place after the DOM has been edited by the application so that the objects move to their final positions, at which point it can accurately calculate the distance the elements have moved.

It turns out that Internet Explorer automatically forces layout if you try to read the offsetTop or offsetLeft properties when the layout is dirty. Therefore, we do not need to force layout explicitly; merely reading offsetTop and offsetLeft is enough to trigger layout.

Resolving styles

In Internet Explorer, styles are resolved when the browser recognizes the value and takes action on it. Styles are resolved as part of the layout process, but you can also force styles to resolve outside of layout.

Since action is taken only when styles are resolved, if you change a property multiple times in a row without an intervening style resolution, then only the final value will have any effect.

The "intermediate writes are ignored" optimization does not cause a problem with most properties, but it affects transitions and animations because transitions and animations capture the values of properties at the point they are triggered, so we have to make sure the properties have the desired values at the point of triggering. If we update a property, then return without resolving styles, then it's possible that the property will change again before the next time styles are resolved (say, by another call to the PVL), resulting in the desired changes from the first animation not taking effect.

Note that the concept of style resolution is not part of the W3C CSS3 specification, which merely leaves to implementation free to decide what the requirements are for a change to a property to be counted as having taken place before, after, or simultaneous to another property change.

In Internet Explorer, resolving any style on an element resolves all styles for that element, so we can combine multiple style-resolve operations into one if all of the modified styles apply to the same element.

Event handler gotchas

All event handlers are registered on the document rather than on the element itself, because an element whose disabled property is set to true does not receive events. Instead, the event bypasses the event and immediately bubbles to the parent.

This quirk is not documented anywhere that I can find.

Watchdog timeouts

Both transitions and animations use watchdog timeouts to catch abandonment scenarios. The watchdog timeout is set to the expected end of the action, plus a little extra time to ensure that when the action completes normally, the completion event fires ahead of the watchdog.

Scheduling the watchdog timeout is tricky because setTimeout starts the timer immediately, even though the animation timer doesn't start until the next render pass. (This is technically a violation of the CSS specification, which says that the timer begins at the time the style is changed.) If we had scheduled the watchdog timer with a normal setTimeout, then a long render pass (which can happen for complex animations) can result in the watchdog timer firing prematurely.

Therefore, we schedule the watchdog timer as a series of timeouts. The first timeout is for a fixed small amount of time. This guarantees that the first timeout will not expire until the first render pass is complete and the animation has started. At that point, we can schedule the real timeout, and this one will not fire prematurely.

Completion

Since we will sometimes cancel old actions while setting up new ones, we are careful to fulfill Promises asynchronously by calling WinJS.Utilities.Scheduler.schedule(c) rather than c(). Otherwise, the application's completion handler might try to schedule a third action while we are still trying to set up the second one, resulting in reentrancy and confusion.

The exception to this rule is in the case where the Promise is being canceled (see below). In that case, we complete the Promise synchronously. This will not create reentrancy because the cancellation was initiated by the application in the first place. Any cancellations performed by the Animation Engine are done via internal mechanisms and are still completed asynchronously.

Cancellation

When you create a Promise, you can optionally pass a cancellation callback function which is invoked when the application invokes the cancel method on the Promise. Upon cancellation, the Promise enters the error state, and the error handler is called.

Animations don't want to work that way. When the application invokes Promise.cancel, the animation should jump to its end state and complete immediately with success.

We have confirmed with the base team that it is supported and designed behavior that if a cancellation function calls the completion callback before returning, then the Promise will enter the fulfilled state rather than the error state. (This is why cancellations complete synchronously: So that we can complete the Promise during the cancellation callback and cause the Promise to complete with success rather than error.)

Therefore, whenever we create a Promise, we register a cancellation callback which cleans up the animation (thereby jumping to the end state) and synchronously invokes the cancellation function.

Optimization notes

Programmatic access to CSS styles

Styles on an element are stored in the element.style object, and calculated styles are obtained by calling window.getComputedStyle. There are three ways of accessing styles from these objects. Here they are with their relative cost, where indexing is normalized to have a cost of 1.

Technique x = element.style x = getComputedStyle(element)
x.getAttribute(propName) 10.82 6.97
x[propName] 1.00 1.00
x.opacity 0.95 1.00

One theory is that calling getAttribute is slow because we end up making two calls into the DOM. First, we have to ask the DOM for the function pointer for the getAttribute method. Then we have to take that raw function pointer and convert it to a JavaScript function object. Those two steps together are already 2½ to 3× slower than indexing. Next, we have to call the JavaScript function object with the two parameters, which means unwrapping the object back into the raw function pointer. That last step takes 4½ to 7× as long as indexing.

At any rate, it's clear that indexing is the way to access attributes from style objects. Switching from getAttribute/setAttribute to indexing improved time-to-first-frame by 5%.

If you're going to be manipulating styles a lot, you should cache the style object in a local variable to avoid having to hit the DOM each time you type element.style.

Parsing transitions

The translation portion of a CSS transform matrix is in positions e and f in the "matrix(a, b, c, d, e, f)" value. (If there is no transform, then the string returned for transform is "none".)

The algorithm for extracting the e and f from the matrix string splits the string on commas, and if six fields are produced, it extracts fields 4 and 5 (counting from zero). There will not be six fields if the string is "none", but in that case, the effective transform is the identity, so there is no translation. Note that we take advantage of the fact that parseFloat ignores trailing garbage, because field 5 will have a trailing close-parenthesis.

You might think that a more efficient algorithm would be

var matrix = new MSCSSMatrix(window.getComputedStyle(e).transform);
left += matrix.e;
top += matrix.f;

It turns out that it's three times faster to do the parsing entirely in native JavaScript (which can be optimized) rather than calling into the DOM four times (once to construct the MSCSSMatrix, once to retrieve e, once to retrieve f, and once to destruct the matrix).

Bulk style updates

Multiple styles can be updated at once by appending to the cssText property:

element.style.opacity = 1;
element.style.backgroundColor = "red";

// versus

element.style.cssText += "; opacity: 1; background-color: red";

The leading semicolon is important so that the new CSS attributes are properly separated from the previous ones.

Although this would appear to be an improvement since it reduces the number of round-trips to the DOM, no measureable improvement was observed in practice because of the cost of generating the original value of cssText. The benefit appears to be dependent on the complexity of the existing styles on the object.

At any rate, we cannot use this technique even if it did provide an improvement because the Internet Explorer implementation of cssText does not properly return properties whose values contain an at-sign, semicolon, or quotation-mark. (The at-sign is used in font names to indicate that the font is vertically-oriented.) This bug in Internet Explorer is marked Won't Fix. Since we allow animations to be triggered on arbitrary application-provided elements, we cannot guarantee that the application's styles avoid the dangerous characters.

Shorthand properties

As noted in the opening discussion, shorthand properties are not reliable for reading, but they work just fine for writing. (Though there are problems if a name contains an escaped reserved character, but those problems existed even in the lifetime-property itself, so we're not introducing any new issues here.)

Using shorthand properties for writing saves a number of round trips into the DOM.

Disabled elements

DOM events do not propagate to disabled elements. The AppBar control will sometimes animate disabled elements (for example, disabled buttons will animate into view).

The workaround is to register the listener on the document root, which remains enabled. However, doing this unconditionally is expensive because it generates a lot of listener traffic which we end up just ignoring. Therefore, we move the registration to the document root only if the element is disabled.

If the element is enabled at the time the action begins, but becomes disabled during the course of the action, then the DOM completion event will not be delivered to the listener. The watchdog timer catches this case and ensurse that the Promise completes eventually.

Finishing due to interruption

It turns out that DOM events are expensive. Therefore, we avoid them whenever possible.

We use the activeActions array to keep track of all the actions that we have scheduled which are still active. When an actions begins, it registers with the array, and when it completes, it unregisters. This is a native JavaScript object and therefore can be manipulated without going through the DOM.

As part of setting up an action, we check if there is already an active action on the same element and property. If so, we finish the old action before replacing its registration with the new action.

By having our own table of active actions, we don't need to listen to the transitionstart event to detect stoppage due to interruption: We stop the action explicitly when the interrupting action is scheduled, rather than relying on the DOM event to notify us. (It also is important in the case of an interrupted Animation, because an interrupted animation does not fire an animationend event at all.)

It also allows us to stop actions on disabled elements. As noted above, native DOM events do not propagate to disabled elements.

The style cache

To reduces round trips to the DOM, we retain a private cache of what we think the DOM styles are and use that cache instead of accessing the DOM directly. The cache is obviously write-through, because we want our changes to take effect, but it avoids DOM accesses on reads.

Miscellaneous failed optimizations

None of these optimizations led to measurable improvements.

These failed optimizations can be explained by the first-order approximation that the cost of the PVL is dominated by calls into the DOM, and none of the above changes affected the number of calls into the DOM.

Transitions

The responsibility for managing transitions is broken into two portions. The high-level animations.js file (the animation library) deals with calculating the necessary transforms, and the low-level transitionAnimation.js file (the animation engine) deals with actually applying them.

The first part is an interaction between the application and the animation library.

The second part is handled by the low-level animation engine.

Let's walk through the above steps with an example. consider an object that starts out at position left = 100.

The remainder of this section discusses the details of the algorithm.

Capturing the current position of the element

The position of an element may be animating at the time the transition is applied. We must capture the in-flight value of the position.

The current layout position can be obtained from the offsetLeft and offsetTop properties.

The tricky part is that we also want to incorporate any translation transform that may be active on the element. To do this, we use window.getComputedStyle(element).transform and extract the x- and y- coordinates from the transformation matrix. Note that the in-flight position is relevant only when capturing the original positions. Final positions need to capture only the layout position, since we are going to be changing the transform ourselves.

Resolving the current style before animating

It is not well-known that the browser does not respond to a change in a CSS value until the style is resolved. (See Resolving styles above.) As a result, many developers will write code like this

// incoming's initial opacity is 1.
// Set it to 0 so it will fade in.
incoming.style.opacity = 0;
WinJS.Animations.crossFade(incoming, outgoing);

Unless special action is taken, this code will not actually animate from opacity 0 to 1, because the starting opacity of 0 was never resolved before the animation engine changed it to 1. As a result, the browser will see that the opacity changed from 1 to 1 and perform no animation.

To cover for this type of mistake, the animation library will automatically resolve the initial CSS property values so that the above code will work.

Since this workaround already is present, we may as well take advantage of it in the animation library: After applying the counteracting transform to each element that moved, we would normally resolve the style so that the initial transform is not animated, and so that it will be taken as the starting point of the animation. But we can skip that step and let the animation engine do the resolve.

Applying the initial transform without animation

When the animation library applies the initial counteracting transform, it does so without animation because our overall design removes the transition properties as soon as the transition begins. Therefore, there are no leftover transition properties that would cause the initial transform to be animated.

After setting the initial transforms, we resolve the transform style so that the browser observes the initial value before we start enabling transitions.

Edge case for transitions

Transitions are triggered by changes to element properties. If the target element's property has a value equal to our desired transition target, then no transition takes place, and consequently, transition events do not fire. We detect this case and convert the transition to a simple timeout.

Note that Internet Explorer returns values that may be different from the value used to set the property. Examples:

Internet Explorer is inconsistent as to whether changes to equivalent values are considered changes for the purpose of triggering transitions. In the above examples, changing the transform to an equivalent translation does trigger a transition, whereas the change the equivalent opacity does not trigger a transition.

To avoid surprises, we ask Internet Explorer to munge the value so we can see how it looks after it has been internalized and spit back out. To do this, we create a temporary element (never added to the document, so it does not trigger layout) and set the property on the temporary element, then read it back, and use that result as the "canonical form" for the property value. (If for some reason, Internet Explorer is inconsistent with its canonical forms, the watchdog timeout will save us.)

Interrupted transitions

If the animating property is modified, either by the application directly, or because the application requested a conflicting transition, no transitionend event is fired. From the browser's point of view, the transition is still in progress; it merely got redirected. The transitionend event fires only when the new transition completes.

For the case where a conflicting action is requested by the application, we manually fire the cleanup callback so that the previous transition will clean up and get out of the way.

Note that if an application attempts to apply a transition to an element without using the animation engine, the animation engine will not detect the interruption. We do not consider this a primary scenario, since applications should be using the animation library for all animations. This will instead be treated as an Abandonment scenario.

Abandonment

For the purpose of this document, we shall use the term abandonment to refer to situations in which the final transitionend event does not fire, and the interruption was not detected when the animation engine scheduled a conflicting action.

Transition redirection

As we noted above, if a new value is set for the property undergoing transition, the existing transition is considered to have been redirected.

Transition removal

According to the CSS3 Transition spec, section 5:

In the case where a transition is removed before completion, such as if the transition-property is removed, then the [transitionend] event will not fire.

The example given refers to this scenario:

The act of setting -ms-transition-property to "opacity" causes the transform transition to become abandoned, since "transform" is no longer set.

We are careful never to remove properties from the -ms-transition-property that we did not put there ourselves. If there are multiple transitions on the same property, the property name appears twice in the list, so that removal of the old transition will still leave the property name in the list and not result in abandonment of the new transition. The only time this problem will occur is the application removed the transition.

One way of detecting abandonment is to register a listener on all attribute changes, and detect that somebody has deleted "transform" from the transition-property attribute. We will not do this, however, because the problem is fixed by the more general solution below.

Element removal

Another case where transitions can become abandoned is if the element is removed from the document. There is a W3C DOM event DOMNodeRemovedFromDocument which fires when this occurs, but Internet Explorer does not support this event.

The watchdog timer

We handle transition abandonment generally by scheduling a timeout on the element which fires slightly after the anticipated completion of the transition. The timeout function completes the transition promise if it has not yet completed.

The timeout is canceled as part of completion.

See Watchdog timeouts for further discussion.

Animations

Interrupted animations

Animations behave better with respect to interruptions than transitions. If an animation is interrupted, its animationend event still fires.

Abandoned animations

Animations can become abandoned if the keyframe is deleted from the -ms-animation-name property by the application, or if the element is destroyed. In these cases case, no animationend event is fired.

We protect against this the same way we protected against abandoned transitions: By creating a watchdog timeout that fires if the animationend event did not fire as expected. When the watchdog timeout fires, we assume that the animation was abandoned and perform our cleanup. See Watchdog timeouts for further discussion.

Animation fill

During the delay before the start of the animation, and after the animation completes, the element's properties are controlled by their actual styles rather than the keyframe. This behavior can be altered by the standard-proposed animation-fill-mode property.

Fill mode Description
none (default) properties are not affected outside the animation timeline
backwards after the animation is applied and before the animation starts, the properties have the value of the keyframe's initial value
forwards after the animation ends and until the animation is removed, the properties have the value of the keyframe's final value
both combine backwards and forwards

The animation-fill-mode is a comma-separated list of values, each one applying respectively to the corresponding comma-separated keyframe name. Like the other animation properties, it is captured when the animation begins.

This property permits us to let the animation "hold" the initial and final values outside of the keyframe.

Note, however, that the effect of the animation-fill-mode extends only as long as the animation remains applied to the element. This creates a problem for staggered animations, because each individual participant in the animation will complete at a different time, but the promise returned by the animation library does not fire until all participating elements have finished animating. Each individual participant does not know when the other participants will complete, so it will not know how long to delay clearing the keyframe. This gets particularly complicated when multiple animations are combined, since there is no way for a Promise to know that it has been joined or chained to another Promise.

Solving this problem would require applications who compose animations (via chaining or joining) to communicate the "final completion" back into the animation engine so it can perform final cleanup. This is problematic, so we solve the problem by punting: If the application wants the object to retain its final property, it needs to set that property prior to triggering the animation.

e.style.opacity = 0; // NEW
WinJS.UI.Animation.fadeOut(e).then(function() { e.parentNode.removeChild(e); });

Although cumbersome, this approach does have the following advantages:

We may want to change fadeIn and friends from animations to transitions, so that the final opacity sticks at the end rather than reverting. This does introduce a breaking change, but perhaps the scope of the break is not significant, since people are likely to expect the opacity to be updated anyway.

Globally enabling and disabling the PVL

The enableAnimations and disableAnimations methods allow an application to influence whether the PVL performs animations. The isAnimationEnabled method allows an application to query whether the PVL will perform an animation. This allows an application to enable or disable its manual animations in sync with the PVL.

The animation enableCount starts at zero.

Calling enableAnimations increments the enableCount, and calling disableAnimations decrements it.

The PVL uses this algorithm to determine whether animations are performed:

This model permits the following usage patterns:

Useful helper functions would be

function withoutAnimation(f) {
 try {
   WinJS.UI.disableAnimations();
   return f();
 } finally {
   WinJS.UI.enableAnimations();
 }
}

function forceAnimation(f) {
 try {
   WinJS.UI.enableAnimations();
   return f();
 } finally {
   WinJS.UI.disableAnimations();
 }
}

withoutAnimation(function() { neverAnimates(); });
forceAnimation(function() { alwaysAnimates(); });

Compensating for time-to-first-frame issues in Internet Explorer

To give Internet Explorer more time to set up animations and transitions, an artificial delay of 34ms (two frames) is added to all actions scheduled by the animation engine. Clients can opt out of the artificial delay by setting exactTiming: true in their action descriptor.

The 34ms delay was originally added in changelist 580919 for Windows 8 bug 900769. Windows 8 had a library delay of 34ms (two frames). Improvements in IE in Windows 8.1 removed the need for this delay, and the default delay is now zero.

Components which trigger CSS animations and transitions manually can retrieve the the animation engine delay from the WinJS.UI._libraryDelay property. This returns the delay in milliseconds.

The WinJS.UI._libraryDelay is writeable, and changes take effect for all future animations and transitions. (Animations/transitions in progress are not affected.)

Animation engine API

Dynamic action properties

Any member of the Animation or Transition object can be set to a function rather than a fixed value. In that case, the function is called for each element, and its return value is used as the corresponding value of the Animation or Transition for that element. Using a function allows a caller to provide a different action for each element. For example, this technique is used to implement stagger animations, and to allow animating objects to move to different final positions.

If you provide a function, it is called with the following parameters:

Parameter Type Description
i number zero-based index of the animating element's group
e DOM element the element being animated

For example, if the target elements for an animation or transition are passed as [a, [b, c], d], then the callback function will be called four times:

The first parameter to the callback is 1 for both b and c since they belong to the same animating group.

Animation object

An Animation object describes the properties of an animation.

Member Type Description
keyframe string (optional) the predefined keyframe to use for this animation.
property string the property to animate. "transform" and "opacity" are the only ones used by the PVL.
delay number delay before the animation begins, in milliseconds
duration number animation duration, in milliseconds
timing string CSS timing function
from value (optional if keyframe is provided) starting value for property specified by property
to value (optional if keyframe is provided) ending value for property specified by property
exactTiming boolean (optional) set to non-false to disable time-to-first-frame compensation. See Compensating for time-to-first-frame issues in Internet Explorer.

All members are required except where noted.

If required, the type of the from and to members must be compatible with the property specified by the property member. In other words, if property is "transform", then from and to must be strings which are valid transforms. If property is "opacity", then from and to must be numbers between 0 to 1.

(Note that the empty string is not a valid transform. To animate to a null transform, you must specify an explicit null transform: "none".)

If a keyframe is provided, then the animation will be performed according to the sequence described by the keyframe. In that case, the from and to in the Animation object are ignored. Note, however, that you must still pass a property which matches the keyframe, so that action conflicts can be detected and dealt with.

Any member of the Animation object can be set to a function that returns the desired value. See Dynamic action properties for more information.

Internals

The animation engine creates copies of the Animation object for capture purposes, so that changes made by the calling application to the Animation object do not interfere with our callbacks, and so that we can attach private information to the copy. (Earlier versions of the animation engine modified the Animation object, which caused problems if the application re-used the Animation object for multiple animations.)

After copying the Animation object (evaluating functions as necessary for dynamically properties), we add the following internal properties to our copy:

Member Type Description
keyframe string the auto-generated unique keyframe name

Transition object

A Transition object describes the properties of a transition.

Member Type Description
property string the property to animate. "transform" and "opacity" are the only ones used by the PVL.
delay number delay before the animation begins, in milliseconds
duration number transition duration, in milliseconds
timing string CSS timing function
from value (optional) starting value for property specified by property. If not specified, then property begins animating from its current value.
to value target value for property specified by property
exactTiming boolean (optional) set to non-false to disable time-to-first-frame compensation. See Compensating for time-to-first-frame issues in Internet Explorer.
skipStylesReset boolean (optional) set to non-false to disable setting the transition style to empty when the transition ends. This can improve performance of scenario where the element will be removed from the DOM when the transition ends.

All members are required except where noted.

The type of the to member must be compatible with the property specified by property. In other words, if property is "transform", then to must be a string. If property is "opacity", then to must be a number between 0 and 1.

(Note that unlike animations, for Transitions, you pass the empty string "" as the to value if the property is "transform".)

Internals

As with Animation objects, the animation engine creates copies of the Transition object for capture purposes as part of the evaluation. Otherwise, if the application modified the property member after the call to executeTransition returned, our event handlers would be looking at the wrong property.

Animation Engine function notes

Action descriptor object

An action descriptor object provides the traits of an action.

Member Type Description
shorthandProp string CSS name of the shorthand property that sets all the action properties at once
nameProp string CSS name of property that controls action lifetime
nameField string name of member of the Action/Transition object to set into the lifetime property
props array an array of arrays, each one describing one CSS property and how it should be initialized from the Transition or Animation object. The properties are listed in the order they appear in the shorthand property.
Index Type Description
0 string CSS name of property to update
1 string name of member of the Action/Transition object to use
2 string string to append to the corresponding member value. For time values, this is "ms"; for other values, this is the null string.
isTransition bool true if this is the descriptor for a transition. Transitions have a few extra quirks that animations don't, like having to set the lifetime property to "none" to stop all transitions.
styleCaches hash the active style cache objects, indexed by uniqueID

There is no good reason why the props is an array of arrays rather than an array of records. It just ended up that way for historical reasons, and there was no justification for changing it. (One might retroactively justify it by claiming that integer indexing is faster than hash indexing, but the difference is not measureable.)

StyleCache object

A style cache object is a JavaScript object with the following properties:

Member Type Description
cref number reference count, the number of actions which are using this object. Each action calls removeName when it completes.
id string uniqueID of the element whose styles are being cached
desc object the action descriptor that created the style cache object
removed hash lifetime object names we have removed since the last style resolution. This is used to detect that we need to force a style resolution if the same name is added back.
prevStyles array the original styles on the object, to be restored when all actions complete. This array runs parallel to desc.props.
prevNames array the original value of the desc.nameProp style.
names string the current cached value of the element's desc.nameProp style

Note that we do not cache a reference to the HTML element or any other DOM objects. The style cache object refers only to strings and global objects. That way, if there is a leak (for example, due to an exception), we only leak some JavaScript strings and objects and not HTML elements (or worse, HTML element trees).

When the reference count drops to zero, the original styles are restored from the prevStyles and prevNames members.

The underlying problem is that changes to the lifetime property are detected only at resolve time. This causes problems for animations, because an animation often interrupts another instance of itself. When that happens, we want the animation to restart, which means that we need to delete the animation from the animation-name property, resolve the style (so that the browser sees the deletion), then add it back.

To detect the case where we need to force a resolution before setting the new value, we see if the animation being added is one that was recently removed. If so, then we force a resolution to ensure that the deletion is seen by the browser.

The removed hash can be cleared if we explicitly resolve styles. But in the common case, the styles are resolved implicitly by the event loop, so we do not know exactly when it has occurred. It is not worth scheduling a setImmediate to clear the hash, because the cost of calling into the DOM to manage the callback far exceeds the savings of this technique. (Two DOM calls, one to cancel the old immediate and another to schedule a new one.)

In practice, objects are not continually kept in an animating state, because PVL animations do not support autorepeat; they are all one-shot animations. Eventually, objects stop animating, and then the entire style cache object goes away.

Note that if an application starts a new animation on completion of an old animation, we do have a brief moment where there are no active animations, so that case is not considered a "continual animation." To get a continual animation, the application would have to keep interrupting the animation with a new animation and never let the old animation end naturally.

StyleCache constructor

Create a new StyleCache object associated with a particular element by capturing the element's initial styles (so they can be restored later).

Parameter Type Description
id string uniqueID of the element whose styles are being cached
desc object the action descriptor that created the style cache object
style StyleCSSProperties style object of the target element
Returns Description
StyleCache object The newly-constructed StyleCache object

StyleCache.destroy method

Destroys a StyleCache object and restores the original styles to the element being tracked.

Parameter Type Description
style StyleCSSProperties style object of the target element
Returns Description
none

Destroying a StyleCache object entails the following steps:

StyleCache.removeName method

Removes a reference to an action name from the associated action property. If other actions remain, then the name is removed from the list. If no other actions remain, then all styles are restored to the element, and the style cache is destroyed.

Parameter Type Description
style StyleCSSProperties style object of the target element
name string name to remove from comma-separated list
elem DOM element (optional) underlying element if this is the cancellation of a transition
Returns Description
none

Suppose the element's elem.style.lifetime property has the value "a, b, c, d". Calling removeName(styleCache, elem.style, "b") would result in the property changing to "a, c, d". The "b" was removed from the list.

The lifetime property is inferred from the action descriptor associated with the style cache object.

Remember that Internet Explorer actually separates items with a comma and space, so we need to include the space in our parsing algorithm.

Removing a name decrements the style cache object's reference count, since there is one fewer outstanding action.

Remember the name as one which was recently removed, so that we will know to force a style resolution if it is added back. This does not need to be done for our auto-generated names, because those are guaranteed unique and will never be seen again. This avoids unbounded build-up of names in the case where the application manages to keep the element animating continuously.

As a special optimization, if the style cache object is about to be destroyed, then there is no point deleting the name from the lifetime property since we're going to restore the lifetime property as part of the style restoration that occurs during destruction.

Observe that when we update the lifetime property, we also update the styleCache.names so that our cache remains valid.

The nameValue local variable and the elem parameter are to handle a strange edge case with canceled transitions. For reasons known only to the W3C, the default value for the transition property is "all". This means that setting the value to "" is not the same as disabling all transitions; in fact, it enables all transitions!

Therefore, if we are trying to cancel the last transition, we have to set the transition property to the special value "none", resolve the style (which forces the transition to stop), and then set it to the empty string.

This extra step is not required for normal expiration of transitions, because there is no need to force the transition to stop; it already stopped on its own.

The caller passes a non-null elem if it is calling to cancel a transition. We then perform the extra step if we find that we just removed the last transition.

Note that it is possible for the last name to be removed from the this.names but still have a nonzero reference count. That happens if the last name was removed as part of canceling an old action while we set up a new one. In that case, we do not want to force any style resolution to "none" because we're going to be doing a style resolution in setTemporaryStyles anyway. This avoids a wasteful double-resolve.

makeArray

Takes an object and converts it into something that supports indexing and has a length property.

Parameter Type Description
elements object (optional) collection or single element
Returns Description
object an object which supports indexing and has a length property

If a single object is passed, then we turn it into a one-element array.

If undefined is passed, then we turn it into a zero-element array.

getUniqueKeyframeName

Returns a unique string to use as a keyframe name.

Parameter Type Description
none
Returns Description
string a unique string to use as a keyframe name

JavaScript integers overflow at 252. Even if running a million animations per second, overflow will not occur for over 140 years. I like those odds.

isUniqueKeyframeName

Determines whether the specified name was generated by getUniqueKeyframeName.

Parameter Type Description
s string the name to check
Returns Description
boolean true if the string is a name generated by getUniqueKeyframeName.

copyWithEvaluation

Creates a callback function which creates a shallow copy of a JavaScript object. If any member is a function, then the function is called to resolve it to a scalar. This function is used to capture Animation and Transition objects.

Parameter Type Description
iElem zero-based index of the animating element's group See below
elem DOM element the element being animated
Returns Description
function function which creates a shallow copy with function evaluation

The return value is a function suitable for use as a callback for Array.map. The function creates a shallow copy of the object and returns the copy. If any member of the source object is a function, then the function is called with the iElem and elem parameters.

stopExistingAction

Stops any active animation on an element for a particular property. This is done by calling the registered cleanup function, if any, specifying that the cleanup is due to interruption. The finish function is expected to call unregisterAction as part of its cleanup.

Parameter Type Description
id string uniqueID of the element
prop string CSS name of property whose animation is to be canceled
Returns Description
none

registerAction

Registers a function to be called if an action needs to be canceled.

Parameter Type Description
id string uniqueID of the element
prop string CSS name of property being animated
finish function (reason) function to call to clean up the animation.
Returns Description
none

Callers are expected to have called stopExistingAnimation at some earlier point.

The finish function is called as follows:

Parameter Type Description
reason integer (optional) reason for cleanup.
Value Name Description
0 reason_ended The action ran to completion normally.
1 reason_interrupted The action was interrupted by a conflicting action.
2 reason_canceled The action was canceled by the application.
If no reason is given, then the reason is assumed to be reason_ended.
Returns Description
none

unregisterAction

Unregisters the function registered by a previous call to registerAction.

Parameter Type Description
id string uniqueID of the element
prop string CSS name of property whose animation or transition has completed
Returns Description
none

setTemporaryStyles

Changes the styles of an element to add new actions, creating a style cache object if this is the first action to be applied to the element.

Parameter Type Description
elem HTMLElement the target element
style StyleCSSProperties style object of the target element
actions array array of Transition or Animation objects
desc action descriptor traits of the action to be applied
Returns Description
function () function to call to clean up temporary changes

The function proceeds in the following steps:

If we did not force the styles to be resolved, then they will be resolved naturally when control returns to the event loop.

executeElementTransition

Apply an array of transitions to a single element.

Parameter Type Description
elem DOM element target element
index number zero-based index of the animating element's group
transitions array of Transition objects transitions to apply to the element
promises array of Promise objects any Promises created by the function are appended to this array
animate boolean true if animations should be performed
Returns Description
none

As we saw in the introduction, the property mapping for transitions is as follows:

Characteristic Property Notes
Lifetime -ms-transition-property
Initial delay -ms-transition-delay append "ms" since the value is in milliseconds
Duration -ms-transition-duration append "ms" since the value is in milliseconds
Curve -ms-transition-timing-function

We create private copies of the Transition objects so that we are insulated from changes made by the application, and so that we can evaluate any callback functions.

If the transition has an explicit from, then set it.

Subtlety: We filter the to through the uniformizeStyle element to account for equivalent properties. See Edge case for transitions for further discussion.

For each transition, we hook up these callback functions:

Callback Fired by Description
finish setTimeout Unregisters all callbacks and completes the promise. This function follows the rules for finish functions as described in registerAction.
onTransitionEnd transitionend If the transition being ended is for the property we are animating, then our transition ran to completion, and we call the finish callback to complete our promise and clean up.

Note that event handlers are registered on the document if the element is disabled, for reasons described in Event handler gotchas.

If the element is already at the final value, then there is no transition, and we merely schedule the cleanup function after an appropriate timeout. We could have detected this case earlier and avoided all of the event handler set-up, but we'll do it this way to reduce churn during Windows 8 RTM. (The case of a null transition is not expected to be common, anyway.)

If the element is not at the final value, then we update the value of the animating property to trigger the transition. We let the style resolution happen when we return to the event loop.

The timeout associated with the finish callback is set in two steps to ensure that the time-to-first-frame does not count toward the timeout. We set the watchdog timeout to the expected completion time after the first frame has completed.

Note that the loop over the transitions must be performed via forEach rather than a for loop, because we need each iteration to capture a different transition local variable.

If animations are disabled (animate is false), then all we do is set the final values of the transitions into the element. No promise is generated since the operation completed synchronously.

When the finish function is called due to reason_interrupted or reason_canceled, we must pass the elem to styleCache.removeName because we may need to set the transition-name to "none" to force the transition to stop. When it is called due to reason_canceled, we pass true to completePromise for reasons explained in Cancellation.

executeTransition

Parameter Type Description
elem see applyAction target elements
transitions array of Transition objects transitions to apply to the elements
Returns Description
Promise The promise completes when all transitions complete

The executeTransition function uses the applyAction function to do the heavy lifting.

executeElementAnimation

Apply an array of animations to a single element.

Parameter Type Description
elem DOM element target element
index number zero-based index of the animating element's group
anims array of Animation objects animations to apply to the element. Assumes that calcAnimationProperties has already been called to reinitialize each Animation object.
promises array of Promise objects any Promises created by the function are appended to this array
animate boolean true if animations should be performed
Returns Description
none

As we saw in the introduction, the property mapping for animations is as follows:

Characteristic Property Notes
Lifetime -ms-animation-name
Initial delay -ms-animation-delay append "ms" since the value is in milliseconds
Duration -ms-animation-duration append "ms" since the value is in milliseconds
Curve -ms-animation-timing-function
Direction -ms-animation-direction hard-coded to "normal"
Iteration -ms-animation-iteration-count hard-coded to "1"
Persistence -ms-animation-fill-mode hard-coded to "both"

We hook up the following callback functions for each animation:

Callback Fired by Description
finish setTimeout Unregisters all callbacks and completes the promise. This function follows the rules for finish functions as described in registerAction.
onAnimationEnd animationend If the animation being ended is for our keyframe, then our animation ran to completion, and we call the finish callback to complete our promise and clean up.

Note that event handlers are registered on the document if the element is disabled, for reasons described in Event handler gotchas.

The timeout associated with the finish callback is set in two steps to ensure that the time-to-first-frame does not count toward the timeout. We set the watchdog timeout to the expected completion time after the first frame has completed.

Note that the loop over the animations must be performed via forEach rather than a for loop, because we need each iteration to capture a different anim local variable.

If animations are disabled (animate is false), then we do nothing, since animations have no lasting effect. No promise is generated since the operation completed synchronously.

If an animation requires a custom keyframe, we create a temporary STYLE element to hold it. The custom style sheet is removed after the animations have triggered. In theory, this cleanup could be done via setImmediate, but setImmediate it turns out is not guaranteed to run after layout. The current implementation of Internet Explorer runs timeouts after layout, so we schedule a timeout and remove the element on the timeout. (If the application emptied the document in the meantime, parentElement will be null, in which case trying to disconnect from the nonexistent parent will raise a null object exception, so watch out for that.)

In the future, we should switch to removing the style sheet when the last animation completes or is canceled. The original code removed the style sheet aggressively because of the documented 31-stylesheet limit in Internet Explorer. That limit was removed in IE10.

executeAnimation

Parameter Type Description
elem see applyAction target elements
anims array of Animation objects animations to apply to the elements
Returns Description
Promise The promise completes when all animations complete

The executeAnimation function uses the applyAction function to do the heavy lifting.

animationSettings object

This is either a Windows.UI.ViewManagement.UISettings object, or a dummy object which reports that animations are always enabled.

Property Type Description
animationsEnabled boolean true is animations are enabled in the system, or false if disabled

If we can create the Windows.UI.ViewManagement.UISettings object, then we detect whether animations are enabled by checking the animationsEnabled property, which corresponds to the SPI_GET­CLIENT­AREA­ANIMATIONS system parameter.

If we are unable to create the object, then we substitute a dummy object whose hard-coded animationsEnabled property is true.

The try...catch around the probe for WinRT is to protect against problems that may occur if the Web page happens to have a global object named Windows. (Perfectly legal and not entirely unreasonable.) If that's the case, we will take an exception when we try to invoke WinRT methods on it, since it's not actually WinRT. If that happens, then we just say, "Forget it, we don't have WinRT here." We perform early tests against window.Windows and Windows.UI to avoid raising exceptions in the common case, thereby avoiding exception noise when the application developer goes to debug their page. (Otherwise, they will see this exception and think, "Maybe that's why my page isn't working.")

applyAction

Parameter Type Description
elem see below target elements
action array of Transition or Animation objects target actions
execAction function (elem, index, actions, promises) callback to apply the actions to the element. Any Promises are appended to the promises array.
Returns Description
Promise The promise completes when all actions have completed.

The elem parameter can be any of the following:

To elaborate on the array of arrays: Suppose the parameter is [a, [b, c], d]. This represents a transition involving four elements.

Element Stagger position
a 0
b 1
c 1
d 2

In other words, elements b and c animate together.

We use the algorithm described in Globally enabling and disabling the PVL to determine whether animations are enabled. Note that we take advantage of the fact that boolean true has numeric value 1, whereas boolean false has numeric value 0.

After the execAction has been called on each animating element, we join together all the Promises, forming the final Promise for the action. If there are no promises to join (all operations completed synchronously), we return a zero-duration timeout promise so that the promise completes asynchronously. This preserves event-ordering behavior when animations are disabled.

Animation Engine events

Animation Library objects

offset object

Application use an offset object to provide animation offsets.

Member Type Description
top string: CSS [LENGTH] the vertical displacement
left string: CSS [LENGTH] the horizontal displacement
rtlflip boolean (optional) if true, change direction of left if applied to an element whose direction is "rtl"
keyframe string (optional) if set, specifies the CSS keyframe to use for the animation. This keyframe must match the top and left parameters. If a keyframe is specified, rtlflip must not be true.

The top and left are CSS [LENGTH] values, usually pixels. For example, "10px".

If the rtlflip property is true, and the offset is being applied to an element whose CSS direction property resolves to "rtl", then the sign of the left property is reversed.

OffsetArray class

The OffsetArray class captures the offsets provided by the application.

OffsetArray constructor

Parameter Type Description
offset object offsets (see discussion below)
keyframe string (optional) keyframe name if default used (see discussion below)
defOffset object (optional) default offset to use if application did not provide one. If provided, this object must have top and left property.

The OffsetArray constructor accepts an object in the following forms:

See the getOffset member function for a description of how these parameters are interpreted to determine the offset to apply to each element.

If the offset is empty or undefined, then the default offset defOffset is used.

If the offset is a single offset object or an array with one offset, we use the checkKeyframe method to see whether that offset happens to match the default offset for this animation. If so, we will use the keyframe. See checkKeyframe for further discussion.

See the chooseKeyframe property for a discussion of how we choose the keyframe in the case where the application asks for the default offsets.

keyframe property

The keyframe property specifies the predefined CSS animation keyframe to use, if any.

If the OffsetArray was constructed with an explicit application-provided offset, then there is no predefined keyframe.

If the OffsetArray was constructed with a default offset, then the chooseKeyframe function is used to determine what keyframe to return.

In all cases, the keyframe property is a value suitable for passing as the keyframe property of an Animation object.

checkKeyframe function

Determines whether the application-provided offsets match the predefined keyframe offsets. If so, then proceeds as if the predefined offsets were used.

Parameter Type Description
offset offset Application-provided offset
defOffset offsets Predefined offset
keyframe string (optional) keyframe name
Returns Description
string static keyframe to apply to all elements
function function to calculate keyframe for each element
null no keyframe applies to this offset

If there is no keyframe provided, then the function returns null, indicating that there is no optimized keyframe for this collection of offsets.

If the application-provided offset does not match the default offset's top and left, then we cannot use the predefined keyframe. Return null.

If the application-provided offset flips in RTL, but we have only an LTR keyframe, then we cannot use the predefined keyframes. Return null. (We cannot return a function that sometimes returns a keyframe and sometimes returns null, because we need to know up front whether to pass a callback as the from and to members of the Animation or Transition object.)

If the application-provided offset does not flip in RTL, then we can apply the LTR keyframe statically to all elements.

Otherwise, the application-provided offset flips in RTL (and therefore the default offset also flips in RTL), so we can use keyframeCallback as our callback function.

Here's a table of the possibilities when a keyframe is available and the offsets match.

offset.rtlflip
true false
defOffset.rtlflip = true keyframeCallback keyframe
defOffset.rtlflip = false null

chooseKeyframe function

Calculates the static keyframe name or keyframe callback function to use for a collection of animation offsets.

Parameter Type Description
defOffset offset Default offset to apply
keyframe string (optional) keyframe name
Returns Description
string static keyframe to apply to all elements
function function to calculate keyframe for each element
null no keyframe applies to this offset

If there is no keyframe provided, then the function returns null, indicating that there is no optimized keyframe for this collection of offsets.

If the defOffset defines a keyframe which does not need to flip in RTL, then the static keyframe is returned. This causes all animations to use the same keyframe.

If the defOffset defines a keyframe which must flip depending on the element's direction, then a function is returned which returns either the keyframe name or the keyframe name with -rtl appended, depending on whether the element is LTR or RTL.

keyframeCallback

Callback function that decides whether to use the LTR or RTL keyframe for an element.

Parameter Type Description
keyframe string keyframe name. The keyframe must be available in both LTR and RTL versions.
Returns Description
function function to calculate keyframe for each element

The callback function returns the appropriate keyframe name based on the element's direction.

getOffset member

Parameter Type Description
i integer Index of object from which to obtain offset
Returns Description
object an offset object

If the OffsetArray was constructed with undefined or an empty array, then the i parameter is ignored, and the function returns the defOffset passed to the constructor. If no defOffset was passed to the constructor, then a default offset of { top: "0px", left: "11px", rtlflip: true } is returned.

If the OffsetArray was constructed with a single object, then the i parameter is ignored, and the single object passed to the constructor is returned.

Otherwise, the OffsetArray was constructed with an array. The i'th element of the array is returned (zero-based index). If fewer than i + 1 elements are present in the array, then the last element in the array is returned. (Effectively, the last element repeats indefinitely.)

Animation Library function notes

makeArray

Identical to makeArray in the animation engine.

collectOffsetArray

Parameter Type Description
elemArray collection target elements
Returns Description
Array Array of { top: value, left: value } objects describing the current positions of the elements in the collection.

The current position of the element is its current offsetTop and offsetLeft, combined with any active translation transform.

See Optimization notes: Parsing transitions for a discussion of the best way to parse the transform property.

Note that the returned array is not an array of offset objects. The top and left properties of the objects returned by collectOffsetArray are simple integers (representing pixels), not CSS lengths.

staggerDelay

Creates the callback function for the delay member of the Animation and Transition objects used by the Animation Engine.

Parameter Type Description
initialDelay number millisecond delay for first item
extraDelay number additional millisecond delay to be added for subsequent items
delayCap number maximum millisecond delay
Returns Description
function (i) Delay computation function which returns delay in milliseconds

No animations in the animation library have a delay factor, and they all have a delay cap, so we can optimize the stagger function for linear stagger:

stagger = min(initialDelay + extraDelay × i, delayCap)

The nominal delay for item 1 is initialDelay + extraDelay * delayFactor.

The nominal delay for item 2 is initialDelay + extraDelay * delayFactor + extraDelay * delayFactor * delayFactor.

In general, the nominal delay for item n is

nominalDelay = initialDelay   +
1 ≤ in
extraDelay × delayFactori
= initialDelay   + extraDelay × delayFactor ×
1 ≤ in
extraDelay × delayFactori−1
= initialDelay   + extraDelay × delayFactor ×
0 ≤ i < n
extraDelay × delayFactori
= initialDelay   + extraDelay × delayFactor ×
1 − delayFactorn

1 − delayFactor

If a delayCap is specified, then the calculated delay is min(nominalDelay, delayCap). Otherwise, the nominal delay is the calculated delay.

makeOffsetsRelative

Parameter Type Description
elemArray collection target elements
offsetArray array original positions
Returns Description
none

Updates offsetArray in place by subtracting the corresponding current locations of the elements in elemArray. Any active transforms on the elements are ignored for the purpose of determining the current location.

animTranslate2DTransform

Parameter Type Description
elemArray collection target elements
offsetArray array original positions
transition array of Transition objects transitions to apply to the elements
Returns Description
Promise The promise completes when all transitions complete

A counteracting transform is applied to elements so that their visual position corresponds to offsetArray. The transform transition is then executed via executeTransition.

translateCallback

Creates the callback function for the to amd from members of the Animation and Transition objects used by the Animation Engine.

Parameter Type Description
offsetArray OffsetArray array of offsets
prefix (optional) string optional string to prefix to the translation string
Returns Description
function (i, e) function which returns translation to apply to element number i, which is e

The returned function takes a zero-based item number and calculates the translation for that item based on the offsetArray. If the offset is marked rtlflip and the target element's resolved direction style is "rtl", then the value is negated. (Note that our negation algorithm is purely textual, and it ends up negating "0pt" to "-0pt". Fortunately, "-0pt" is still a legal CSS dimension, and it will be simplified to "0pt" by the animation engine.

If the prefix is present, then it is prepended to the calculated translation.

translateCallbackAnimate

Creates the callback function for the to amd from members of the Animation object used by the Animation Engine.

Parameter Type Description
offsetArray OffsetArray array of offsets
suffix (optional) string optional string to suffix to the translation string
Returns Description
function (i, e) function which returns translation to apply to element number i, which is e

The returned function takes a zero-based item number and calculates the translation for that item based on the offsetArray.

If the suffix is present, then it is appended to the calculated translation.

RTL flipping is not performed by this function. It is assumed that offsetArray is based on physical coordinates and therefore any flipping was performed during layout.

keyframeCallbackAnimate

Callback function that decides whether to use a keyframe for an animation.

Parameter Type Description
offsetArray OffsetArray array of offsets
keyframe string keyframe name
Returns Description
function function to calculate keyframe for each element

The callback function returns the keyframe name if the corresponding offset is zero. Otherwise, it returns null and the Animation object's from and to values are used.