1. Introduction
This section is not normative.This document introduces a new primitive that provides extensibility in web animations and enables high performance interactive procedural animations on the web. For details on the rationale and motivation see both [explainer] and [principles].
The Animation Worklet API provides a method to create scripted animations that control a set of animation effects. The API is designed to make it possible for user agents to run such animations in their own dedicated thread to provide a degree of performance isolation from main thread.
1.1. Relationship to the Web Animations API
This section is not normative.Animations running inside an Animation Worklet execution context expose the Animation
interface from the Web Animations specification on the main javascript execution context. This means
they can be controlled and inspected from main thread using the same Web Animation APIs.
2. Animation Worklet
Animation Worklet is aWorklet
responsible for all classes related to custom
animations. The worklet can be accessed via animationWorklet
attribute.
[Exposed =Window ]partial namespace CSS { [SameObject ]readonly attribute Worklet ; };
animationWorklet
The animationWorklet
's worklet global scope type is AnimationWorkletGlobalScope
.
AnimationWorkletGlobalScope
represents the global execution context of animationWorklet
.
[Global =(Worklet ,AnimationWorklet ),Exposed =AnimationWorklet ]interface :
AnimationWorkletGlobalScope WorkletGlobalScope {undefined registerAnimator (DOMString ,
name AnimatorInstanceConstructor ); };
animatorCtor callback =
AnimatorInstanceConstructor any (any ,
options optional any );
state
3. Animator
An Animator represents a custom animation that is running inside AnimationWorkletGlobalScope
. Each Animator is associated with an animation instance (of
type WorkletAnimation
) in the document and determines how that animation progresses its animation effect. The animate
function contains the logic responsible for
translating the animation current time into appropriate progress of the animation effect. An
animator can only be instantiated by construction of a WorkletAnimation
in the document.
Two animators types are supported: Stateless Animator and Stateful Animator each providing a different state management strategy.
3.1. Stateless Animator
A Stateless Animator is a type of animator does not depend on any local state either stored on the instance or global scope. Effectively, the animate function of an Stateless Animator can be treated as a pure function with the expectation that given the same input, it produces the same output.
class FooAnimator{ constructor ( options) { // Called when a new animator is instantiated. } animate( currentTime, effect) { // Animation frame logic goes here. } }
Note: The statelessness allows animation worklet to perform optimization such as producing multiple animation frames in parallel, sharing a single animator instance for multiple animations, and performing very cheap teardown and setup. Using Stateless Animator is highly recommended to enable such optimizations.
3.2. Stateful Animator
A Stateful Animator is a type of animator that can have local state and animation worklet guarantees that it maintains this state as long as the stateful animator fulfills the contract required by its interface and as described following.
Animation worklet maintains a set of WorkletGlobalScope
s which may exist across different
threads or processes. Animation worklet may temporarily terminate a global scope (e.g., to preserve
resources) or move a running animator instance across different global scopes (e.g., if its
effect is mutable only in a certain thread). Animation worklet guarantees that a stateful animator
instance’s state is maintained even if the instance is respawned in a different global scope.
The basic mechanism for maintaining the state is that the animation worklet snapshots the local state that is exposed via the state function and then reifies it so that it can be passed into the constructor when the animator instance is respawned at a later time in a potentially different global scope. The migrate an animator instance algorithm specifies this process in details.
A user-defined stateful animator is expected to fulfill the required contract which is that its state function returns an object representing its state that can be serialized using structured serialized algorithm and that it can also recreate its state given that same object passed to its constructor.
class BarAnimator{ constructor ( options, state) { // Called when a new animator is instantiated (either first time or after being respawned). this . currentVelocity= state? state. velocity: 0 ; } animate( currentTime, effect) { // Animation frame logic goes here and can rely on this.currentVelocity. this . currentVelocity+= 0.1 ; } state() { // The returned object should be serializable using structured clonable algorithm. return { velocity: this . currentVelocity; } } }
3.3. Animator Definition
An animator definition is a struct which describes the author defined custom animation logic. It consists of:
-
- animator name
-
A <ident>#.
-
- class constructor
-
- animate function
-
A Function callback function type.
-
- state function
-
A Function callback function type.
-
- stateful flag
-
A boolean flag
A stateful animator definition is an animator definition whose stateful flag is true.
A document animator definition is a struct which describes the information needed by the document about the author defined custom animation. It consists of:
-
- stateful flag
-
A boolean flag
3.4. Registering an Animator Definition
The document has a map of document animator definitions. The map gets populated whenregisterAnimator(name, animatorCtor)
is called.
An AnimationWorkletGlobalScope
has a map of animator definitions. The map gets
populated when registerAnimator(name, animatorCtor)
is called.
Note that to register a stateful animator definition it is simply enough for the registered
class to have a state
function.
When the registerAnimator(name, animatorCtor)
method is called in a AnimationWorkletGlobalScope
, the user agent must run the
following steps:
-
If name is not a valid <ident>, throw a TypeError and abort all these steps.
-
Let animatorDefinitions be the
AnimationWorkletGlobalScope
's animator definitions map. -
If animatorDefinitions[name] exists, throw a NotSupportedError and abort all these steps.
-
If the result of IsConstructor(animatorCtor) is false, throw a TypeError and abort all these steps.
-
Let prototype be the result of Get(animatorCtor, "prototype").
-
Let animateFuncValue be the result of Get(prototype, "animate").
-
Let animateFunc be the result of converting animateFuncValue to the Function callback function type. If an exception is thrown, rethrow the exception and abort all these steps.
-
Let stateFuncValue be the result of Get(prototype, "state").
-
Let stateFunc be the result of converting stateFuncValue to the Function callback function type, If an exception is thrown, set stateful to be false, otherwise set stateful to be true and stateFunc to be undefined.
-
Let definition be a new animator definition with:
-
animator name being name
-
class constructor being animatorCtor
-
animate function being animateFunc
-
state function being stateFunc
-
stateful flag being stateful
-
-
set the animatorDefinitions[name] to definition.
-
Queue a task to run the following steps:
-
Let documentAnimatorDefinitions be the associated document’s document animator definitions map.
-
Let documentDefinition be a new document animator definition with:
-
stateful flag being stateful
-
-
If documentAnimatorDefinitions[name] exists, run the following steps:
-
Let existingDocumentDefinition be the result of get documentAnimatorDefinitions[name].
-
If existingDocumentDefinition is
"invalid"
, abort all these steps. -
If existingDocumentDefinition and documentDefinition are not equivalent, (that is their stateful flags are different), then:
set documentAnimatorDefinitions[name] to
"invalid"
.Log an error to the debugging console stating that the same class was registered with different
stateful flag
.
-
-
Otherwise, set documentAnimatorDefinitions[name] to documentDefinition.
-
3.5. Animator Effect
A Animator Effect represents the underlying animation effect inside animation worklet.
It has a corresponding effect property which is a reference to the underlying animation effect. It also has corresponding properties for the following animation effect's properties:
-
playback direction, and
Animator Effect is represented by the WorkletAnimationEffect
interface
inside AnimationWorkletGlobalScope
.
[Exposed =AnimationWorklet ]interface {
WorkletAnimationEffect EffectTiming getTiming ();ComputedEffectTiming getComputedTiming ();attribute double ?localTime ; };
Note: WorkletAnimationEffect
is basically a restricted version of AnimationEffect
interface
which does not have updateTiming
but additionally allows local time to be set.
getTiming()
-
Returns the specified timing properties using the corresponding properties.
getComputedTiming()
-
Returns the calculated timing properties using the corresponding properties.
localTime
, of type double, nullable-
Getting the attribute returns the corresponding local time. Setting the attribute updates the local time given this effect as effect and the attribute value as time:
-
If the time is the same as effect’s local time then skip following steps.
-
Set the effect’s local time to time.
-
Set the effect’s animator instance’s sync requested flag to true.
-
4. Animator Instance
An animator instance is a struct which describes a fully realized custom animator in an AnimationWorkletGlobalScope
. It has a reference to an animator definition and owns the instance specific state such as animation effect and
timeline. It consists of:
-
- animator name
-
A string used to identify the animator definition.
-
- frame requested flag
-
A boolean flag that indicates if the animator needs to animate.
-
- sync requested flag
-
A flag that indicates if the animator needs to sync its output.
-
- effect
-
An Animator Effect.
-
- animator current time
-
A time value equivalent to the corresponding worklet animation's current time.
-
- animator timeline
-
The timeline of the corresponding worklet animation.
-
- animator serialized options
-
The serializable object representing the options to be used when constructing the animator instance.
A stateful animator instance is an animator instance whose corresponding definition is a stateful animator definition.
4.1. Creating an Animator Instance
Each animator instance lives in an AnimationWorkletGlobalScope
.
Each AnimationWorkletGlobalScope
has an animator instance set. The set is populated
when the user agent constructs a new animator instance in the AnimationWorkletGlobalScope
scope. Each animator instance corresponds to a worklet animation in the document scope.
To create a new animator instance given a name, timeline, effect, serializedOptions, serializedState, and workletGlobalScope, the user agent must run the following steps:
-
Let the definition be the result of looking up name on the workletGlobalScope’s animator definitions.
If definition does not exist abort the following steps.
-
Let animatorCtor be the class constructor of definition.
-
Let options be StructuredDeserialize(serializedOptions).
-
Let state be StructuredDeserialize(serializedState).
-
Let animatorInstance be the result of constructing animatorCtor with «options, state» as arguments. If an exception is thrown, rethrow the exception and abort all these steps.
-
Let animatorEffect be the result of constructing a
WorkletAnimationEffect
with its corresponding effect being effect. -
Set the following on animatorInstance with:
-
animator name being name
-
frame requested flag being false
-
sync requested flag being false
-
animator current time being unresolved
-
effect being animatorEffect
-
animator timeline being timeline
-
animator serialized options being options
-
-
Add animatorInstance to workletGlobalScope’s animator instance set.
4.2. Running Animators
When the user agent wants to produce a new animation frame, if for any animator instance the associated frame requested flag is true then the the user agent must run animators for the current frame in all its associated global scopes.
Note: The user agent is not required to run animations on every visual frame. It is legal to defer generating an animation frame until a later frame. This allow the user agent to provide a different service level according to their policy.
When the user agent wants to run animators in a given workletGlobalScope, it must run the following steps:
-
Iterate over all animator instances in the workletGlobalScope’s animator instance set. For each such animator the user agent must perform the following steps:
-
Let animatorName be animator’s animator name
-
Let the definition be the result of looking up animatorName on the workletGlobalScope’s animator definitions.
If definition does not exist then abort the following steps.
-
If the frame requested flag for animator is false or the effect belonging to the animator will not be visible within the visual viewport of the current frame the user agent may abort all the following steps.
Consider giving user agents permission to skip running individual animator instances to throttle slow animators.
-
Let animateFunction be definition’s animate function.
-
Let currentTime be animator current time of animator.
-
Let effect be effect of animator.
-
Invoke animateFunction with arguments «currentTime, effect», and with animator as the callback this value.
-
-
If any animator instances in the workletGlobalScope’s animator instance set has its sync requested flag set to true then sync local times to document given workletGlobalScope.
Note: Although inefficient, it is legal for the user agent to run animators multiple times in the same frame.
should be explicit as to what happens if the animateFunction throws an exception. At least we should have wording that the localTime values of the effects are ignored to avoid incorrect partial updates.
4.3. Removing an Animator Instance
To remove an animator instance given animator and workletGlobalScope the user agent must run the following steps:
-
Remove animator from workletGlobalScope’s animator instance set.
4.4. Migrating an Animator Instance
The migration process allows stateful animator instance to be migrated to a different AnimationWorkletGlobalScope
without losing their local state.
To migrate an animator instance from one AnimationWorkletGlobalScope
to another,
given animator, sourceWorkletGlobalScope, destinationWorkletGlobalScope, the user agent must run the following steps :
-
Let serializedState be undefined.
-
Queue a task on sourceWorkletGlobalScope to run the following steps:
-
Let animatorName be animator’s animator name
-
Let definition be the result of looking up animatorName on sourceWorkletGlobalScope’s animator definitions.
If definition does not exist then abort the following steps.
-
Let stateful be the stateful flag of definition.
-
If stateful is false then abort the following steps.
-
Let stateFunction be definition’s state function.
-
Let state be the result of Invoke stateFunction with animator as the callback this value. If any exception is thrown, rethrow the exception and abort the following steps.
-
Set serializedState to be the result of StructuredSerialize(state). If any exception is thrown, then abort the following steps.
-
Run the procedure to remove an animator instance given animator, and sourceWorkletGlobalScope.
-
-
Wait for the above task to complete. If the task is aborted, abort the following steps.
-
Queue a task on destinationWorkletGlobalScope to run the following steps:
-
Run the procedure to create a new animator instance given:
-
The animator’s animator name as name.
-
The animator’s animator timeline as timeline.
-
The animator’s effect as effect.
-
The animator’s animator serialized options as options.
-
The serializedState as state.
-
The destinationWorkletGlobalScope as workletGlobalScope.
-
-
If an animator state getter throws the user agent will remove the animator but does not recreate it. This effectively removes the animator instance.
4.5. Requesting Animation Frames
Each animator instance has an associated frame requested flag. It is initially set to false. Different circumstances can cause the frame requested flag to be set to true. These include the following:
-
Changes in the current time of the animator’s timeline
-
Changes in the current time of the animator’s corresponding worklet animation
Performing run animators resets the frame requested flag on animators to false.
5. Web Animations Integration
5.1. Worklet Animation
Worklet animation is a kind of animation that delegates animating its animation effect to an animator instance. It controls the lifetime and playback state of its corresponding animator instance.Being an animation, worklet animation has an animation effect and a timeline. However unlike other animations the worklet animation’s current time does not directly determine the animation effect’s local time (via its inherited time). Instead the associated animator instance controls the animation effect’s local time directly. Note that this means that the timeline’s current time does not fully determine the animation’s output.
Worklet animation has the following properties in addition to the Animation
interface:
-
- animation animator name
-
A string that identifies its animator definition.
-
- serialized options
-
A serializable options object that is used whe constructing a new animator instance.
-
- corresponding animator instance
The existence of corresponding animator instance for a worklet animation depends on the animation play state. See § 5.4 Web Animations Overrides for details on when and this correspondence changes.
[Exposed =Window ]interface :
WorkletAnimation Animation {constructor (DOMString ,
animatorName optional (AnimationEffect or sequence <AnimationEffect >)?=
effects null ,optional AnimationTimeline ?,
timeline optional any );
options readonly attribute DOMString ; };
animatorName
5.2. Creating a Worklet Animation
WorkletAnimation(animatorName, effects, timeline, options)
Creates a new WorkletAnimation
object using the following procedure:
-
Let documentAnimatorDefinitions be the associated document’s document animator definitions map.
-
If documentAnimatorDefinitions[animatorName] does not exists, throw an TypeError and abort the following steps.
-
If documentAnimatorDefinitions[animatorName] is
"invalid"
, throw an TypeError and abort the following steps. -
Let workletAnimation be a new
WorkletAnimation
object. -
Run the procedure to set the timeline of an animation on workletAnimation passing timeline as the new timeline or, if a timeline argument is not provided, passing the default document timeline of the
Document
associated with theWindow
that is the current global object. -
Let effect be the result corresponding to the first matching condition from below.
- If effects is a
AnimationEffect
object, -
Let effect be effects.
- If effects is a list of
AnimationEffect
objects, -
Let effect be a new
WorkletGroupEffect
with its children set to effects. - Otherwise,
-
Let effect be undefined.
- If effects is a
-
Let serializedOptions be the result of StructuredSerialize(options). Rethrow any exceptions.
-
Set the serialized options of workletAnimation to serializedOptions.
-
Set the animation animator name of workletAnimation to animatorName.
-
Run the procedure to set the target effect of an animation on workletAnimation passing effect as the new effect. Note that this may trigger action to set animator instance of worklet animation. See § 5.4 Web Animations Overrides for more details.
5.3. Worklet Animation Timing and Sync Model
This section describes how worklet animation’s timing model differs from other animations.
As described in § 5.1 Worklet Animation, the worklet animation’s current time does
not determine its animation effect’s local time. Instead the associated animator instance controls the animation effect’s local time directly. This means that the
animation effect’s local time is controlled from a WorkletGlobalScope
which may be in a parallel
execution context.
Here are a few implications of the above semantics:
-
Setting the current time or start time of a worklet animation does not necessarily change its output, but may change the animation play state.
-
Similarly, invoking
finish()
or updating a worklet animation’s playback rate does not necessarily change its output, but may change the animation play state -
Querying the animation effect’s local time using
getComputedTiming()
may return stale information, in the case where the animator instance is running in a parallel execution context.
If a Worklet Animation animation is executing in a parallel worklet execution context, the last known state of its Animator Effects should be periodically synced back to the main javascript execution context. The synchronization of effect values from the parallel worklet execution context to the main javascript execution context must occur before running the animation frame callbacks as part of the document lifecycle.
Note that due to the asynchronous nature of this animation model a script running in the main javascript execution context may see a stale value when reading a target property that is being animated by a Worklet Animation, compared to the value currently being used to produce the visual frame that is visible to the user. This is similar to the effect of asynchronous scrolling when reading scroll offsets in the main javascript execution context.
To sync local times to document for a given workletGlobalScope the user agent must perform the action that corresponds to the first matching condition from the following:
- If the workletGlobalScope is not running in a parallel execution context
-
perform the following steps immediately:
- If the workletGlobalScope is running in a parallel execution context
-
queue a task to run the following steps before running the animation frame callbacks as part of the document lifecycle:
-
Iterate over all animator instances in the animation worklet’s global scope animator instance set. For each such animator perform the following steps:
-
If animator’s sync requested flag is false skip the rest of the steps.
-
Let animatorEffect be animator’s effect.
-
Let effect be animatorEffect’s corresponding effect.
-
Set effect’s local time to animatorEffect’s local time.
-
Set animator’s sync requested flag to false.
-
To sync animation timings to worklet for a given workletAnimation the user agent must perform the following steps:
-
If workletAnimation does not have a corresponding animator instance, abort the following steps.
-
Let animator be workletAnimation’s corresponding animator instance.
-
Let workletGlobalScope be the
AnimationWorkletGlobalScope
associated with workletAnimation. -
- If the workletGlobalScope is not running in a parallel execution context
-
perform the following steps immediately.
- If the workletGlobalScope is running in a parallel execution context
-
queue a task to run the following steps:
-
Set animator’s animator current time to workletAnimation’s current time
-
Let animatorEffect be animator’s effect.
-
Let effect be animatorEffect’s corresponding effect.
-
Set the following properties on animatorEffect to be the same as effect:
-
Note: Notice that the local time is not synced from the document to animation worklet.
Come with appropriate mechanism’s for animator instance to get notified when its animation currentTime is changing e.g., via reverse(), finish() or playbackRate change. So that it can react appropriately. [Issue #811]
5.4. Web Animations Overrides
In addition to the existing conditions on when the animation is considered ready, a worklet animation is only considered ready when the following condition is also true:
-
the user agent has completed any setup required to create the worklet animation’s corresponding animator instance.
When a given worklet animation’s play state changes from idle to finished, running, or paused, run the procedure to associate animator instance of worklet animation given the worket animation as workletAnimation.
When a given worklet animation’s play state changes from finished, running or paused to idle, run the procedure to disassociate animator instance of worklet animationgiven the worklet animation as workletAnimation.
When a given worklet animation’s replace state changes from active to either persisted or removed run the procedure to [=disassociate animator instance of worklet animation]= given the worklet animation as workletAnimation.
In web-animation play state is updated before the actual change in particular some operations such as play() are asynchronous. We should really invoke these Animator related operation after the appropriate animation operation is complete instead of when play state has changed. This will require either finding (or introducing) q new hook in web animation or having override for each such async operation.
When the procedure to set the target effect of an animation for a given worklet animation is called, then set animator instance of worklet animation given the worklet animation as workletAnimation.
When the procedure to set the timeline of an animation for a given workletAnimation is called, then set animator instance of worklet animation given the worklet animation as workletAnimation.
When the procedure to set the current time or set the start time for a given worklet animation is called, then sync animation timings to worklet given the worklet animation as workletAnimation.
When the procedure to update the timing properties of an animation effect for a given effect is called and that effect is owned be a worklet animation, then sync animation timings to worklet given that worklet animation as workletAnimation.
To associate animator instance of worklet animation given workletAnimation, the user agent must run the following steps:
-
If workletAnimation has a corresponding animator instance, abort the following steps.
-
Let workletGlobalScope be the
AnimationWorkletGlobalScope
associated with workletAnimation. -
Queue a task on workletGlobalScope to run the procedure to create a new animator instance, passing:
-
The workletAnimation’s animation animator name as name.
-
The workletAnimation’s timeline as timeline.
-
The workletAnimation’s animation effect as effect.
-
The workletAnimation’s serialized options as options.
-
The workletGlobalScope as workletGlobalScope.
-
-
If the procedure was successful, set the resulting animator instance to be the corresponding animator instance of workletAnimation.
To disassociate animator instance of worklet animation given workletAnimation, the user age must run the following steps:
-
If workletAnimation does not have a corresponding animator instance, abort the following steps.
-
Let workletGlobalScope be the
AnimationWorkletGlobalScope
associated with workletAnimation. -
Let animatorInstance be workletAnimation’s corresponding animator instance.
-
Queue a task on the workletGlobalScope to run the procedure to remove an animator instance, passing animatorInstance as instance and workletGlobalScope as workletGlobalScope.
-
Set workletAnimation’s corresponding animator instance as undefined.
To set animator instance of worklet animation given workletAnimation, the user agent must run the following steps:
-
disassociate animator instance of worklet animation given workletAnimation.
-
associate animator instance of worklet animation given workletAnimation.
5.5. Effect Stack and Composite Order
As with other animations, worklet animations participate in the effect stack. A worklet animation does not have a specific animation class which means it has the same composite order as other Javascript created web animations.
6. Additional Related Concepts
6.1. Worklet Group Effect
This section is not normative.Group effect is a type of animation effect that enbales multiple child animation effects to be animated together as a group.
WorkletGroupEffect
is a type of group effect that allows its child effect’s local times to be mutated individually.
When a WorkletGroupEffect
is set as the animation effect of a worklet animation,
its corresponding animator instance can directly control the child effects' local
times. This allows a single worklet animation to coordinate multiple effects - see § 9.2 Example 2: Twitter header. for an example of such a use-case.
[Exposed =AnimationWorklet ]interface {
WorkletGroupEffect sequence <WorkletAnimationEffect >(); };
getChildren
The above interface exposes a conservative subset of GroupEffect proposed as part of web-animation-2. We should instead move this into a delta spec against the web-animation. [Issue #w3c/csswg-drafts#2071]
Note: Group Effect is currently an experimental feature and not well specified in web animations. So
the concept of WorkletGroupEffect
may change dramatically as Group Effect get specified.
See https://github.com/yi-gu/group_effect/blob/master/README.md for more details.
6.2. ScrollTimeline
This section is not normative.ScrollTimeline
is a new concept being proposed for addition to web animation API. It defines
an animation timeline whose time value depends on the scroll position of a scroll container. Worklet animations can have a scroll timeline and thus drive their scripted effects based
on a scroll offset.
Note: Access to input: We are interested on exposing additional user input beside scrolling (e.g., touch/pointer input) to these animations so that authors can create jank-free input driven animations which are not really possible today. We are still trying to figure out the right abstractions and mechanisms to do this.
7. Security Considerations
There are no known security issues introduced by these features.
8. Privacy Considerations
There are no known privacy issues introduced by these features.
9. Examples
9.1. Example 1: Spring timing.
Here we use Animation Worklet to create animation with a custom spring timing.< div id = 'target' ></ div > < script > await CSS. animationWorklet. addModule( 'spring-animator.js' ); targetEl= document. getElementById( 'target' ); const effect= new KeyframeEffect( targetEl, { transform: [ 'translateX(0)' , 'translateX(50vw)' ]}, { duration: 1000 } ); const animation= new WorkletAnimation( 'spring' , effect, document. timeline, { k: 2 , ratio: 0.7 }); animation. play(); </ script >
registerAnimator( 'spring' , class SpringAnimator{ constructor ( options= { k: 1 , ratio: 0.5 }) { this . timing= createSpring( options. k, options. ratio); } animate( currentTime, effect) { let delta= this . timing( currentTime); // scale this by target duration delta= delta* ( effect. getTimings(). duration/ 2 ); effect. localTime= delta; // TODO: Provide a method for animate to mark animation as finished once // spring simulation is complete, e.g., this.finish() // See issue https://github.com/w3c/css-houdini-drafts/issues/808 } }); function createSpring( springConstant, ratio) { // Normalize mass and distance to 1 and assume a reasonable init velocit // but these can also become options to this animator. const velocity= 0.2 ; const mass= 1 ; const distance= 1 ; // Keep ratio < 1 to ensure it is under-damped. ratio= Math. min( ratio, 1 - 1e-5 ); const damping= ratio* 2.0 * Math. sqrt( springConstant); const w= Math. sqrt( 4.0 * springConstant- damping* damping) / ( 2.0 * mass); const r= - ( damping/ 2.0 ); const c1= distance; const c2= ( velocity- r* distance) / w; // return a value in [0..distance] return function springTiming( timeMs) { const time= timeMs/ 1000 ; // in seconds const result= Math. pow( Math. E, r* time) * ( c1* Math. cos( w* time) + c2* Math. sin( w* time)); return distance- result; } }
9.2. Example 2: Twitter header.
An example of twitter profile header effect where two elements (avatar, and header) are updated in sync with scroll offset with an additional feature where avatar can have additional physic based movement based on the velocity and acceleration of the scrolling.< div id = 'scrollingContainer' > < div id = 'header' style = 'height: 150px' ></ div > < div id = 'avatar' >< img ></ div > </ div > // In document scope.< script > const headerEl= document. getElementById( 'header' ); const avatarEl= document. getElementById( 'avatar' ); const scrollingContainerEl= document. getElementById( 'scrollingContainer' ); const scrollTimeline= new ScrollTimeline({ scrollSource: scrollingContainerEl, orientation: 'block' , timeRange: 1000 , startScrollOffset: 0 , endScrollOffset: headerEl. clientHeight}); const effects= [ /* avatar scales down as we scroll up */ new KeyframeEffect( avatarEl, { transform: [ 'scale(1)' , 'scale(0.5)' ]}, { duration: scrollTimeline. timeRange}), /* header loses transparency as we scroll up */ new KeyframeEffect( headerEl, { opacity: [ 0 , 0.8 ]}, { duration: scrollTimeline. timeRange}) ]; await CSS. animationWorklet. addModule( 'twitter-header-animator.js' ); const animation= new WorkletAnimation( 'twitter-header' , effects, scrollTimeline); animation. play(); </ script >
// Inside AnimationWorkletGlobalScope. registerAnimator( 'twitter-header' , class HeaderAnimator{ constructor ( options, state= { velocity: 0 , acceleration: 0 }) { // `state` is either undefined (first time) or it is the previous state (after an animator // is migrated between global scopes). this . velocity= state. velocity; this . acceleration= state. acceleration; } animate( currentTime, effect) { const scroll= currentTime; // scroll is in [0, 1000] range if ( this . prevScroll) { this . velocity= scroll- this . prevScroll; this . acceleration= this . velocity- this . prevVelocity; } this . prevScroll= scroll; this . prevVelocity= velocity; // Drive the output group effect by setting its children local times individually. effect. children[ 0 ]. localTime= scroll; effect. children[ 1 ]. localTime= curve( velocity, acceleration, scroll); } state() { // Invoked before any migration attempts. The returned object must be structure clonable // and will be passed to constructor to help animator restore its state after migration to the // new scope. return { this . velocity, this . acceleration} } }); curve( scroll, velocity, acceleration) { return /* compute an return a physical movement curve based on scroll position, and per frame velocity and acceleration. */ ; }
9.3. Example 3: Parallax backgrounds.
A simple parallax background example.< style > . parallax { position : fixed ; top : 0 ; left : 0 ; opacity : 0.5 ; } </ style > < div id = 'scrollingContainer' > < div id = "slow" class = "parallax" ></ div > < div id = "fast" class = "parallax" ></ div > </ div > < script > await CSS. animationWorklet. addModule( 'parallax-animator.js' ); const parallaxSlowEl= document. getElementById( 'slow' ); const parallaxFastEl= document. getElementById( 'fast' ); const scrollingContainerEl= document. getElementById( 'scrollingContainer' ); const scrollTimeline= new ScrollTimeline({ scrollSource: scrollingContainerEl, orientation: 'block' , timeRange: 1000 }); const scrollRange= scrollingContainerEl. scrollHeight- scrollingContainerEl. clientHeight; const slowParallax= new WorkletAnimation( 'parallax' , new KeyframeEffect( parallaxSlowEl, { 'transform' : [ 'translateY(0)' , 'translateY(' + - scrollRange+ 'px)' ]}, { duration: scrollTimeline. timeRange}), scrollTimeline, { rate: 0.4 } ); slowParallax. play(); const fastParallax= new WorkletAnimation( 'parallax' , new KeyframeEffect( parallaxFastEl, { 'transform' : [ 'translateY(0)' , 'translateY(' + - scrollRange+ 'px)' ]}, { duration: scrollTimeline. timeRange}), scrollTimeline, { rate: 0.8 } ); fastParallax. play(); </ script >
// Inside AnimationWorkletGlobalScope. registerAnimator( 'parallax' , class ParallaxAnimator{ constructor ( options) { this . rate_= options. rate; } animate( currentTime, effect) { effect. localTime= currentTime* this . rate_; } });