4
4
# Overview
5
5
6
6
Animation Worklet is a new primitive that provides extensibility in web animations and enables high
7
- performance procedural animations on the web. The feature is developed as part of the
7
+ performance procedural animations on the web. The feature is developed as part of the
8
8
[ CSS Houdini task force] ( https://github.com/w3c/css-houdini-drafts/wiki ) .
9
9
10
10
The Animation Worklet API provides a method to create scripted animations that control a set of
@@ -43,7 +43,6 @@ address this problem.
43
43
* Custom scrollbars.
44
44
* High-fidelity location tracking and positioning
45
45
* [ More examples] ( https://github.com/w3c/css-houdini-drafts/blob/master/scroll-customization-api/UseCases.md ) of scroll-driven effects.
46
-
47
46
* Gesture driven effects:
48
47
* [ Image manipulator] ( https://github.com/w3c/csswg-drafts/issues/2493#issuecomment-422153926 ) that scales, rotates etc.
49
48
* Swipe to dismiss.
@@ -65,11 +64,11 @@ address this problem.
65
64
66
65
Not all of these usecases are immediately enabled by the current proposed API. However Animation
67
66
Worklet provides a powerfull primitive (off main-thread scriped animation) which when combined with
68
- other upcoming features (e.g.,
67
+ other upcoming features (e.g.,
69
68
[ Event in Worklets] ( https://github.com/w3c/css-houdini-drafts/issues/834 ) ,
70
69
[ ScrollTimeline] ( https://wicg.github.io/scroll-animations/ ) ,
71
- [ GroupEffect] ( https://github.com/w3c/csswg-drafts/issues/2071 ) ) can address all these usecases and
72
- allows many of currently main-thread rAF-based animations to move off thread with significant
70
+ [ GroupEffect] ( https://github.com/w3c/csswg-drafts/issues/2071 ) ) can address all these usecases and
71
+ allows many of currently main-thread rAF-based animations to move off thread with significant
73
72
improvement to their smoothness.
74
73
See [ Animation Worklet design principles and goals] ( principles.md ) for a more extended discussion
75
74
of this.
@@ -86,21 +85,29 @@ flag) otherwise they fallback to using main thread rAF to emulate the behaviour.
86
85
## Animation Worklet Global Scope
87
86
A [ worklet global scope] ( https://drafts.css-houdini.org/worklets/#the-global-scope ) that is created
88
87
by Animation Worklet. Note that Animation Worklet creates multiple such scopes and uses them to
89
- execute user defined effects.
88
+ execute user defined effects. In particular global scopes are regularly switched to enforce
89
+ stateless and stateful animator contracts.
90
+
91
+
92
+ ## Animator
93
+
94
+ Animator is a Javascript class that encapsulates the custom animation logic. Similar to other
95
+ Houdinig worklets, animators are registered inside the worklet global scope with a unique name which
96
+ can be used to uniquely identify them.
97
+
90
98
91
99
## WorkletAnimation
92
- ` WorkletAnimation ` is a subclass of Animation that can be used to create an custom animation effect
93
- that runs inside a standalone animation worklet scope. A worklet animation has a corresponding
94
- animator instance in a animation worklet scope which is responsible to drive its keyframe effects.
95
- Here are the key differences compared to a regular web animation:
96
- - AnimationId should match a specific animator class registered in the animation worklet scope.
97
- - ` WorkletAnimation ` may have multiple timelines (including ` ScrollTimeline ` s).
98
- - ` WorkletAnimation ` may have a custom properties bag that can be cloned and provided to animator
99
- constructor when it is being instantiated.
100
+ ` WorkletAnimation ` is a subclass of Animation that can be used to create an custom animation that
101
+ runs inside a standalone animation worklet scope. A worklet animation has a corresponding animator
102
+ instance in a animation worklet scope which is responsible to drive its keyframe effects. Here are
103
+ the key differences compared to a regular web animation:
104
+ - Name: The name identifies the custom animator class registered in the animation worklet scope.
105
+ - Options: ` WorkletAnimation ` may have a custom properties bag that is cloned and provided to the
106
+ corresponding animator constructor when it is being instantiated.
100
107
101
108
Note that worklet animations expose same API surface as other web animations and thus they may be
102
- created, played, paused, inspected, and generally controlled from main document scope. Here is how
103
- various methods roughly translate:
109
+ created, played, paused, inspected, and generally controlled from the main document scope. Here is
110
+ how various methods roughly translate:
104
111
105
112
- ` cancel() ` : cancels the animation and the corresponding animator instance is removed.
106
113
- ` play() ` : starts the animation and the corresponding animator instance gets constructed and
@@ -111,39 +118,8 @@ various methods roughly translate:
111
118
the animator instance. (We are considering possiblity of having a ` onPlaybackRateChanged `
112
119
callback)
113
120
114
- ## ScrollTimeline
115
- [ ScrollTimeline] ( https://wicg.github.io/scroll-animations/#scrolltimeline ) is a concept introduced in
116
- scroll-linked animation proposal. It defines an animation timeline whose time value depends on
117
- scroll position of a scroll container. ` ScrollTimeline ` can be used an an input timeline for
118
- worklet animations and it is the intended mechanisms to give read access to scroll position.
119
-
120
- ## GroupEffect
121
- [ GroupEffect] ( https://w3c.github.io/web-animations/level-2/#the-animationgroup-interfaces ) is a
122
- concept introduced in Web Animation Level 2 specification. It provides a way to group multiple
123
- effects in a tree structure. ` GroupEffect ` can be used as the output for worklet animations. It
124
- makes it possible for worklet animation to drive effects spanning multiple elements.
125
-
126
- ** TODO** : At the moment, ` GroupEffect ` only supports just two different scheduling models (i.e.,
127
- parallel, sequence). These models governs how the group effect time is translated to its children
128
- effect times by modifying the child effect start time. Animation Worklet allows a much more
129
- flexible scheduling model by making it possible to to set children effect's local time directly. In
130
- other words we allow arbitrary start time for child effects. This is something that needs to be
131
- added to level 2 spec.
132
-
133
- ## ~~ Multiple Timelines~~
134
- Unlike typical animations, worklet animations can be attached to multiple timelines. This is
135
- necessary to implement key usecases where the effect needs to smoothly animate across different
136
- timelines (e.g., scroll and wall clock).
137
-
138
- ** NOTE** : We have decided to drop this piece in favor of alternative ideas. Most recent
139
- [ promising idea] ( https://docs.google.com/document/d/1byDy6IZqvaci-FQoiRzkeAmTSVCyMF5UuaSeGJRHpJk/edit#heading=h.dc7o68szgx2r )
140
- revolves around allowing worklet and workers to receive input events directly. (here are some
141
- earlier alternative design: [ 1] ( https://docs.google.com/document/d/1-APjTs9fn4-E7pFeFSfiWV8tYitO84VpmKuNpE-25Qk/edit ) , [ 2] ( https://github.com/w3c/csswg-drafts/issues/2493 ) , [ 3] ( https://github.com/w3c/csswg-drafts/issues/2493#issuecomment-422109535 )
142
-
143
-
144
121
## Statefull and Statelss Animators
145
122
146
-
147
123
Sometimes animation effects require maintaining internal state (e.g., when animation needs to depend
148
124
on velocity). Such animators have to explicitly declare their statefulness but by inheritting from
149
125
` StatefulAnimator ` superclass.
@@ -153,7 +129,6 @@ lifetime duration. For example user agents are free to initially run the animato
153
129
but later decide to migrate it off main thread to get certain performance optimizations or to tear
154
130
down scopes to save resources.
155
131
156
-
157
132
Animation Worklet helps stateful animators to maintain their state across such migration events.
158
133
This is done through a state() function which is called and animator exposes its state. Here is
159
134
an example:
@@ -199,82 +174,98 @@ registerAnimator('animation-with-local-state', class FoorAnimator extends Statef
199
174
});
200
175
```
201
176
202
- # Examples
177
+ # Related Concepts
178
+
179
+ The following concepts are not part of Animation Worklet specification but animation worklet is
180
+ designed to take advantage of them to enable a richer set of usecases.
181
+
182
+ ## ScrollTimeline
183
+ [ ScrollTimeline] ( https://wicg.github.io/scroll-animations/#scrolltimeline ) is a concept introduced in
184
+ scroll-linked animation proposal. It defines an animation timeline whose time value depends on
185
+ scroll position of a scroll container. ` ScrollTimeline ` can be used an an input timeline for
186
+ worklet animations and it is the intended mechanisms to give read access to scroll position.
187
+
188
+ ## GroupEffect
189
+ [ GroupEffect] ( https://w3c.github.io/web-animations/level-2/#the-animationgroup-interfaces ) is a
190
+ concept introduced in Web Animation Level 2 specification. It provides a way to group multiple
191
+ effects in a tree structure. ` GroupEffect ` can be used as the output for worklet animations. It
192
+ makes it possible for worklet animation to drive effects spanning multiple elements.
203
193
204
- ** TODO** : Add gifs that visualize these effects
194
+ ** TODO** : At the moment, ` GroupEffect ` only supports just two different scheduling models (i.e.,
195
+ parallel, sequence). These models governs how the group effect time is translated to its children
196
+ effect times by modifying the child effect start time. Animation Worklet allows a much more
197
+ flexible scheduling model by making it possible to to set children effect's local time directly. In
198
+ other words we allow arbitrary start time for child effects. This is something that needs to be
199
+ added to level 2 spec.
205
200
206
- ## Hidey Bar
207
- An example of header effect where a header is moved with scroll and as soon as finger is lifted
208
- it animates fully to close or open position depending on its current position.
209
201
202
+ # Examples
210
203
211
- ``` html
212
- <div id =' scrollingContainer' >
213
- <div id =' header' >Some header</div >
214
- <div >content</div >
215
- </div >
204
+ ## Custom Spring Timing
216
205
217
- <script >
218
- await CSS .animationWorklet .addModule (' hidey-bar-animator.js' );
206
+ Use Animation Worklet to create animation with a custom spring timing.
219
207
220
- const scrollTimeline = new ScrollTimeline ({
221
- scrollSource: $scrollingContainer,
222
- orientation: ' block' ,
223
- timeRange: 100
224
- });
225
- const documentTimeline = document .timeline ;
226
208
209
+ ``` html
227
210
228
- const animation = new WorkletAnimation ( ' hidey-bar ' ,
229
- new KeyframeEffect ($header,
230
- [{transform : ' translateX(100px) ' }, {transform : ' translateX(0px) ' }],
231
- {duration : 100 , iterations : 1 , fill : ' both ' })
232
- scrollTimeline,
233
- {scrollTimeline, documentTimeline},
234
- );
211
+ < div id = ' target ' ></ div >
212
+
213
+ < script >
214
+ await CSS . animationWorklet . addModule ( ' spring-animator.js ' );
215
+
216
+ const effect = new KeyframeEffect ($target,{transform : [ ' translateX(0) ' , ' translateX(50vw) ' }], {duration : 1000 });
217
+ const animation = new WorkletAnimation ( ' spring ' , effect, document . timeline , {k : 2 , ratio : 0.7 );
235
218
animation .play ();
236
219
< / script>
237
220
` ` `
238
221
239
- hidey-bar-animator.js:
240
- ``` js
241
- registerAnimator (' hidey-bar' , class {
222
+ spring-animator.js:
242
223
243
- constructor (options ) {
244
- this .scrollTimeline_ = options .scrollTimeline ;
245
- this .documentTimeline_ = options .documentTimeline ;
224
+ ` ` ` js
225
+ registerAnimator (' spring' , class SpringAnimator extends StatelessAnimator {
226
+ constructor (options = {k: 1 , ratio: 0.5 }) {
227
+ this .timing = createSpring (options .k , options .ratio );
246
228
}
247
229
248
230
animate (currentTime , effect ) {
249
- const scroll = this .scrollTimeline_ .currentTime ; // [0, 100]
250
- const time = this .documentTimeline_ .currentTime ;
251
-
252
- // **TODO**: use a hypothetical 'phase' property on timeline as a way to detect when user is no
253
- // longer actively scrolling. This is a reasonable thing to have on scroll timeline but we can
254
- // fallback to using a timeout based approach as well.
255
- const activelyScrolling = this .scrollTimeline_ .phase == ' active' ;
256
-
257
- let localTime;
258
- if (activelyScrolling) {
259
- this .startTime_ = undefined ;
260
- localTime = scroll;
261
- } else {
262
- this .startTime_ = this .startTime_ || time;
263
- // Decide on close/open direction depending on how far we have scrolled the header
264
- // This can even do more sophisticated animation curve by computing the scroll velocity and
265
- // using it.
266
- this .direction_ = scroll >= 50 ? + 1 : - 1 ;
267
- localTime = this .direction_ * (time - this .startTime_ );
268
- }
269
-
270
- // Drive the output effects by setting its local time.
271
- effect .localTime = localTime;
231
+ let delta = this .timing (currentTime);
232
+ // scale this by target duration
233
+ delta = delta * (effect .getTimings ().duration / 2 );
234
+ effect .localTime = delta;
235
+ // TODO: Provide a method for animate to mark animation as finished once
236
+ // spring simulation is complete, e.g., this.finish()
237
+ // See issue https://github.com/w3c/css-houdini-drafts/issues/808
238
+ }
272
239
});
273
240
241
+ function createSpring (springConstant , ratio ) {
242
+ // Normalize mass and distance to 1 and assume a reasonable init velocit
243
+ // but these can also become options to this animator.
244
+ const velocity = 0.2 ;
245
+ const mass = 1 ;
246
+ const distance = 1 ;
247
+
248
+ // Keep ratio < 1 to ensure it is under-damped.
249
+ ratio = Math .min (ratio, 1 - 1e-5 );
250
+
251
+ const damping = ratio * 2.0 * Math .sqrt (springConstant);
252
+ const w = Math .sqrt (4.0 * springConstant - damping * damping) / (2.0 * mass);
253
+ const r = - (damping / 2.0 );
254
+ const c1 = distance;
255
+ const c2 = (velocity - r * distance) / w;
256
+
257
+ // return a value in [0..distance]
258
+ return function springTiming (timeMs ) {
259
+ const time = timeMs / 1000 ; // in seconds
260
+ const result = Math .pow (Math .E , r * time) *
261
+ (c1 * Math .cos (w * time) + c2 * Math .sin (w * time));
262
+ return distance - result;
263
+ }
264
+ }
274
265
` ` `
275
266
276
-
277
267
## Twitter Header
268
+
278
269
An example of twitter profile header effect where two elements (avatar, and header) are updated in
279
270
sync with scroll offset.
280
271
@@ -307,7 +298,7 @@ animation.play();
307
298
308
299
twitter-header-animator.js:
309
300
` ` ` js
310
- registerAnimator (' twitter-header' , class {
301
+ registerAnimator (' twitter-header' , class TwitterHeader extends StatelessAnimator {
311
302
constructor (options ) {
312
303
this .timing_ = new CubicBezier (' ease-out' );
313
304
}
@@ -329,6 +320,7 @@ registerAnimator('twitter-header', class {
329
320
` ` `
330
321
331
322
### Parallax
323
+
332
324
` ` ` html
333
325
< style>
334
326
.parallax {
@@ -375,7 +367,7 @@ parallax-animator.js:
375
367
376
368
` ` ` js
377
369
// Inside AnimationWorkletGlobalScope.
378
- registerAnimator (' parallax' , class {
370
+ registerAnimator (' parallax' , class Parallax extends StatelessAnimator {
379
371
constructor (options ) {
380
372
this .rate_ = options .rate ;
381
373
}
@@ -386,7 +378,6 @@ registerAnimator('parallax', class {
386
378
});
387
379
` ` `
388
380
389
-
390
381
# WEBIDL
391
382
392
383
` WorkletAnimation` extends ` Animation` and adds a getter for its timelines.
@@ -424,7 +415,6 @@ partial interface AnimationEffectReadOnly {
424
415
425
416
` ` `
426
417
427
-
428
418
# Specification
429
419
The [draft specification](https://drafts.css-houdini.org/css-animationworklet) is
430
420
the most recent version.
0 commit comments