diff --git a/css-animationworklet/CONTRIBUTING.md b/css-animationworklet/CONTRIBUTING.md new file mode 100644 index 00000000..82877ce8 --- /dev/null +++ b/css-animationworklet/CONTRIBUTING.md @@ -0,0 +1,23 @@ +# Web Platform Incubator Community Group + +This repository is being used for work in the Web Platform Incubator Community Group, governed by the [W3C Community License +Agreement (CLA)](http://www.w3.org/community/about/agreements/cla/). To contribute, you must join +the CG. + +If you are not the sole contributor to a contribution (pull request), please identify all +contributors in the pull request's body or in subsequent comments. + +To add a contributor (other than yourself, that's automatic), mark them one per line as follows: + +``` ++@github_username +``` + +If you added a contributor by mistake, you can remove them in a comment with: + +``` +-@github_username +``` + +If you are making a pull request on behalf of someone else but you had no part in designing the +feature, you can remove yourself with the above syntax. diff --git a/css-animationworklet/LICENSE.md b/css-animationworklet/LICENSE.md new file mode 100644 index 00000000..98fad556 --- /dev/null +++ b/css-animationworklet/LICENSE.md @@ -0,0 +1,5 @@ +All Reports in this Repository are licensed by Contributors under the +[W3C Software and Document +License](http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). Contributions to +Specifications are made under the [W3C CLA](https://www.w3.org/community/about/agreements/cla/). + diff --git a/css-animationworklet/Makefile b/css-animationworklet/Makefile new file mode 100644 index 00000000..04d7e27d --- /dev/null +++ b/css-animationworklet/Makefile @@ -0,0 +1,20 @@ +# $Id: Makefile,v 1.5 2008/02/06 14:05:15 mike Exp $ +# +# FIXME: New documentation needed. +# +# Use "make REMOTE=1" to use remote bikeshed + +SOURCEFILE=index.bs +OUTPUTFILE=index.html +PREPROCESSOR=bikeshed.py +REMOTE_PREPROCESSOR_URL=https://api.csswg.org/bikeshed/ + +all: $(OUTPUTFILE) + +$(OUTPUTFILE): $(SOURCEFILE) +ifneq (,$(REMOTE)) + curl $(REMOTE_PREPROCESSOR_URL) -F file=@$(SOURCEFILE) > "$@" +else + $(PREPROCESSOR) -f spec "$<" "$@" +endif + diff --git a/css-animationworklet/README.md b/css-animationworklet/README.md new file mode 100644 index 00000000..858e7a24 --- /dev/null +++ b/css-animationworklet/README.md @@ -0,0 +1,383 @@ +# Animation Worklet Explainer +--- + +# Overview + +AnimationWorklet is a new primitive for creating scroll-linked and other high performance +procedural animations on the web. It is being incubated here as part of the +[CSS Houdini task force](https://github.com/w3c/css-houdini-drafts/wiki), and if successful will be +transferred to that task force for full standardization. + +# Introduction + +Scripted effects (written in response to `requestAnimationFrame` or async `onscroll` events) are +rich but are subject to main thread jankiness. On the other hand, accelerated CSS transitions and +animations can be fast (for a subset of *accelerated* properties) but are not rich enough to enable +[many common use cases](#motivating-use-cases) and currently have no way to access scroll offset +and other user input. This is why scripted effects are still very popular for implementing common +effects such as hidey-bars, parallax, position:sticky, and etc. We believe (and others +[agree][roc-thread]) that there is a need for a new primitive for creating fast and rich visual +effects with the ability to respond to user input such as scroll. + +This document proposes an API to create custom animations that execute inside an isolated execution +environment, *worklet*. It aims to be compatible with Web Animations and uses existing constructs as +much as possible. We believe this API hits a sweet spot in balancing among performance, richness, +and rationality for addressing our key use cases. + +This design supersedes our [CompositorWorker proposal][cw-proposal]. + +# Motivating Use Cases + +* Scroll-linked effects: + - Parallax ([demo](https://googlechromelabs.github.io/houdini-samples/animation-worklet/parallax-scrolling/)) + - Animated scroll headers, eg. "hidey-bars" ([demo](https://googlechromelabs.github.io/houdini-samples/animation-worklet/twitter-header/), [twitter](https://twitter.com/LEGO_Group), [Polymer `paper-scroll-header-panel`](https://elements.polymer-project.org/elements/paper-scroll-header-panel?view=demo:demo/index.html)) + - Springy sticky elements ([demo](http://googlechromelabs.github.io/houdini-samples/animation-worklet/spring-sticky/)) + +* Animations with custom timing functions (particularly those that are not calculable a priori) + + - Spring timing function ([demo](https://googlechromelabs.github.io/houdini-samples/animation-worklet/spring-timing/)) + +* Location tracking and positioning: + + - Position: sticky + +* Procedural animation of multiple elements in sync: + + - Efficient Expando ([demo](http://googlechromelabs.github.io/houdini-samples/animation-worklet/expando/), [more info](https://developers.google.com/web/updates/2017/03/performant-expand-and-collapse)) + - Compositing growing / shrinking box with border (using 9 patch) + +* Animating scroll offsets: + + - Having multiple scrollers scroll in sync e.g. diff viewer keeping old/new in sync when you + scroll either ([demo](https://googlechromelabs.github.io/houdini-samples/animation-worklet/sync-scroller/)) + - Implementing smooth scroll animations (e.g., custom physic based fling curves) + +***Note***: Demos work best in the latest Chrome Canary with the experimental +web platform features enabled (`--enable-experimental-web-platform-features` +flag) otherwise they fallback to using main thread rAF to emulate the behaviour. + + +# Key Concepts + +## Animation Worklet Global Scope +A [worklet global scope](https://drafts.css-houdini.org/worklets/#the-global-scope) that is created +by Animation Worklet. Note that Animation Worklet creates multiple such scopes and uses them to +execute user defined effects. + +## WorkletAnimation +`WorkletAnimation` is a subclass of Animation that can be used to create an custom animation effect +that runs inside a standalone animation worklet scope. A worklet animation has a corresponding +animator instance in a animation worklet scope which is responsible to drive its keyframe effects. +Here are the key differences compared to a regular web animation: + - AnimationId should match a specific animator class registered in the animation worklet scope. + - `WorkletAnimation` may have multiple timelines (including `ScrollTimeline`s). + - `WorkletAnimation` may have a custom properties bag that can be cloned and provided to animator + constructor when it is being instantiated. + +Note that worklet animations expose same API surface as other web animations and thus they may be +created, played, paused, inspected, and generally controlled from main document scope. Here is how +various methods roughly translate: + + - cancel(): cancels the animation and the corresponding animator instance is removed. + - play(): starts the animation and the corresponding animator instance may get its `animate` function + called periodically as a result of changes in its timelines. + - pause(): pauses the animation and the corresponding animator instance no longer receives + `animate` calls. + - finish(): invokes `finish` on the corresponding animator instance. + - reverse() or mutating playbackRate: invokes `playbackRateChanged` on the corresponding + animator instance. + +## ScrollTimeline +[ScrollTimeline](https://wicg.github.io/scroll-animations/#scrolltimeline) is a concept introduced in +scroll-linked animation proposal. It defines an animation timeline whose time value depends on +scroll position of a scroll container. `ScrollTimeline` can be used an an input timeline for +worklet animations and it is the intended mechanisms to give read access to scroll position. + +## GroupEffect +[GroupEffect](https://w3c.github.io/web-animations/level-2/#the-animationgroup-interfaces) is a +concept introduced in Web Animation Level 2 specification. It provides a way to group multiple +effects in a tree structure. `GroupEffect` can be used as the output for worklet animations. It +makes it possible for worklet animation to drive effects spanning multiple elements. + +**TODO**: At the moment, `GroupEffect` only supports just two different scheduling models (i.e., +parallel, sequence). These models governs how the group effect time is translated to its children +effect times by modifying the child effect start time. AnimationWorklet allows a much more flexible +scheduling model by making it possible to to set children effect's local time directly. In other +words we allow arbitrary start time for child effects. This is something that needs to be added to +level 2 spec. + +## Multiple Timelines +Unlike typical animations, worklet animations can be attached to multiple timelines. This is +necessary to implement key usecases where the effect needs to smoothly animate across different +timelines (e.g., scroll and wall clock). + +### Primary Timeline +The first timeline is considered the *primary timeline*. The only purpose of the primary timeline is +to make integration with existing web animation machinery easier, in particular the primary timeline +time will be used anywhere the web animation API needs to expose a time value, for example in +[event timeline time](https://w3c.github.io/web-animations/level-2/#event-timeline-time), or +[event current time](https://w3c.github.io/web-animations/level-2/#event-current-time). + + +**TODO**: We are considering API designs that can make it possible for an animation to observe multiple +timelines but only gets activated on a (dynamic) subset of them. This ensures we can be more +efficient when updating the animation. + +## Animator Migration +The animators are not guaranteed to run in the same global scope (or underlying thread) for their +lifetime duration. For example, a user agents is free to initially run the animator on main thread +but later decide to migrate it off main thread to get certain performance optimizations. To allow +worklet animators to keep their state across migrations, the API provides the following lifetime +hooks: + +```js +// in document scope +new WorklerAnimation('animation-with-local-state', [], [], {value: 1}); +``` + + +```js +registerAnimator('animation-with-local-state', class { + constructor(options, state) { + // |options| may be either: + // - The user provided options bag passed into the WorkletAnimation constructor on first initialization i.e, {value: 1}. + // - The object returned by |destroy| after each migration i.e. {value: 42}. + this.options_ = options; + this.state_ = state || {value: Math.random()}; + } + + animate(timelines, effect) { + this.state_.value += 0.1; + effect.localTime = this.state_.value; + } + + destroy() { + // Invoked before each 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.state_; + } +}); +``` + +# Examples + +**TODO**: Add gifs that visualize these effects + +## Hidey Bar +An example of header effect where a header is moved with scroll and as soon as finger is lifted +it animates fully to close or open position depending on its current position. + + +``` html +
+ +
content
+
+ + +``` + +hidey-bar-animator.js: +```js +registerAnimator('hidey-bar', class { + + constructor(options) { + this.scrollTimeline_ = options.scrollTimeline; + this.documentTimeline_ = options.documentTimeline; + } + + animate(currentTime, effect) { + const scroll = this.scrollTimeline_.currentTime; // [0, 100] + const time = this.documentTimeline_.currentTime; + + // **TODO**: use a hypothetical 'phase' property on timeline as a way to detect when user is no + // longer actively scrolling. This is a reasonable thing to have on scroll timeline but we can + // fallback to using a timeout based approach as well. + const activelyScrolling = this.scrollTimeline_.phase == 'active'; + + let localTime; + if (activelyScrolling) { + this.startTime_ = undefined; + localTime = scroll; + } else { + this.startTime_ = this.startTime_ || time; + // Decide on close/open direction depending on how far we have scrolled the header + // This can even do more sophisticated animation curve by computing the scroll velocity and + // using it. + this.direction_ = scroll >= 50 ? +1 : -1; + localTime = this.direction_ * (time - this.startTime_); + } + + // Drive the output effects by setting its local time. + effect.localTime = localTime; +}); + +``` + + +## Twitter Header +An example of twitter profile header effect where two elements (avatar, and header) are updated in +sync with scroll offset. + +```html + +
+ +
+
+ + +``` + +twitter-header-animator.js: +```js +registerAnimator('twitter-header', class { + constructor(options) { + this.timing_ = new CubicBezier('ease-out'); + } + + clamp(value, min, max) { + return Math.min(Math.max(value, min), max); + } + + animate(currentTime, effect) { + const scroll = currentTime; // [0, 1] + + // Drive the output group effect by setting its children local times. + effect.children[0].localTime = scroll; + // Can control the child effects individually +    effect.children[1].localTime = this.timing_(this.clamp(scroll, 0, 1)); + } +}); + +``` + +### Parallax +```html + +
+
+
+
+ + + +``` + +parallax-animator.js: + +```js +// Inside AnimationWorkletGlobalScope. +registerAnimator('parallax', class { + constructor(options) { + this.rate_ = options.rate; + } + + animate(currentTime, effect) { + effect.localTime = currentTime * this.rate_; + } +}); +``` + + +# WEBIDL + +`WorkletAnimation` extends `Animation` and adds a getter for its timelines. +Its constructor takes: + - `animatiorId` which should match the id of an animator which is registered in +the animation worklet scope. + - A sequence of effects which are passed into a `GroupEffect` constructor. + - A sequence of timelines, the first one of which is considered primary timeline and passed to + `Animation` constructor. + +```webidl + +[Constructor (DOMString animatorName, + optional (AnimationEffectReadOnly or array)? effects = null, + AnimationTimeline? timeline, + optional WorkletAnimationOptions)] +interface WorkletAnimation : Animation { + readonly attribute DOMString animatorName; +} +``` + +**TODO**: At the moment `GroupEffect` constructor requires a timing but this seems unnecessary for +`WorkletAnimation` where it should be possible to directly control individual child effect local +times. We need to bring this up with web-animation spec. + +`AnimationEffectReadOnly` gets a writable `localTime` attribute which may be used to drive the +effect from the worklet global scope. + +```webidl +partial interface AnimationEffectReadOnly { + [Exposed=Worklet] + // Intended for use inside Animation Worklet scope to drive the effect. + attribute double localTime; +}; + +``` + + + + +# Specification +The [draft specification](https://wicg.github.io/animation-worklet) is updated to reflect the +the agreed design direction from Houdini Tokyo F2F meeting and recent changes in the same direction. + + +[roc-thread]: https://lists.w3.org/Archives/Public/public-houdini/2015Mar/0020.html +[cw-proposal]: https://github.com/w3c/css-houdini-drafts/blob/master/composited-scrolling-and-animation/Explainer.md diff --git a/css-animationworklet/WIP.md b/css-animationworklet/WIP.md new file mode 100644 index 00000000..9b36c15a --- /dev/null +++ b/css-animationworklet/WIP.md @@ -0,0 +1,103 @@ +# Open API Questions +--- + + +## Creation/Registration timing + +What should happen if an animation is created before the animator is registered? + +## Timelines + +* observe-only timelines? i.e., have access to timeline but the animate is not triggered when its + value changes. + +* Should we have a Timeline.currentTime and Timeline.localTime, where the latter is + the former but offset by startTime & scaled by playbackRate? + +* Access to the actual scroll position in the ScrollTimeline + +* Access to scroll phase (inactive, active, inertial etc.) + + +## Updating Elements + +For some effects we need to be able to add new participating elements without +restarting the effect. Here is an initial idea on how this can work. + +```js +// Effects and data can change after some time. +// We might want to break this out into separate functions or optional +// updates so you can just update options or just effects and options +// without having to pass other parameters again. +anim.update({ + [ /* new list of effects? */], + [ /* new list of timelines */], + {/* options */} +}); +``` + +```js +// In worklet scope +class MyAnimator{ + update(options) { + // this is a V2 concept, + } +} +``` + +## CSS Notation + +We are not proposing including this in the initial spec, but including some +preliminary thoughts here so that we can keep the eventual declarative CSS +specification in mind. + +index.html: +```html +<-- animator instance is declared here, with its timelines --> +
+ <-- effect timing is declared here and assigned to above animator --> +
+
+
+
+``` + +style.css: +```css +#main { + animation: worklet('twitter-header') + animation-timeline: scroll(#scroller_element.....) /* https://wicg.github.io/scroll-animations/#animation-timeline */ +} + +/* These are descendants of the animation */ +#main .header { + animation-group: 'twitter-header' 'header' #... + /* This syntax should be similar to what the plan is for Web Animation Group Effects */ +} + +#main .avatar { + animation-group: 'twitter-header' 'avatar' #... +} +``` + +This is equivalent to calling: + +```js +new WorkletAnimation('twitter-header', + [ + new KeyFrameEffect(.header[0], [], {}), + new KeyFrameEffect(.avatar, [], {}), + new KeyFrameEffect(.header[1], [], {}), + ], + [new ScrollingTimeline(#selector, {...})], + { elements: [ + /* This is admittedly a bit magical. */ + {'name': 'header'}, + {'name': 'avatar'}, + {'name': 'header'}, + ]} +).play(); + +``` + +Adding new elements that match the selector will be equivalent to invoking `update`. diff --git a/css-animationworklet/img/AnimationWorklet-threading-model.svg b/css-animationworklet/img/AnimationWorklet-threading-model.svg new file mode 100644 index 00000000..26d7ba93 --- /dev/null +++ b/css-animationworklet/img/AnimationWorklet-threading-model.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/css-animationworklet/img/WorkletAnimation-timing-model.svg b/css-animationworklet/img/WorkletAnimation-timing-model.svg new file mode 100644 index 00000000..c29d115e --- /dev/null +++ b/css-animationworklet/img/WorkletAnimation-timing-model.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/css-animationworklet/index.bs b/css-animationworklet/index.bs new file mode 100644 index 00000000..89901124 --- /dev/null +++ b/css-animationworklet/index.bs @@ -0,0 +1,917 @@ + + + + +
+urlPrefix: https://heycam.github.io/webidl/; type: dfn;
+    text: NotSupportedError
+    urlPrefix: #dfn-;
+        text: callback this value
+        text: exception
+        text: throw
+        url: throw; text: thrown
+    urlPrefix: #;
+        url: Function; text: Function
+        url: VoidFunction; text: VoidFunction
+    url: invoke-a-callback-function; text: Invoke
+    url: construct-a-callback-function; text: constructing
+    url: es-type-mapping; text: converting
+urlPrefix: https://html.spec.whatwg.org/#; type: dfn;
+    url: run-the-animation-frame-callbacks; text: running the animation frame callbacks
+urlPrefix: http://w3c.github.io/html/infrastructure.html#; type: dfn;
+    text: structuredserialize
+    text: structureddeserialize
+urlPrefix: https://www.w3.org/TR/css3-transitions/#; type: dfn;
+    text: animatable properties
+urlPrefix: https://w3c.github.io/web-animations/#; type: dfn;
+    url: the-documents-default-timeline; text: default document timeline
+    url: concept-animation; text: animation
+    text: effect value
+    text: effect stack
+    text: target property
+    text: timeline
+    text: animation effect
+    text: current time
+    text: local time
+    text: inherited time
+    text: ready
+    text: play state
+    text: playback rate
+    text: set the target effect of an animation
+    text: set the timeline of an animation
+    text: finished
+    text: idle
+    text: paused
+    text: pending
+    text: running
+    text: composite operation
+    text: animation class
+urlPrefix: https://w3c.github.io/web-animations/level-2/#;
+    type: dfn;
+        text: group effect
+        text: child effect
+urlPrefix: https://tc39.github.io/ecma262/#sec-; type: dfn;
+    text: IsCallable
+    text: IsConstructor
+    text: HasProperty
+    url: ecmascript-data-types-and-values; text: Type
+    url: map-objects; text:map object
+    url: get-o-p; text: Get
+    url: set-o-p-v-throw; text: Set
+    urlPrefix: native-error-types-used-in-this-standard-
+        text: TypeError
+urlPrefix: https://www.w3.org/TR/hr-time-2/#dom-; type: dfn
+    text: DOMHighResTimeStamp
+urlPrefix: https://wicg.github.io/scroll-animations/#; type: interface
+    url: scrolltimeline; text: ScrollTimeline
+    url: dictdef-scrolltimelineoptions; text: ScrollTimelineOptions
+    url: dom-scrolltimeline-scrollsource; text: scrollSource
+urlPrefix: https://wicg.github.io/scroll-animations/#; type: dfn
+    url: current-time-algorithm; text: current time of the ScrollTimeline;
+
+ +
+{
+    "explainer": {
+        "href": "https://github.com/WICG/animation-worklet/blob/gh-pages/README.md",
+        "title": "Animation Worklet Explainer",
+        "status": "CR",
+        "publisher": "WICG",
+        "deliveredBy": [
+            "https://github.com/WICG/animation-worklet//"
+        ]
+    }
+}
+
+ +Introduction {#intro} +===================== +This section is not normative. + +This document introduces a new primitive for creating scroll-linked and other high performance +procedural animations on the web. For details on the rationale and motivation see [[explainer]]. + +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. + +Relationship to the Web Animations API {#relationship-to-web-animations} +------------------------------------------------------------------------ + +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 many of the Web Animation APIs. However +Animation Worklet animations follow a different timing model that enables them to be script-driven, +stateful, and runnable in a parallel worklet execution context. As such Web Animation APIs that seek +or alter the input time (reverse, finish, etc.) have different semantics for Animation Worklet +animations. + + +Threading Model {#threading-model} +================================== +This section is not normative. + +Animation Worklet is designed to be thread-agnostic. Rendering engines may create one or more +parallel worklet execution contexts separate from the main javascript execution context, e.g., on +their own dedicated threads. Rendering engines may then choose to assign Animation Worklet +animations to run in such contexts. Doing so allows Animation Worklet animations to avoid being +impacted by main thread jank. + +Rendering engines may wish to make a best-effort attempt to execute animate callbacks synchronously +with visual frame production to ensure smooth animation. However it is legal for rendering engines +to produce visual frames without blocking to receive animation updates from a worklet (i.e., letting +the effects slip behind). For example, this could occur when the animate function callback is +unable to complete before the frame deadline. + +We believe that scripted animations which are run in a parallel execution environment and which +limit themselves to animating properties which do not require the user agent to consult main thread +will have a much better chance of meeting the strict frame budgets required for smooth playback. + +If a Worklet Animation animation is executing in a parallel worklet execution context, the last +known state of its animation 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 in 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. + + +
+ Overview of the animation worklet threading model. +
+ Overview of the animation worklet threading model.
+ + A simplified visualization of how animators running in a parallel execution environment can sync + their update to main thread while remaining in sync with visual frame production. +
+
+ +Animation Worklet {#animation-worklet-desc} +============================== +Animation Worklet is a {{Worklet}} responsible for all classes related to custom +animations. The worklet can be accessed via {{animationWorklet}} attribute. + +The {{animationWorklet}}'s worklet global scope type is {{AnimationWorkletGlobalScope}}. + +{{AnimationWorkletGlobalScope}} represents the global execution context of {{animationWorklet}}. + + +partial namespace CSS { + [SameObject] readonly attribute Worklet animationWorklet; +}; + + + +[ Exposed=AnimationWorklet, Global=AnimationWorklet ] +interface AnimationWorkletGlobalScope : WorkletGlobalScope { + void registerAnimator(DOMString name, VoidFunction animatorCtor); +}; + + + +
+ Note: This is how the class should look. +
+        class FooAnimator {
+            constructor(options) {
+                // Called when a new animator is instantiated.
+            }
+            animate(currentTime, effect) {
+                // Animation frame logic goes here.
+            }
+        }
+    
+
+ + + +Animator Definition {#animator-definition-desc} +==================== +An animator definition is a struct which describes the author defined custom +animation as needed by {{AnimationWorkletGlobalScope}}. It consists of: + + - An animator name <>#. + + - A class constructor which is a VoidFunction callback function type. + + - An animate function which is a Function callback function type. + + - A destroy function which is a Function callback function type. + + +Registering an Animator Definition {#registering-animator-definition} +------------------------------------- +An {{AnimationWorkletGlobalScope}} has a animator name to animator definition map. +The map gets populated when {{registerAnimator(name, animatorCtorValue)}} is called. + +
+ +When the registerAnimator(|name|, |animatorCtorValue|) +method is called in a {{AnimationWorkletGlobalScope}}, the user agent must run the +following steps: + + 1. If |name| is not a valid <>, throw a TypeError and abort all these + steps. + + 2. If |name| exists as a key in the animator name to animator definition map, + throw a NotSupportedError and abort all these steps. + + 3. If the result of IsConstructor(|animatorCtorValue|) is false, throw a + TypeError and abort all these steps. + + 4. Let |animatorCtor| be the result of converting animatorCtorValue to the + VoidFunction callback function type. If an exception is thrown, rethrow the + exception and abort all these steps. + + 4. Let |prototype| be the result of Get(|animatorCtorValue|, "prototype"). + + 5. If the result of Type(|prototype|) is not Object, throw a TypeError + and abort all these steps. + + 6. Let |animateValue| be the result of Get(|prototype|, "animate"). + + 7. Let |animate| be the result of converting |animateValue| to the Function + callback function type. If an exception is thrown, rethrow the exception and abort + all these steps. + + 8. Let |destroyValue| be the result of Get(|prototype|, "onDestroy"). + + 9. Let |destroy| be the result of converting |destroyValue| to the Function + callback function type. If an exception is thrown, rethrow the exception and abort + all these steps. + + + 8. Let |definition| be a new animator definition with: + + - animator name being |name| + + - class constructor being |animatorCtor| + + - animate function being |animate| + + - destroy function being |destroy| + + + 9. Add the key-value pair (|name| - |definition|) to the animator name to animator + definition map. +
+ + +Animator Instance {#animator-instance-section} +====================================== + +An animator instance is a struct which describes a fully realized custom animation +instance in an {{AnimationWorkletGlobalScope}}. It has a reference to an animator definition +and owns the instance specific state such as animation effect and timelines. It consists of: + + - An animator name. + + - An animation requested flag. + + - An animator effect which is an animation effect. + + - An animator current time which is the corresponding worklet animation's current + time. + + - An animator timeline which is a timeline. + + - An animator attached timelines which is list of attached timelines + + - An animator serialized options which is a serializable object. + + +Creating an Animator Instance {#creating-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: + + 1. Let the |definition| be the result of looking up |name| on the |workletGlobalScope|'s + animator name to animator definition map. + + If |definition| does not exist abort the following steps. + + 2. Let |animatorCtor| be the class constructor of |definition|. + + 3. Let |timelineList| be a new list with |timeline| added to it. + + 4. Let |options| be StructuredDeserialize(|serializedOptions|). + + 5. Let |state| be StructuredDeserialize(|serializedState|). + + 6. Let |animatorInstance| be the result of constructing |animatorCtor| with + [|options|, |state| as args. If an exception is thrown, rethrow the exception and abort all + these steps. + + 7. Set the following on |animatorInstance| with: + - animator name being |name| + - animation requested flag being frame-current + - animator current time being unresolved + - animator effect being |effect| + - animator timeline being |timeline| + - animator attached timelines being |timelineList| + - animator serialized options being |options| + + 8. Add |animatorInstance| to |workletGlobalScope|'s animator instance set. + +
+ + +Running Animators {#running-animators} +-------------------------------------- + +When a user agent wants to produce a new animation frame, if for any animator instance the +associated animation requested flag is frame-requested then the the user agent +must run animators for the current frame. + +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 iterate over all animator instances in the |workletGlobalScope|'s animator +instance set. For each such |instance| the user agent must perform the following steps: + + 1. Let |animatorName| be |instance|'s animator name + + 2. Let the |definition| be the result of looking up |animatorName| on the |workletGlobalScope|'s + animator name to animator definition map. + + If |definition| does not exist then abort the following steps. + + 3. If the animation requested flag for |instance| is frame-current or the effect + belonging to the |instance| will not be visible within the visual viewport of the current + frame the user agent may abort all the following steps. + + Issue: Consider giving user agents permission to skip running animator instances to + throttle slow animators. + + 4. Let |animateFunction| be |definition|'s animate function. + + 5. Let |currentTime| be animator current time of |instance|. + + 6. Let |effect| be animator effect of |instance|. + + 7. Invoke |animateFunction| with arguments «|currentTime|, |effect|», + and with |instance| as the callback this value. + +
+Note: Although inefficient, it is legal for the user agent to run animators multiple times +in the same frame. + +Removing an Animator Instance {#removing-animator} +----------------------------------------- + +
+ +To remove an animator instance given |instance| and |workletGlobalScope| the user agent +must run the following steps: + +1. Remove |instance| from |workletGlobalScope|'s animator instance set. + +
+ + +Migrating an Animator Instance {#migrating-animator} +----------------------------------------- + +User agents are responsible for assigning an animator instance to a {{WorkletGlobalScope}}. +There can be many such {{WorkletGlobalScope}}s, which may exist across different threads or +processes. To give the most flexibility to user agents in this respect, we allow migration of an +animator instance while it is running. The basic mechanism is to serialize the internal state +of any author-defined effect, and restore it after migration. + +
+ +To migrate an animator instance from one {{WorkletGlobalScope}} to another, given +|instance|, |sourceWorkletGlobalScope|, |destinationWorkletGlobalScope|, the user agent +must run the following steps : + + 1. Let |serializedState| be undefined. + + 2. Queue a task on |sourceWorkletGlobalScope| to run the following steps: + + 1. Let |animatorName| be |instance|'s animator name + + 2. Let |definition| be the result of looking up |animatorName| on |sourceWorkletGlobalScope|'s + animator name to animator definition map. + + If |definition| does not exist then abort the following steps. + + 3. Let |destroyFunction| be the destroy function of |definition|. + + + 4. Invoke |destroyFunction| with |instance| as the callback this value and + let |state| be the result of the invocation. If any exception is thrown, rethrow the + exception and abort the following steps. + + 5. Set |serializedState| to be the result of StructuredSerialize(|state|). + If any exception is thrown, then abort the following steps. + + 6. Run the procedure to remove an animator instance given |instance|, and + |sourceWorkletGlobalScope|. + + 2. Wait for the above task to complete. If the task is aborted, abort the following steps. + + 3. Queue a task on |destinationWorkletGlobalScope| to run the following steps: + + 1. Run the procedure to create a new animator instance given: + - The |instance|'s animator name as name. + - The |instance|'s animator timeline as timeline. + - The |instance|'s animator effect as effect. + - The |instance|'s animator serialized options as options. + - The |serializedState| as state. + - The |destinationWorkletGlobalScope| as workletGlobalScope. + +
+ + +Requesting Animation Frames {#requesting-animation-frames} +---------------------------------------------------------- + +Each animator instance has an associated animation requested flag. It must be +either frame-requested or frame-current. It is initially set to +frame-current. Different circumstances can cause the animation requested flag to be +set to frame-requested. These include the following: + - Changes in the current time of any timeline in the animator's animator attached timelines + - Changes in the current time of the animator's corresponding Worklet Animation + +[[#running-animators]] resets the animation requested flag on animators to +frame-current. + + +Web Animations Integration {#web-animation-integration} +=============================== + + +Worklet Animation {#worklet-animation-desc} +------------------------------------------- +Worklet animation is a kind of animation that delegates the animation playback 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: + - an animation animator name which identifies its animator definition. + - a serialized options which is serializable object that is used when + constructing a new animator instance. + +
+ Overview of the WorkletAnimation timing model. +
+ Overview of the WorkletAnimation timing model.
+ + The animation current time is input to the animator instance, which produces a local time value + for the animation effect. If the animator instance is running in a parallel global scope the + implementation may also choose to use the local time value to produce the final effect value and + update the visuals in parallel. + +
+
+ + +Creating a Worklet Animation {#creating-worklet-animation} +----------------------------------------------------------- + + + + +[Constructor (DOMString animatorName, + optional (AnimationEffect or sequence<AnimationEffect>)? effects = null, + optional AnimationTimeline? timeline, + optional any options)] +interface WorkletAnimation : Animation { + readonly attribute DOMString animatorName; +}; + + + + +
+WorkletAnimation(|animatorName|, |effects|, |timeline|, |options|) + +Creates a new {{WorkletAnimation}} object using the following procedure. + + 1. Let |workletAnimation| be a new {{WorkletAnimation}} object. + + 2. 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 the + {{Window}} that is the current global object. + + 3. 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. + + 4. Run the procedure to set the target effect of an animation on |workletAnimation| + passing |effect| as the new effect. + + 5. Let |serializedOptions| be the result of StructuredSerialize(|options|). + Rethrow any exceptions. + + 6. Set the serialized options of |workletAnimation| to |serializedOptions|. + + 7. Set the animation animator name of |workletAnimation| to |animatorName|. +
+ + +Worklet Animation timing model {#timing-model} +------------------------------------ + +This section describes how worklet animation's timing model differs from other +animations. + +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. + +As described in [[#worklet-animation-desc]], 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 {{Animation/finish()}} or updating a worklet animation's playback + rate will only change the animation play state and may not change the output. + - Querying the animation effect's local time using {{AnimationEffect/getComputedTiming()}} + may return stale information, in the case where the animator instance is running in a + parallel execution context. + +Issue(63): 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. + + +Interaction with Animator Instances {#worklet-animation-animator-instances} +----------------------------------- + +A worklet animation corresponds to at most one animator instance at any time, and may +have no current corresponding animator instance. The correspondance of an animator +instance for a worklet animation depends on the animation play state. + +
+ +To associate animator instance of worklet animation given |workletAnimation|, +the user agent must run the following steps: + + 1. If |workletAnimation| has a corresponding animator instance, abort the following steps. + 2. Let |workletGlobalScope| be the {{AnimationWorkletGlobalScope}} associated with + |workletAnimation|. + 3. 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. + 4. If the procedure was successful, set the resulting animator instance as corresponding to + |workletAnimation|. + +
+ +
+ +To disassociate animator instance of worklet animation given +|workletAnimation|, the user age must run the following steps: + + 1. If |workletAnimation| does not have a corresponding animator instance, abort the + following steps. + 2. Let |workletGlobalScope| be the {{AnimationWorkletGlobalScope}} associated with + |workletAnimation|. + 3. Let |animatorInstance| be |workletAnimation|'s corresponding animator instance. + 4. Queue a task on the |workletGlobalScope| to run the procedure to remove an animator + instance, passing |animatorInstance| as instance and |workletGlobalScope| as + workletGlobalScope. + 5. Set |workletAnimation| as having no corresponding animator instance. + +
+ + +
+ +To set animator instance of worklet animation given +|workletAnimation|, the user agent must run the following steps: + + 1. disassociate animator instance of worklet animation given |workletAnimation|. + 2. associate animator instance of worklet animation given |workletAnimation|. + +
+ +When a given |workletAnimation|'s play state changes to pending, running, or +paused, run the procedure to +associate animator instance of worklet animation given |workletAnimation|. + + +When a given |workletAnimation|'s play state changes to idle or finished, +run the procedure to +disassociate animator instance of worklet animation given |workletAnimation|. + +When the procedure to set the target effect of an animation for a given |workletAnimation| +is called, then set animator instance of worklet animation given |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 |workletAnimation|. + + +Timeline Attachment {#timeline-attachment} +------------------- + +Issue(61): Define semantics of attachment and detachment. + +ScrollTimeline {#scroll-timeline} +--------------------------------- +{{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. + +WorkletGroupEffect {#worklet-group-effect} +------------------ + +{{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 {{WorkletAnimation}}, the +corresponding animator instance can directly control the child effects' local +times. This allows a single worklet animation to coordinate multiple effects - see +[[#example-2]] for an example of such a use-case. + + + +interface WorkletGroupEffect { + sequence<AnimationEffect> getChildren(); +}; + +[Exposed=AnimationWorklet] +partial interface AnimationEffect { + // Intended for use inside Animation Worklet scope to drive the effect. + attribute double localTime; +}; + + +
+ +To set the {{localTime}} property on a |effect| to value |t|, the user agent should perform the +action that corresponds to the first matching condition from the following: + + : If the |effect| does not have a parent group, + :: Set the |effect| local time to |t|. + : If the |effect| has a parent group and it is of {{WorkletGroupEffect}} type, + :: Set the effect start time to (parent's transformed time - t). Note this effectively set's the + |effect|'s local time to t. + : Otherwise + :: Throw an exception indicating that the child effect time can only be controlled by + its parent group. + +
+ +Issue(w3c/csswg-drafts#2071): The above interface exposes a conservative subset +of GroupEffect proposed as part of web-animation-2. Once that is available we +should switch to it. + + +Effect Stack and Composite Order {#effect-stack-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. + + +Examples {#examples} +==================== + +Example 1: Hidey Bar. {#example-1} +----------------------------------------- +An example of header effect where a header is moved with scroll and as soon as finger is lifted it +animates fully to close or open position depending on its current position. + + + +<div id='scrollingContainer'> + <div id='header'>Some header</div> + <div>content</div> +</div> + +<script> +await CSS.animationWorklet.addModule('hidey-bar-animator.js'); +const scrollTimeline = new ScrollTimeline($scrollingContainer, {timeRange: 1000}); +const documentTimeline = document.timeline; + +// Note we pass in two timelines in the options bag which allows the animation to read their +// currenTime values directly. +const animation = new WorkletAnimation( + 'hidey-bar', + new KeyFrameEffect($header, + [{transform: 'translateX(100px)'}, {transform: 'translateX(0px)'}], + {duration: 1000, iterations: 1, fill: 'both' }]), + scrollTimeline, + {scrollTimeline, documentTimeline}); + +animation.play(); +</script> + + + + +// Inside AnimationWorkletGlobalScope + +registerAnimator('hidey-bar', class { + constructor(options) { + this.scrollTimeline_ = options.scrollTimeline; + this.documentTimeline_ = options.documentTimeline; + } + + animate(currentTime, effect) { + const scroll = this.scrollTimeline_.currentTime; // [0, 100] + const time = this.documentTimeline_.currentTime; + + const activelyScrolling = this.scrollTimeline_.phase == 'active'; + + let localTime; + if (activelyScrolling) { + this.startTime_ = undefined; + localTime = scroll; + } else { + this.startTime_ = this.startTime_ || time; + // Decide on close/open direction depending on how far we have scrolled the header + // This can even do more sophisticated animation curve by computing the scroll velocity and + // using it. + this.direction_ = scroll >= 50 ? +1 : -1; + localTime = this.direction_ * (time - this.startTime_); + } + + // Drive the output effect by setting its local time. + effect.localTime = localTime; + } +}); + + + +Issue: This example uses a hypothetical "phase" property on timeline as a way to detect when user +is no longer actively scrolling. This is a reasonable thing to have on scroll timeline. A simple +fallback can emulate this by detecting when timeline time (i.e. scroll offset) has not changed in +the last few frames. + + +Example 2: Twitter header. {#example-2} +-------------------------- +An example of twitter profile header effect where two elements (avatar, and header) are updated in +sync with scroll offset. + + + +// In document scope. +<div id='scrollingContainer'> + <div id='header' style='height: 150px'></div> + <div id='avatar'><img></div> +</div> + +<script> +await CSS.animationWorklet.addModule('twitter-header-animator.js'); +const animation = new WorkletAnimation( + 'twitter-header', + [new KeyFrameEffect($avatar, /* scales down as we scroll up */ + [{transform: 'scale(1)'}, {transform: 'scale(0.5)'}], + {duration: 1000, iterations: 1}), + new KeyFrameEffect($header, /* loses transparency as we scroll up */ + [{opacity: 0}, {opacity: 0.8}], + {duration: 1000, iterations: 1})], + new ScrollTimeline($scrollingContainer, {timeRange: 1000, startScrollOffset: 0, endScrollOffset: $header.clientHeight})); +animation.play(); + +// Since this animation is using a group effect, the same animation instance +// is accessible via different handles: $avatarEl.getAnimations()[0], $headerEl.getAnimations()[0] + +</script> + + + + +// Inside AnimationWorkletGlobalScope. +registerAnimator('twitter-header', class { + constructor(options) { + this.timing_ = new CubicBezier('ease-out'); + } + + clamp(value, min, max) { + return Math.min(Math.max(value, min), max); + } + + animate(currentTime, effect) { + const scroll = currentTime; // scroll is in [0, 1000] range + + // Drive the output group effect by setting its children local times individually. + effect.children[0].localTime = scroll; + effect.children[1].localTime = this.timing_(clamp(scroll, 0, 500)); + } +}); + + +Example 3: Parallax backgrounds. {#example-3} +----------------------------------------- +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 scrollTimeline = new ScrollTimeline($scrollingContainer, {timeRange: 1000}); +const scrollRange = $scrollingContainer.scrollHeight - $scrollingContainer.clientHeight; + +const slowParallax = new WorkletAnimation( + 'parallax', + new KeyframeEffect($parallax_slow, [{'transform': 'translateY(0)'}, {'transform': 'translateY(' + -scrollRange + 'px)'}], {duration: 1000}), + scrollTimeline, + {rate : 0.4} +); +slowParallax.play(); + +const fastParallax = new WorkletAnimation( + 'parallax', + new KeyframeEffect($parallax_fast, [{'transform': 'translateY(0)'}, {'transform': 'translateY(' + -scrollRange + 'px)'}], {duration: 1000}), + scrollTimeline, + {rate : 0.8} +); +fastParallax.play(); +</script> + + + + +// Inside AnimationWorkletGlobalScope. +registerAnimator('parallax', class { + constructor(options) { + this.rate_ = options.rate; + } + + animate(currentTime, effect) { + effect.localTime = currentTime * this.rate_; + } +}); + diff --git a/css-animationworklet/index.html b/css-animationworklet/index.html new file mode 100644 index 00000000..2ae61256 --- /dev/null +++ b/css-animationworklet/index.html @@ -0,0 +1,3115 @@ + + + + CSS Animation Worklet API + + + + + + + + + + + + + +
+

+

CSS Animation Worklet API

+

Draft Community Group Report,

+
+
+
This version: +
https://wicg.github.io/animation-worklet/ +
Issue Tracking: +
GitHub +
Inline In Spec +
Editors: +
+
+
+
+
+
+ +
+
+
+

Abstract

+
+
+

Status of this document

+
+

This specification was published by the Web Platform Incubator Community Group. + It is not a W3C Standard nor is it on the W3C Standards Track. + + Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. + + Learn more about W3C Community and Business Groups.

+

+
+
+ +
+

1. Introduction

+ This section is not normative. +

This document introduces a new primitive for creating scroll-linked and other high performance +procedural animations on the web. For details on the rationale and motivation see [explainer].

+

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

+

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 many of the Web Animation APIs. However +Animation Worklet animations follow a different timing model that enables them to be script-driven, +stateful, and runnable in a parallel worklet execution context. As such Web Animation APIs that seek +or alter the input time (reverse, finish, etc.) have different semantics for Animation Worklet +animations.

+

2. Threading Model

+ This section is not normative. +

Animation Worklet is designed to be thread-agnostic. Rendering engines may create one or more +parallel worklet execution contexts separate from the main javascript execution context, e.g., on +their own dedicated threads. Rendering engines may then choose to assign Animation Worklet +animations to run in such contexts. Doing so allows Animation Worklet animations to avoid being +impacted by main thread jank.

+

Rendering engines may wish to make a best-effort attempt to execute animate callbacks synchronously +with visual frame production to ensure smooth animation. However it is legal for rendering engines +to produce visual frames without blocking to receive animation updates from a worklet (i.e., letting +the effects slip behind). For example, this could occur when the animate function callback is +unable to complete before the frame deadline.

+

We believe that scripted animations which are run in a parallel execution environment and which +limit themselves to animating properties which do not require the user agent to consult main thread +will have a much better chance of meeting the strict frame budgets required for smooth playback.

+

If a Worklet Animation animation is executing in a parallel worklet execution context, the last +known state of its animation 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 in 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.

+
+ Overview of the animation worklet threading model. +
+ Overview of the animation worklet threading model.
+

A simplified visualization of how animators running in a parallel execution environment can sync + their update to main thread while remaining in sync with visual frame production.

+
+
+

3. Animation Worklet

+ Animation Worklet is a Worklet responsible for all classes related to custom +animations. The worklet can be accessed via animationWorklet attribute. +

The animationWorklet's worklet global scope type is AnimationWorkletGlobalScope.

+

AnimationWorkletGlobalScope represents the global execution context of animationWorklet.

+
partial namespace CSS {
+    [SameObject] readonly attribute Worklet animationWorklet;
+};
+
+
[ Exposed=AnimationWorklet, Global=AnimationWorklet ]
+interface AnimationWorkletGlobalScope : WorkletGlobalScope {
+    void registerAnimator(DOMString name, VoidFunction animatorCtor);
+};
+
+
+ Note: This is how the class should look. +
class FooAnimator {
+    constructor(options) {
+        // Called when a new animator is instantiated.
+    }
+    animate(currentTime, effect) {
+        // Animation frame logic goes here.
+    }
+}
+
+
+

4. Animator Definition

+ An animator definition is a struct which describes the author defined custom +animation as needed by AnimationWorkletGlobalScope. It consists of: + +

4.1. Registering an Animator Definition

+ An AnimationWorkletGlobalScope has a animator name to animator definition map. +The map gets populated when registerAnimator(name, animatorCtorValue) is called. +
+

When the registerAnimator(name, animatorCtorValue) method is called in a AnimationWorkletGlobalScope, the user agent must run the +following steps:

+
    +
  1. +

    If name is not a valid <ident>, throw a TypeError and abort all these +steps.

    +
  2. +

    If name exists as a key in the animator name to animator definition map, throw a NotSupportedError and abort all these steps.

    +
  3. +

    If the result of IsConstructor(animatorCtorValue) is false, throw a TypeError and abort all these steps.

    +
  4. +

    Let animatorCtor be the result of converting animatorCtorValue to the VoidFunction callback function type. If an exception is thrown, rethrow the +exception and abort all these steps.

    +
  5. +

    Let prototype be the result of Get(animatorCtorValue, "prototype").

    +
  6. +

    If the result of Type(prototype) is not Object, throw a TypeError and abort all these steps.

    +
  7. +

    Let animateValue be the result of Get(prototype, "animate").

    +
  8. +

    Let animate be the result of converting animateValue to the Function callback function type. If an exception is thrown, rethrow the exception and abort +all these steps.

    +
  9. +

    Let destroyValue be the result of Get(prototype, "onDestroy").

    +
  10. +

    Let destroy be the result of converting destroyValue to the Function callback function type. If an exception is thrown, rethrow the exception and abort +all these steps.

    +
  11. +

    Let definition be a new animator definition with:

    + +
  12. +

    Add the key-value pair (name - definition) to the animator name to animator +definition map.

    +
+
+

5. Animator Instance

+

An animator instance is a struct which describes a fully realized custom animation +instance in an AnimationWorkletGlobalScope. It has a reference to an animator definition and owns the instance specific state such as animation effect and timelines. It consists of:

+ +

5.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:

+
    +
  1. +

    Let the definition be the result of looking up name on the workletGlobalScope’s animator name to animator definition map.

    +

    If definition does not exist abort the following steps.

    +
  2. +

    Let animatorCtor be the class constructor of definition.

    +
  3. +

    Let timelineList be a new list with timeline added to it.

    +
  4. +

    Let options be StructuredDeserialize(serializedOptions).

    +
  5. +

    Let state be StructuredDeserialize(serializedState).

    +
  6. +

    Let animatorInstance be the result of constructing animatorCtor with +[options, state as args. If an exception is thrown, rethrow the exception and abort all +these steps.

    +
  7. +

    Set the following on animatorInstance with:

    + +
  8. +

    Add animatorInstance to workletGlobalScope’s animator instance set.

    +
+
+

5.2. Running Animators

+

When a user agent wants to produce a new animation frame, if for any animator instance the +associated animation requested flag is frame-requested then the the user agent must run animators for the current frame.

+

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 iterate over all animator instances in the workletGlobalScope’s animator +instance set. For each such instance the user agent must perform the following steps:

+
    +
  1. +

    Let animatorName be instance’s animator name

    +
  2. +

    Let the definition be the result of looking up animatorName on the workletGlobalScope’s animator name to animator definition map.

    +

    If definition does not exist then abort the following steps.

    +
  3. +

    If the animation requested flag for instance is frame-current or the effect + belonging to the instance 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 animator instances to + throttle slow animators.

    +
  4. +

    Let animateFunction be definition’s animate function.

    +
  5. +

    Let currentTime be animator current time of instance.

    +
  6. +

    Let effect be animator effect of instance.

    +
  7. +

    Invoke animateFunction with arguments «currentTime, effect», + and with instance as the callback this value.

    +
+
+ Note: Although inefficient, it is legal for the user agent to run animators multiple times +in the same frame. +

5.3. Removing an Animator Instance

+
+

To remove an animator instance given instance and workletGlobalScope the user agent must run the following steps:

+
    +
  1. +

    Remove instance from workletGlobalScope’s animator instance set.

    +
+
+

5.4. Migrating an Animator Instance

+

User agents are responsible for assigning an animator instance to a WorkletGlobalScope. +There can be many such WorkletGlobalScopes, which may exist across different threads or +processes. To give the most flexibility to user agents in this respect, we allow migration of an animator instance while it is running. The basic mechanism is to serialize the internal state +of any author-defined effect, and restore it after migration.

+
+

To migrate an animator instance from one WorkletGlobalScope to another, given instance, sourceWorkletGlobalScope, destinationWorkletGlobalScope, the user agent must run the following steps :

+
    +
  1. +

    Let serializedState be undefined.

    +
  2. +

    Queue a task on sourceWorkletGlobalScope to run the following steps:

    +
      +
    1. +

      Let animatorName be instance’s animator name

      +
    2. +

      Let definition be the result of looking up animatorName on sourceWorkletGlobalScope’s animator name to animator definition map.

      +

      If definition does not exist then abort the following steps.

      +
    3. +

      Let destroyFunction be the destroy function of definition.

      +
    4. +

      Invoke destroyFunction with instance as the callback this value and +let state be the result of the invocation. If any exception is thrown, rethrow the +exception and abort the following steps.

      +
    5. +

      Set serializedState to be the result of StructuredSerialize(state). +If any exception is thrown, then abort the following steps.

      +
    6. +

      Run the procedure to remove an animator instance given instance, and sourceWorkletGlobalScope.

      +
    +
  3. +

    Wait for the above task to complete. If the task is aborted, abort the following steps.

    +
  4. +

    Queue a task on destinationWorkletGlobalScope to run the following steps:

    +
      +
    1. +

      Run the procedure to create a new animator instance given:

      + +
    +
+
+

5.5. Requesting Animation Frames

+

Each animator instance has an associated animation requested flag. It must be +either frame-requested or frame-current. It is initially set to frame-current. Different circumstances can cause the animation requested flag to be +set to frame-requested. These include the following:

+ +

§5.2 Running Animators resets the animation requested flag on animators to frame-current.

+

6. Web Animations Integration

+

6.1. Worklet Animation

+ Worklet animation is a kind of animation that delegates the animation playback 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:

+
    +
  • +

    an animation animator name which identifies its animator definition.

    +
  • +

    a serialized options which is serializable object that is used when +constructing a new animator instance.

    +
+
+ Overview of the WorkletAnimation timing model. +
+ Overview of the WorkletAnimation timing model.
+

The animation current time is input to the animator instance, which produces a local time value + for the animation effect. If the animator instance is running in a parallel global scope the + implementation may also choose to use the local time value to produce the final effect value and + update the visuals in parallel.

+
+
+

6.2. Creating a Worklet Animation

+
[Constructor (DOMString animatorName,
+              optional (AnimationEffect or sequence<AnimationEffect>)? effects = null,
+              optional AnimationTimeline? timeline,
+              optional any options)]
+interface WorkletAnimation : Animation {
+        readonly attribute DOMString animatorName;
+};
+
+
+
+ WorkletAnimation(animatorName, effects, timeline, options) +

Creates a new WorkletAnimation object using the following procedure.

+
    +
  1. +

    Let workletAnimation be a new WorkletAnimation object.

    +
  2. +

    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 the Window that is the current global object.

    +
  3. +

    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.

    +
    +
  4. +

    Run the procedure to set the target effect of an animation on workletAnimation passing effect as the new effect.

    +
  5. +

    Let serializedOptions be the result of StructuredSerialize(options). +Rethrow any exceptions.

    +
  6. +

    Set the serialized options of workletAnimation to serializedOptions.

    +
  7. +

    Set the animation animator name of workletAnimation to animatorName.

    +
+
+

6.3. Worklet Animation timing model

+

This section describes how worklet animation’s timing model differs from other animations.

+

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:

+ +

As described in §6.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:

+ +

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. <https://github.com/wicg/animation-worklet/issues/63>

+

6.4. Interaction with Animator Instances

+

A worklet animation corresponds to at most one animator instance at any time, and may +have no current corresponding animator instance. The correspondance of an animator +instance for a worklet animation depends on the animation play state.

+
+

To associate animator instance of worklet animation given workletAnimation, +the user agent must run the following steps:

+
    +
  1. +

    If workletAnimation has a corresponding animator instance, abort the following steps.

    +
  2. +

    Let workletGlobalScope be the AnimationWorkletGlobalScope associated with workletAnimation.

    +
  3. +

    Queue a task on workletGlobalScope to run the procedure to create a new animator + instance, passing:

    + +
  4. +

    If the procedure was successful, set the resulting animator instance as corresponding to workletAnimation.

    +
+
+
+

To disassociate animator instance of worklet animation given workletAnimation, the user age must run the following steps:

+
    +
  1. +

    If workletAnimation does not have a corresponding animator instance, abort the +following steps.

    +
  2. +

    Let workletGlobalScope be the AnimationWorkletGlobalScope associated with workletAnimation.

    +
  3. +

    Let animatorInstance be workletAnimation’s corresponding animator instance.

    +
  4. +

    Queue a task on the workletGlobalScope to run the procedure to remove an animator + instance, passing animatorInstance as instance and workletGlobalScope as + workletGlobalScope.

    +
  5. +

    Set workletAnimation as having no corresponding animator instance.

    +
+
+
+

To set animator instance of worklet animation given workletAnimation, the user agent must run the following steps:

+
    +
  1. +

    disassociate animator instance of worklet animation given workletAnimation.

    +
  2. +

    associate animator instance of worklet animation given workletAnimation.

    +
+
+

When a given workletAnimation’s play state changes to pending, running, or paused, run the procedure to associate animator instance of worklet animation given workletAnimation.

+

When a given workletAnimation’s play state changes to idle or finished, +run the procedure to disassociate animator instance of worklet animation given workletAnimation.

+

When the procedure to set the target effect of an animation for a given workletAnimation is called, then set animator instance of worklet animation given 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 workletAnimation.

+

6.5. Timeline Attachment

+

Define semantics of attachment and detachment. <https://github.com/wicg/animation-worklet/issues/61>

+

6.6. ScrollTimeline

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

+

6.7. WorkletGroupEffect

+

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 WorkletAnimation, the +corresponding animator instance can directly control the child effects' local +times. This allows a single worklet animation to coordinate multiple effects - see §7.2 Example 2: Twitter header. for an example of such a use-case.

+
interface WorkletGroupEffect {
+  sequence<AnimationEffect> getChildren();
+};
+
+[Exposed=AnimationWorklet]
+partial interface AnimationEffect {
+    // Intended for use inside Animation Worklet scope to drive the effect.
+    attribute double localTime;
+};
+
+
+

To set the localTime property on a effect to value t, the user agent should perform the +action that corresponds to the first matching condition from the following:

+
+
If the effect does not have a parent group, +
+

Set the effect local time to t.

+
If the effect has a parent group and it is of WorkletGroupEffect type, +
+

Set the effect start time to (parent’s transformed time - t). Note this effectively set’s the effect’s local time to t.

+
Otherwise +
+

Throw an exception indicating that the child effect time can only be controlled by +its parent group.

+
+
+

The above interface exposes a conservative subset +of GroupEffect proposed as part of web-animation-2. Once that is available we +should switch to it. <https://github.com/w3c/csswg-drafts/issues/2071>

+

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

+

7. Examples

+

7.1. Example 1: Hidey Bar.

+ An example of header effect where a header is moved with scroll and as soon as finger is lifted it +animates fully to close or open position depending on its current position. +
<div id='scrollingContainer'>
+  <div id='header'>Some header</div>
+  <div>content</div>
+</div>
+
+<script>
+await CSS.animationWorklet.addModule('hidey-bar-animator.js');
+const scrollTimeline = new ScrollTimeline($scrollingContainer, {timeRange: 1000});
+const documentTimeline = document.timeline;
+
+// Note we pass in two timelines in the options bag which allows the animation to read their
+// currenTime values directly.
+const animation = new WorkletAnimation(
+    'hidey-bar',
+    new KeyFrameEffect($header,
+                        [{transform: 'translateX(100px)'}, {transform: 'translateX(0px)'}],
+                        {duration: 1000, iterations: 1, fill: 'both' }]),
+    scrollTimeline,
+    {scrollTimeline, documentTimeline});
+
+animation.play();
+</script>
+
+
// Inside AnimationWorkletGlobalScope
+
+registerAnimator('hidey-bar', class {
+  constructor(options) {
+    this.scrollTimeline_ = options.scrollTimeline;
+    this.documentTimeline_ = options.documentTimeline;
+  }
+
+  animate(currentTime, effect) {
+    const scroll = this.scrollTimeline_.currentTime;  // [0, 100]
+    const time = this.documentTimeline_.currentTime;
+
+    const activelyScrolling = this.scrollTimeline_.phase == 'active';
+
+    let localTime;
+    if (activelyScrolling) {
+      this.startTime_ = undefined;
+      localTime = scroll;
+    } else {
+      this.startTime_ = this.startTime_ || time;
+      // Decide on close/open direction depending on how far we have scrolled the header
+      // This can even do more sophisticated animation curve by computing the scroll velocity and
+      // using it.
+      this.direction_ = scroll >= 50 ? +1 : -1;
+      localTime = this.direction_ * (time - this.startTime_);
+    }
+
+    // Drive the output effect by setting its local time.
+    effect.localTime = localTime;
+  }
+});
+
+

This example uses a hypothetical "phase" property on timeline as a way to detect when user +is no longer actively scrolling. This is a reasonable thing to have on scroll timeline. A simple +fallback can emulate this by detecting when timeline time (i.e. scroll offset) has not changed in +the last few frames.

+

7.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. +
// In document scope.
+<div id='scrollingContainer'>
+  <div id='header' style='height: 150px'></div>
+  <div id='avatar'><img></div>
+</div>
+
+<script>
+await CSS.animationWorklet.addModule('twitter-header-animator.js');
+const animation = new WorkletAnimation(
+    'twitter-header',
+    [new KeyFrameEffect($avatar,  /* scales down as we scroll up */
+                    [{transform: 'scale(1)'}, {transform: 'scale(0.5)'}],
+                    {duration: 1000, iterations: 1}),
+    new KeyFrameEffect($header, /* loses transparency as we scroll up */
+                    [{opacity: 0}, {opacity: 0.8}],
+                    {duration: 1000, iterations: 1})],
+    new ScrollTimeline($scrollingContainer, {timeRange: 1000, startScrollOffset: 0, endScrollOffset: $header.clientHeight}));
+animation.play();
+
+// Since this animation is using a group effect, the same animation instance
+// is accessible via different handles: $avatarEl.getAnimations()[0], $headerEl.getAnimations()[0]
+
+</script>
+
+
// Inside AnimationWorkletGlobalScope.
+registerAnimator('twitter-header', class {
+  constructor(options) {
+    this.timing_ = new CubicBezier('ease-out');
+  }
+
+  clamp(value, min, max) {
+    return Math.min(Math.max(value, min), max);
+  }
+
+  animate(currentTime, effect) {
+    const scroll = currentTime;  // scroll is in [0, 1000] range
+
+    // Drive the output group effect by setting its children local times individually.
+    effect.children[0].localTime = scroll;
+    effect.children[1].localTime = this.timing_(clamp(scroll, 0, 500));
+  }
+});
+
+

7.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 scrollTimeline = new ScrollTimeline($scrollingContainer, {timeRange: 1000});
+const scrollRange = $scrollingContainer.scrollHeight - $scrollingContainer.clientHeight;
+
+const slowParallax = new WorkletAnimation(
+    'parallax',
+    new KeyframeEffect($parallax_slow, [{'transform': 'translateY(0)'}, {'transform': 'translateY(' + -scrollRange + 'px)'}], {duration: 1000}),
+    scrollTimeline,
+    {rate : 0.4}
+);
+slowParallax.play();
+
+const fastParallax = new WorkletAnimation(
+    'parallax',
+    new KeyframeEffect($parallax_fast, [{'transform': 'translateY(0)'}, {'transform': 'translateY(' + -scrollRange + 'px)'}], {duration: 1000}),
+    scrollTimeline,
+    {rate : 0.8}
+);
+fastParallax.play();
+</script>
+
+
// Inside AnimationWorkletGlobalScope.
+registerAnimator('parallax', class {
+  constructor(options) {
+    this.rate_ = options.rate;
+  }
+
+  animate(currentTime, effect) {
+    effect.localTime = currentTime * this.rate_;
+  }
+});
+
+
+
+

Conformance

+

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. + The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” + in the normative parts of this document + are to be interpreted as described in RFC 2119. + However, for readability, + these words do not appear in all uppercase letters in this specification.

+

All of the text of this specification is normative + except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

+

Examples in this specification are introduced with the words “for example” + or are set apart from the normative text with class="example", like this:

+
This is an example of an informative example.
+

Informative notes begin with the word “Note” + and are set apart from the normative text with class="note", like this:

+

Note, this is an informative note.

+
+ +

Index

+

Terms defined by this specification

+ + + + + + + + + + + + + + + + + + + + + + + + + +

Terms defined by reference

+
    +
  • + [css-transitions-1] defines the following terms: +
      +
    • start time +
    +
  • + [css-values-4] defines the following terms: +
      +
    • <ident> +
    +
  • + [cssom-1] defines the following terms: +
      +
    • CSS +
    +
  • + [DOM] defines the following terms: +
      +
    • Document +
    +
  • + [HTML] defines the following terms: +
      +
    • Window +
    • current global object +
    • queue a task +
    +
  • + [INFRA] defines the following terms: +
      +
    • list +
    • struct +
    +
  • + [web-animations-1] defines the following terms: +
      +
    • Animation +
    • AnimationEffect +
    • AnimationTimeline +
    • finish() +
    • getComputedTiming() +
    +
  • + [WebIDL] defines the following terms: +
      +
    • DOMString +
    • Exposed +
    • Global +
    • SameObject +
    • VoidFunction +
    • callback function +
    • double +
    +
  • + [worklets-1] defines the following terms: +
      +
    • Worklet +
    • WorkletGlobalScope +
    • worklet global scope type +
    +
+

References

+

Normative References

+
+
[CSS-TRANSITIONS-1] +
David Baron; Dean Jackson; Brian Birtles. CSS Transitions. 30 November 2017. WD. URL: https://www.w3.org/TR/css-transitions-1/ +
[CSS-VALUES-4] +
CSS Values and Units Module Level 4 URL: https://drafts.csswg.org/css-values-4/ +
[CSSOM-1] +
Simon Pieters; Glenn Adams. CSS Object Model (CSSOM). 17 March 2016. WD. URL: https://www.w3.org/TR/cssom-1/ +
[DOM] +
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/ +
[HTML] +
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/ +
[INFRA] +
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/ +
[RFC2119] +
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119 +
[WEB-ANIMATIONS-1] +
Brian Birtles; et al. Web Animations. 13 September 2016. WD. URL: https://www.w3.org/TR/web-animations-1/ +
[WebIDL] +
Cameron McCormack; Boris Zbarsky; Tobie Langel. Web IDL. 15 December 2016. ED. URL: https://heycam.github.io/webidl/ +
[WORKLETS-1] +
Ian Kilpatrick. Worklets Level 1. 7 June 2016. WD. URL: https://www.w3.org/TR/worklets-1/ +
+

Informative References

+
+
[EXPLAINER] +
Animation Worklet Explainer. CR. URL: https://github.com/WICG/animation-worklet/blob/gh-pages/README.md +
+

IDL Index

+
partial namespace CSS {
+    [SameObject] readonly attribute Worklet animationWorklet;
+};
+
+[ Exposed=AnimationWorklet, Global=AnimationWorklet ]
+interface AnimationWorkletGlobalScope : WorkletGlobalScope {
+    void registerAnimator(DOMString name, VoidFunction animatorCtor);
+};
+
+[Constructor (DOMString animatorName,
+              optional (AnimationEffect or sequence<AnimationEffect>)? effects = null,
+              optional AnimationTimeline? timeline,
+              optional any options)]
+interface WorkletAnimation : Animation {
+        readonly attribute DOMString animatorName;
+};
+
+
+interface WorkletGroupEffect {
+  sequence<AnimationEffect> getChildren();
+};
+
+[Exposed=AnimationWorklet]
+partial interface AnimationEffect {
+    // Intended for use inside Animation Worklet scope to drive the effect.
+    attribute double localTime;
+};
+
+
+

Issues Index

+
+
Consider giving user agents permission to skip running animator instances to + throttle slow animators.
+
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. <https://github.com/wicg/animation-worklet/issues/63>
+
Define semantics of attachment and detachment. <https://github.com/wicg/animation-worklet/issues/61>
+
The above interface exposes a conservative subset +of GroupEffect proposed as part of web-animation-2. Once that is available we +should switch to it. <https://github.com/w3c/csswg-drafts/issues/2071>
+
This example uses a hypothetical "phase" property on timeline as a way to detect when user +is no longer actively scrolling. This is a reasonable thing to have on scroll timeline. A simple +fallback can emulate this by detecting when timeline time (i.e. scroll offset) has not changed in +the last few frames.
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/css-animationworklet/w3c.json b/css-animationworklet/w3c.json new file mode 100644 index 00000000..8f70b426 --- /dev/null +++ b/css-animationworklet/w3c.json @@ -0,0 +1,5 @@ + { + "group": ["80485"] +, "contacts": ["yoavweiss"] +, "shortName": "animation-worklet" +} \ No newline at end of file