├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── WIP.md
├── img
├── AnimationWorklet-threading-model.svg
└── WorkletAnimation-timing-model.svg
├── index.bs
├── index.html
└── w3c.json
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Web Platform Incubator Community Group
2 |
3 | This repository is being used for work in the Web Platform Incubator Community Group, governed by the [W3C Community License
4 | Agreement (CLA)](http://www.w3.org/community/about/agreements/cla/). To contribute, you must join
5 | the CG.
6 |
7 | If you are not the sole contributor to a contribution (pull request), please identify all
8 | contributors in the pull request's body or in subsequent comments.
9 |
10 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows:
11 |
12 | ```
13 | +@github_username
14 | ```
15 |
16 | If you added a contributor by mistake, you can remove them in a comment with:
17 |
18 | ```
19 | -@github_username
20 | ```
21 |
22 | If you are making a pull request on behalf of someone else but you had no part in designing the
23 | feature, you can remove yourself with the above syntax.
24 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | All Reports in this Repository are licensed by Contributors under the
2 | [W3C Software and Document
3 | License](http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). Contributions to
4 | Specifications are made under the [W3C CLA](https://www.w3.org/community/about/agreements/cla/).
5 |
6 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # $Id: Makefile,v 1.5 2008/02/06 14:05:15 mike Exp $
2 | #
3 | # FIXME: New documentation needed.
4 | #
5 | # Use "make REMOTE=1" to use remote bikeshed
6 |
7 | SOURCEFILE=index.bs
8 | OUTPUTFILE=index.html
9 | PREPROCESSOR=bikeshed.py
10 | REMOTE_PREPROCESSOR_URL=https://api.csswg.org/bikeshed/
11 |
12 | all: $(OUTPUTFILE)
13 |
14 | $(OUTPUTFILE): $(SOURCEFILE)
15 | ifneq (,$(REMOTE))
16 | curl $(REMOTE_PREPROCESSOR_URL) -F file=@$(SOURCEFILE) > "$@"
17 | else
18 | $(PREPROCESSOR) -f spec "$<" "$@"
19 | endif
20 |
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # New Home!
2 |
3 | This repository has been merged into [css-houdini-drafts](https://github.com/w3c/css-houdini-drafts) repository of [CSS-TAG Houdini Task Force](https://github.com/w3c/css-houdini-drafts/wiki).
4 |
5 | Please file issues and send PRs there.
6 |
7 | * [Draft Specifications](https://drafts.css-houdini.org/css-animationworklet/)
8 | * [Issues](https://github.com/w3c/css-houdini-drafts/labels/css-workletanimation-1)
--------------------------------------------------------------------------------
/WIP.md:
--------------------------------------------------------------------------------
1 | # Open API Questions
2 | ---
3 |
4 |
5 | ## Creation/Registration timing
6 |
7 | What should happen if an animation is created before the animator is registered?
8 |
9 | ## Timelines
10 |
11 | * observe-only timelines? i.e., have access to timeline but the animate is not triggered when its
12 | value changes.
13 |
14 | * Should we have a Timeline.currentTime and Timeline.localTime, where the latter is
15 | the former but offset by startTime & scaled by playbackRate?
16 |
17 | * Access to the actual scroll position in the ScrollTimeline
18 |
19 | * Access to scroll phase (inactive, active, inertial etc.)
20 |
21 |
22 | ## Updating Elements
23 |
24 | For some effects we need to be able to add new participating elements without
25 | restarting the effect. Here is an initial idea on how this can work.
26 |
27 | ```js
28 | // Effects and data can change after some time.
29 | // We might want to break this out into separate functions or optional
30 | // updates so you can just update options or just effects and options
31 | // without having to pass other parameters again.
32 | anim.update({
33 | [ /* new list of effects? */],
34 | [ /* new list of timelines */],
35 | {/* options */}
36 | });
37 | ```
38 |
39 | ```js
40 | // In worklet scope
41 | class MyAnimator{
42 | update(options) {
43 | // this is a V2 concept,
44 | }
45 | }
46 | ```
47 |
48 | ## CSS Notation
49 |
50 | We are not proposing including this in the initial spec, but including some
51 | preliminary thoughts here so that we can keep the eventual declarative CSS
52 | specification in mind.
53 |
54 | index.html:
55 | ```html
56 | <-- animator instance is declared here, with its timelines -->
57 |
58 | <-- effect timing is declared here and assigned to above animator -->
59 |
60 |
61 |
62 |
63 | ```
64 |
65 | style.css:
66 | ```css
67 | #main {
68 | animation: worklet('twitter-header')
69 | animation-timeline: scroll(#scroller_element.....) /* https://wicg.github.io/scroll-animations/#animation-timeline */
70 | }
71 |
72 | /* These are descendants of the animation */
73 | #main .header {
74 | animation-group: 'twitter-header' 'header' #...
75 | /* This syntax should be similar to what the plan is for Web Animation Group Effects */
76 | }
77 |
78 | #main .avatar {
79 | animation-group: 'twitter-header' 'avatar' #...
80 | }
81 | ```
82 |
83 | This is equivalent to calling:
84 |
85 | ```js
86 | new WorkletAnimation('twitter-header',
87 | [
88 | new KeyFrameEffect(.header[0], [], {}),
89 | new KeyFrameEffect(.avatar, [], {}),
90 | new KeyFrameEffect(.header[1], [], {}),
91 | ],
92 | [new ScrollingTimeline(#selector, {...})],
93 | { elements: [
94 | /* This is admittedly a bit magical. */
95 | {'name': 'header'},
96 | {'name': 'avatar'},
97 | {'name': 'header'},
98 | ]}
99 | ).play();
100 |
101 | ```
102 |
103 | Adding new elements that match the selector will be equivalent to invoking `update`.
104 |
--------------------------------------------------------------------------------
/img/AnimationWorklet-threading-model.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/img/WorkletAnimation-timing-model.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/index.bs:
--------------------------------------------------------------------------------
1 |
102 |
103 | Introduction {#intro}
104 | =====================
105 | This section is not normative.
106 |
107 | This document introduces a new primitive for creating scroll-linked and other high performance
108 | procedural animations on the web. For details on the rationale and motivation see [[explainer]].
109 |
110 | The Animation Worklet API provides a method to create scripted animations that control a set
111 | of animation effects. The API is designed to make it possible for user agents to run such
112 | animations in their own dedicated thread to provide a degree of performance isolation from main
113 | thread.
114 |
115 | Relationship to the Web Animations API {#relationship-to-web-animations}
116 | ------------------------------------------------------------------------
117 |
118 | Animations running inside an Animation Worklet execution context expose the {{Animation}}
119 | interface from the Web Animations specification on the main javascript execution context. This means
120 | they can be controlled and inspected from main thread using many of the Web Animation APIs. However
121 | Animation Worklet animations follow a different timing model that enables them to be script-driven,
122 | stateful, and runnable in a parallel worklet execution context. As such Web Animation APIs that seek
123 | or alter the input time (reverse, finish, etc.) have different semantics for Animation Worklet
124 | animations.
125 |
126 |
127 | Threading Model {#threading-model}
128 | ==================================
129 | This section is not normative.
130 |
131 | Animation Worklet is designed to be thread-agnostic. Rendering engines may create one or more
132 | parallel worklet execution contexts separate from the main javascript execution context, e.g., on
133 | their own dedicated threads. Rendering engines may then choose to assign Animation Worklet
134 | animations to run in such contexts. Doing so allows Animation Worklet animations to avoid being
135 | impacted by main thread jank.
136 |
137 | Rendering engines may wish to make a best-effort attempt to execute animate callbacks synchronously
138 | with visual frame production to ensure smooth animation. However it is legal for rendering engines
139 | to produce visual frames without blocking to receive animation updates from a worklet (i.e., letting
140 | the effects slip behind). For example, this could occur when the animate function callback is
141 | unable to complete before the frame deadline.
142 |
143 | We believe that scripted animations which are run in a parallel execution environment and which
144 | limit themselves to animating properties which do not require the user agent to consult main thread
145 | will have a much better chance of meeting the strict frame budgets required for smooth playback.
146 |
147 | If a Worklet Animation animation is executing in a parallel worklet execution context, the last
148 | known state of its animation effects should be periodically synced back to the main javascript
149 | execution context. The synchronization of effect values from the parallel worklet execution
150 | context to the main javascript execution context must occur before running the animation
151 | frame callbacks as part of the document lifecycle. Note that due to the asynchronous nature of
152 | this animation model a script running in the main javascript execution context may see a stale value
153 | when reading a target property that is being animated in a Worklet Animation, compared to the
154 | value currently being used to produce the visual frame that is visible to the user. This is similar
155 | to the effect of asynchronous scrolling when reading scroll offsets in the main javascript execution
156 | context.
157 |
158 |
159 |
160 |
162 |
163 | Overview of the animation worklet threading model.
164 |
165 | A simplified visualization of how animators running in a parallel execution environment can sync
166 | their update to main thread while remaining in sync with visual frame production.
167 |
168 |
169 |
170 | Animation Worklet {#animation-worklet-desc}
171 | ==============================
172 | Animation Worklet is a {{Worklet}} responsible for all classes related to custom
173 | animations. The worklet can be accessed via {{animationWorklet}} attribute.
174 |
175 | The {{animationWorklet}}'s worklet global scope type is {{AnimationWorkletGlobalScope}}.
176 |
177 | {{AnimationWorkletGlobalScope}} represents the global execution context of {{animationWorklet}}.
178 |
179 |
180 | [Exposed=Window]
181 | partial namespace CSS {
182 | [SameObject] readonly attribute Worklet animationWorklet;
183 | };
184 |
185 |
186 |
187 | [Exposed=AnimationWorklet, Global=AnimationWorklet]
188 | interface AnimationWorkletGlobalScope : WorkletGlobalScope {
189 | void registerAnimator(DOMString name, VoidFunction animatorCtor);
190 | };
191 |
192 |
193 |
194 |
195 | Note: This is how the class should look.
196 |
197 | class FooAnimator {
198 | constructor(options) {
199 | // Called when a new animator is instantiated.
200 | }
201 | animate(currentTime, effect) {
202 | // Animation frame logic goes here.
203 | }
204 | }
205 |
206 |
207 |
208 |
209 |
210 | Animator Definition {#animator-definition-desc}
211 | ====================
212 | An animator definition is a struct which describes the author defined custom
213 | animation as needed by {{AnimationWorkletGlobalScope}}. It consists of:
214 |
215 | - An animator name <>#.
216 |
217 | - A class constructor which is a VoidFunctioncallback function type.
218 |
219 | - An animate function which is a Functioncallback function type.
220 |
221 | - A destroy function which is a Functioncallback function type.
222 |
223 |
224 | Registering an Animator Definition {#registering-animator-definition}
225 | -------------------------------------
226 | An {{AnimationWorkletGlobalScope}} has a animator name to animator definition map.
227 | The map gets populated when {{registerAnimator(name, animatorCtorValue)}} is called.
228 |
229 |
230 |
231 | When the registerAnimator(|name|, |animatorCtorValue|)
232 | method is called in a {{AnimationWorkletGlobalScope}}, the user agent must run the
233 | following steps:
234 |
235 | 1. If |name| is not a valid <>, throw a TypeError and abort all these
236 | steps.
237 |
238 | 2. If |name| exists as a key in the animator name to animator definition map,
239 | throw a NotSupportedError and abort all these steps.
240 |
241 | 3. If the result of IsConstructor(|animatorCtorValue|) is false, throw a
242 | TypeError and abort all these steps.
243 |
244 | 4. Let |animatorCtor| be the result of converting animatorCtorValue to the
245 | VoidFunctioncallback function type. If an exception is thrown, rethrow the
246 | exception and abort all these steps.
247 |
248 | 4. Let |prototype| be the result of Get(|animatorCtorValue|, "prototype").
249 |
250 | 5. If the result of Type(|prototype|) is not Object, throw a TypeError
251 | and abort all these steps.
252 |
253 | 6. Let |animateValue| be the result of Get(|prototype|, "animate").
254 |
255 | 7. Let |animate| be the result of converting |animateValue| to the Function
256 | callback function type. If an exception is thrown, rethrow the exception and abort
257 | all these steps.
258 |
259 | 8. Let |destroyValue| be the result of Get(|prototype|, "onDestroy").
260 |
261 | 9. Let |destroy| be the result of converting |destroyValue| to the Function
262 | callback function type. If an exception is thrown, rethrow the exception and abort
263 | all these steps.
264 |
265 |
266 | 8. Let |definition| be a new animator definition with:
267 |
268 | - animator name being |name|
269 |
270 | - class constructor being |animatorCtor|
271 |
272 | - animate function being |animate|
273 |
274 | - destroy function being |destroy|
275 |
276 |
277 | 9. Add the key-value pair (|name| - |definition|) to the animator name to animator
278 | definition map.
279 |
280 |
281 |
282 | Animator Instance {#animator-instance-section}
283 | ======================================
284 |
285 | An animator instance is a struct which describes a fully realized custom animation
286 | instance in an {{AnimationWorkletGlobalScope}}. It has a reference to an animator definition
287 | and owns the instance specific state such as animation effect and timelines. It consists of:
288 |
289 | - An animator name.
290 |
291 | - An animation requested flag.
292 |
293 | - An animator effect which is an animation effect.
294 |
295 | - An animator current time which is the corresponding worklet animation's current
296 | time.
297 |
298 | - An animator timeline which is a timeline.
299 |
300 | - An animator attached timelines which is list of attached timelines
301 |
302 | - An animator serialized options which is a serializable object.
303 |
304 |
305 | Creating an Animator Instance {#creating-animator-instance}
306 | -----------------------------------------------------------
307 |
308 | Each animator instance lives in an {{AnimationWorkletGlobalScope}}.
309 |
310 | Each {{AnimationWorkletGlobalScope}} has an animator instance set. The set is populated
311 | when the user agent constructs a new animator instance in the {{AnimationWorkletGlobalScope}}
312 | scope. Each animator instance corresponds to a worklet animation in the document scope.
313 |
314 |
315 |
316 | To create a new animator instance given a |name|, |timeline|, |effect|, |serializedOptions|,
317 | |serializedState|, and |workletGlobalScope|, the user agent must run the following steps:
318 |
319 | 1. Let the |definition| be the result of looking up |name| on the |workletGlobalScope|'s
320 | animator name to animator definition map.
321 |
322 | If |definition| does not exist abort the following steps.
323 |
324 | 2. Let |animatorCtor| be the class constructor of |definition|.
325 |
326 | 3. Let |timelineList| be a new list with |timeline| added to it.
327 |
328 | 4. Let |options| be StructuredDeserialize(|serializedOptions|).
329 |
330 | 5. Let |state| be StructuredDeserialize(|serializedState|).
331 |
332 | 6. Let |animatorInstance| be the result of constructing |animatorCtor| with
333 | [|options|, |state| as args. If an exception is thrown, rethrow the exception and abort all
334 | these steps.
335 |
336 | 7. Set the following on |animatorInstance| with:
337 | - animator name being |name|
338 | - animation requested flag being frame-current
339 | - animator current time being unresolved
340 | - animator effect being |effect|
341 | - animator timeline being |timeline|
342 | - animator attached timelines being |timelineList|
343 | - animator serialized options being |options|
344 |
345 | 8. Add |animatorInstance| to |workletGlobalScope|'s animator instance set.
346 |
347 |
348 |
349 |
350 | Running Animators {#running-animators}
351 | --------------------------------------
352 |
353 | When a user agent wants to produce a new animation frame, if for any animator instance the
354 | associated animation requested flag is frame-requested then the the user agent
355 | mustrun animators for the current frame.
356 |
357 | Note: The user agent is not required to run animations on every visual frame. It is legal to defer
358 | generating an animation frame until a later frame. This allow the user agent to
359 | provide a different service level according to their policy.
360 |
361 |
362 |
363 | When the user agent wants to run animators in a given |workletGlobalScope|, it
364 | must iterate over all animator instances in the |workletGlobalScope|'s animator
365 | instance set. For each such |instance| the user agent must perform the following steps:
366 |
367 | 1. Let |animatorName| be |instance|'s animator name
368 |
369 | 2. Let the |definition| be the result of looking up |animatorName| on the |workletGlobalScope|'s
370 | animator name to animator definition map.
371 |
372 | If |definition| does not exist then abort the following steps.
373 |
374 | 3. If the animation requested flag for |instance| is frame-current or the effect
375 | belonging to the |instance| will not be visible within the visual viewport of the current
376 | frame the user agent may abort all the following steps.
377 |
378 | Issue: Consider giving user agents permission to skip running animator instances to
379 | throttle slow animators.
380 |
381 | 4. Let |animateFunction| be |definition|'s animate function.
382 |
383 | 5. Let |currentTime| be animator current time of |instance|.
384 |
385 | 6. Let |effect| be animator effect of |instance|.
386 |
387 | 7. Invoke |animateFunction| with arguments «|currentTime|, |effect|»,
388 | and with |instance| as the callback this value.
389 |
390 |
391 | Note: Although inefficient, it is legal for the user agent to run animators multiple times
392 | in the same frame.
393 |
394 | Removing an Animator Instance {#removing-animator}
395 | -----------------------------------------
396 |
397 |
398 |
399 | To remove an animator instance given |instance| and |workletGlobalScope| the user agent
400 | must run the following steps:
401 |
402 | 1. Remove |instance| from |workletGlobalScope|'s animator instance set.
403 |
404 |
405 |
406 |
407 | Migrating an Animator Instance {#migrating-animator}
408 | -----------------------------------------
409 |
410 | User agents are responsible for assigning an animator instance to a {{WorkletGlobalScope}}.
411 | There can be many such {{WorkletGlobalScope}}s, which may exist across different threads or
412 | processes. To give the most flexibility to user agents in this respect, we allow migration of an
413 | animator instance while it is running. The basic mechanism is to serialize the internal state
414 | of any author-defined effect, and restore it after migration.
415 |
416 |
417 |
418 | To migrate an animator instance from one {{WorkletGlobalScope}} to another, given
419 | |instance|, |sourceWorkletGlobalScope|, |destinationWorkletGlobalScope|, the user agent
420 | must run the following steps :
421 |
422 | 1. Let |serializedState| be undefined.
423 |
424 | 2. Queue a task on |sourceWorkletGlobalScope| to run the following steps:
425 |
426 | 1. Let |animatorName| be |instance|'s animator name
427 |
428 | 2. Let |definition| be the result of looking up |animatorName| on |sourceWorkletGlobalScope|'s
429 | animator name to animator definition map.
430 |
431 | If |definition| does not exist then abort the following steps.
432 |
433 | 3. Let |destroyFunction| be the destroy function of |definition|.
434 |
435 |
436 | 4. Invoke |destroyFunction| with |instance| as the callback this value and
437 | let |state| be the result of the invocation. If any exception is thrown, rethrow the
438 | exception and abort the following steps.
439 |
440 | 5. Set |serializedState| to be the result of StructuredSerialize(|state|).
441 | If any exception is thrown, then abort the following steps.
442 |
443 | 6. Run the procedure to remove an animator instance given |instance|, and
444 | |sourceWorkletGlobalScope|.
445 |
446 | 2. Wait for the above task to complete. If the task is aborted, abort the following steps.
447 |
448 | 3. Queue a task on |destinationWorkletGlobalScope| to run the following steps:
449 |
450 | 1. Run the procedure to create a new animator instance given:
451 | - The |instance|'s animator name as name.
452 | - The |instance|'s animator timeline as timeline.
453 | - The |instance|'s animator effect as effect.
454 | - The |instance|'s animator serialized options as options.
455 | - The |serializedState| as state.
456 | - The |destinationWorkletGlobalScope| as workletGlobalScope.
457 |
458 |
459 |
460 |
461 | Requesting Animation Frames {#requesting-animation-frames}
462 | ----------------------------------------------------------
463 |
464 | Each animator instance has an associated animation requested flag. It must be
465 | either frame-requested or frame-current. It is initially set to
466 | frame-current. Different circumstances can cause the animation requested flag to be
467 | set to frame-requested. These include the following:
468 | - Changes in the current time of any timeline in the animator's animator attached timelines
469 | - Changes in the current time of the animator's corresponding Worklet Animation
470 |
471 | [[#running-animators]] resets the animation requested flag on animators to
472 | frame-current.
473 |
474 |
475 | Web Animations Integration {#web-animation-integration}
476 | ===============================
477 |
478 |
479 | Worklet Animation {#worklet-animation-desc}
480 | -------------------------------------------
481 | Worklet animation is a kind of animation that delegates the animation playback to
482 | an animator instance. It controls the lifetime and playback state of its corresponding
483 | animator instance.
484 |
485 | Being an animation, worklet animation has an animation effect and a
486 | timeline. However unlike other animations the worklet animation's current time does
487 | not directly determine the animation effect's local time (via its inherited time).
488 | Instead the associated animator instance controls the animation effect's local time
489 | directly. Note that this means that the timeline's current time does not fully determine the
490 | animation's output.
491 |
492 | Worklet animation has the following properties in addition to the {{Animation}} interface:
493 | - an animation animator name which identifies its animator definition.
494 | - a serialized options which is serializable object that is used when
495 | constructing a new animator instance.
496 |
497 |
498 |
500 |
501 | Overview of the WorkletAnimation timing model.
502 |
503 | The animation current time is input to the animator instance, which produces a local time value
504 | for the animation effect. If the animator instance is running in a parallel global scope the
505 | implementation may also choose to use the local time value to produce the final effect value and
506 | update the visuals in parallel.
507 |
508 |
509 |
510 |
511 |
512 | Creating a Worklet Animation {#creating-worklet-animation}
513 | -----------------------------------------------------------
514 |
515 |
516 | [Exposed=Window,
517 | Constructor (DOMString animatorName,
518 | optional (AnimationEffect or sequence)? effects = null,
519 | optional AnimationTimeline? timeline,
520 | optional any options)]
521 | interface WorkletAnimation : Animation {
522 | readonly attribute DOMString animatorName;
523 | };
524 |
525 |
526 |
527 |
528 |
529 | WorkletAnimation(|animatorName|, |effects|, |timeline|, |options|)
530 |
531 | Creates a new {{WorkletAnimation}} object using the following procedure.
532 |
533 | 1. Let |workletAnimation| be a new {{WorkletAnimation}} object.
534 |
535 | 2. Run the procedure to set the timeline of an animation on |workletAnimation| passing
536 | |timeline| as the new timeline or, if a |timeline| argument is not provided,
537 | passing the default document timeline of the {{Document}} associated with the
538 | {{Window}} that is the current global object.
539 |
540 | 3. Let |effect| be the result corresponding to the first matching condition from below.
541 | : If |effects| is a {{AnimationEffect}} object,
542 | :: Let effect be |effects|.
543 | : If |effects| is a list of {{AnimationEffect}} objects,
544 | :: Let |effect| be a new {{WorkletGroupEffect}} with its children set to |effects|.
545 | : Otherwise,
546 | :: Let |effect| be undefined.
547 |
548 | 4. Run the procedure to set the target effect of an animation on |workletAnimation|
549 | passing |effect| as the new effect.
550 |
551 | 5. Let |serializedOptions| be the result of StructuredSerialize(|options|).
552 | Rethrow any exceptions.
553 |
554 | 6. Set the serialized options of |workletAnimation| to |serializedOptions|.
555 |
556 | 7. Set the animation animator name of |workletAnimation| to |animatorName|.
557 |
558 |
559 |
560 | Worklet Animation timing model {#timing-model}
561 | ------------------------------------
562 |
563 | This section describes how worklet animation's timing model differs from other
564 | animations.
565 |
566 | In addition to the existing conditions on when the animation is considered ready, a
567 | worklet animation is only considered ready when the following condition is also true:
568 |
569 | - the user agent has completed any setup required to create the worklet animation's
570 | corresponding animator instance.
571 |
572 | As described in [[#worklet-animation-desc]], the worklet animation'scurrent time does
573 | not determine its animation effect'slocal time. Instead the associated animator
574 | instance controls the animation effect's local time directly. This means that the
575 | animation effect's local time is controlled from a {{WorkletGlobalScope}} which may be in a parallel
576 | execution context.
577 |
578 | Here are a few implications of the above semantics:
579 |
580 | - Setting the current time or start time of a worklet animation does not
581 | necessarily change its output, but may change the animation play state.
582 | - Similarly, invoking {{Animation/finish()}} or updating a worklet animation'splayback
583 | rate will only change the animation play state and may not change the output.
584 | - Querying the animation effect's local time using {{AnimationEffect/getComputedTiming()}}
585 | may return stale information, in the case where the animator instance is running in a
586 | parallel execution context.
587 |
588 | Issue(63): Come with appropriate mechanism's for animator instance to get notified when its
589 | animation currentTime is changing e.g., via reverse(), finish() or playbackRate change. So that
590 | it can react appropriately.
591 |
592 |
593 | Interaction with Animator Instances {#worklet-animation-animator-instances}
594 | -----------------------------------
595 |
596 | A worklet animation corresponds to at most one animator instance at any time, and may
597 | have no current corresponding animator instance. The correspondance of an animator
598 | instance for a worklet animation depends on the animation play state.
599 |
600 |
601 |
602 | To associate animator instance of worklet animation given |workletAnimation|,
603 | the user agent must run the following steps:
604 |
605 | 1. If |workletAnimation| has a corresponding animator instance, abort the following steps.
606 | 2. Let |workletGlobalScope| be the {{AnimationWorkletGlobalScope}} associated with
607 | |workletAnimation|.
608 | 3. Queue a task on |workletGlobalScope| to run the procedure to create a new animator
609 | instance, passing:
610 | * The |workletAnimation|'s animation animator name as name.
611 | * The |workletAnimation|'s timeline as timeline.
612 | * The |workletAnimation|'s animation effect as effect.
613 | * The |workletAnimation|'s serialized options as options.
614 | * The |workletGlobalScope| as workletGlobalScope.
615 | 4. If the procedure was successful, set the resulting animator instance as corresponding to
616 | |workletAnimation|.
617 |
618 |
619 |
620 |
621 |
622 | To disassociate animator instance of worklet animation given
623 | |workletAnimation|, the user age must run the following steps:
624 |
625 | 1. If |workletAnimation| does not have a corresponding animator instance, abort the
626 | following steps.
627 | 2. Let |workletGlobalScope| be the {{AnimationWorkletGlobalScope}} associated with
628 | |workletAnimation|.
629 | 3. Let |animatorInstance| be |workletAnimation|'s corresponding animator instance.
630 | 4. Queue a task on the |workletGlobalScope| to run the procedure to remove an animator
631 | instance, passing |animatorInstance| as instance and |workletGlobalScope| as
632 | workletGlobalScope.
633 | 5. Set |workletAnimation| as having no corresponding animator instance.
634 |
635 |
647 |
648 | When a given |workletAnimation|'s play state changes to pending, running, or
649 | paused, run the procedure to
650 | associate animator instance of worklet animation given |workletAnimation|.
651 |
652 |
653 | When a given |workletAnimation|'s play state changes to idle or finished,
654 | run the procedure to
655 | disassociate animator instance of worklet animation given |workletAnimation|.
656 |
657 | When the procedure to set the target effect of an animation for a given |workletAnimation|
658 | is called, then set animator instance of worklet animation given |workletAnimation|.
659 |
660 | When the procedure to set the timeline of an animation for a given |workletAnimation|
661 | is called, then set animator instance of worklet animation given |workletAnimation|.
662 |
663 |
664 | Timeline Attachment {#timeline-attachment}
665 | -------------------
666 |
667 | Issue(61): Define semantics of attachment and detachment.
668 |
669 | ScrollTimeline {#scroll-timeline}
670 | ---------------------------------
671 | {{ScrollTimeline}} is a new concept being proposed for addition to web animation API. It defines
672 | an animation timeline whose time value depends on the scroll position of a scroll container.
673 | Worklet animations can have a scroll timeline and thus drive their scripted effects based
674 | on a scroll offset.
675 |
676 | Note: Access to input: We are interested on exposing additional user input beside
677 | scrolling (e.g., touch/pointer input) to these animations so that authors can create jank-free
678 | input driven animations which are not really possible today. We are still trying to figure out the
679 | right abstractions and mechanisms to do this.
680 |
681 | WorkletGroupEffect {#worklet-group-effect}
682 | ------------------
683 |
684 | {{WorkletGroupEffect}} is a type of group effect that allows its child effect's
685 | local times to be mutated individually.
686 |
687 | When a {{WorkletGroupEffect}} is set as the animation effect of a {{WorkletAnimation}}, the
688 | corresponding animator instance can directly control the child effects' local
689 | times. This allows a single worklet animation to coordinate multiple effects - see
690 | [[#example-2]] for an example of such a use-case.
691 |
692 |
693 | [Exposed=AnimationWorklet]
694 | interface WorkletGroupEffect {
695 | sequence getChildren();
696 | };
697 |
698 | [Exposed=AnimationWorklet]
699 | partial interface AnimationEffect {
700 | // Intended for use inside Animation Worklet scope to drive the effect.
701 | attribute double localTime;
702 | };
703 |
704 |
705 |
706 |
707 | To set the {{localTime}} property on a |effect| to value |t|, the user agent should perform the
708 | action that corresponds to the first matching condition from the following:
709 |
710 | : If the |effect| does not have a parent group,
711 | :: Set the |effect| local time to |t|.
712 | : If the |effect| has a parent group and it is of {{WorkletGroupEffect}} type,
713 | :: Set the effect start time to (parent's transformed time - t). Note this effectively set's the
714 | |effect|'s local time to t.
715 | : Otherwise
716 | :: Throw an exception indicating that the child effect time can only be controlled by
717 | its parent group.
718 |
719 |
720 |
721 | Issue(w3c/csswg-drafts#2071): The above interface exposes a conservative subset
722 | of GroupEffect proposed as part of web-animation-2. Once that is available we
723 | should switch to it.
724 |
725 |
726 | Effect Stack and Composite Order {#effect-stack-composite-order}
727 | --------------------------------
728 |
729 | As with other animations, worklet animations participate in the effect stack. A
730 | worklet animation does not have a specific animation class which means it has the same
731 | composite order as other Javascript created web animations.
732 |
733 |
734 | Examples {#examples}
735 | ====================
736 |
737 | Example 1: Hidey Bar. {#example-1}
738 | -----------------------------------------
739 | An example of header effect where a header is moved with scroll and as soon as finger is lifted it
740 | animates fully to close or open position depending on its current position.
741 |
742 |
743 |
744 |
745 |
Some header
746 |
content
747 |
748 |
749 |
766 |
767 |
768 |
769 |
770 | // Inside AnimationWorkletGlobalScope
771 |
772 | registerAnimator('hidey-bar', class {
773 | constructor(options) {
774 | this.scrollTimeline_ = options.scrollTimeline;
775 | this.documentTimeline_ = options.documentTimeline;
776 | }
777 |
778 | animate(currentTime, effect) {
779 | const scroll = this.scrollTimeline_.currentTime; // [0, 100]
780 | const time = this.documentTimeline_.currentTime;
781 |
782 | const activelyScrolling = this.scrollTimeline_.phase == 'active';
783 |
784 | let localTime;
785 | if (activelyScrolling) {
786 | this.startTime_ = undefined;
787 | localTime = scroll;
788 | } else {
789 | this.startTime_ = this.startTime_ || time;
790 | // Decide on close/open direction depending on how far we have scrolled the header
791 | // This can even do more sophisticated animation curve by computing the scroll velocity and
792 | // using it.
793 | this.direction_ = scroll >= 50 ? +1 : -1;
794 | localTime = this.direction_ * (time - this.startTime_);
795 | }
796 |
797 | // Drive the output effect by setting its local time.
798 | effect.localTime = localTime;
799 | }
800 | });
801 |
802 |
803 |
804 | Issue: This example uses a hypothetical "phase" property on timeline as a way to detect when user
805 | is no longer actively scrolling. This is a reasonable thing to have on scroll timeline. A simple
806 | fallback can emulate this by detecting when timeline time (i.e. scroll offset) has not changed in
807 | the last few frames.
808 |
809 |
810 | Example 2: Twitter header. {#example-2}
811 | --------------------------
812 | An example of twitter profile header effect where two elements (avatar, and header) are updated in
813 | sync with scroll offset.
814 |
815 |
816 |
817 | // In document scope.
818 |
819 |
820 |
821 |
822 |
823 |
840 |
841 |
842 |
843 |
844 | // Inside AnimationWorkletGlobalScope.
845 | registerAnimator('twitter-header', class {
846 | constructor(options) {
847 | this.timing_ = new CubicBezier('ease-out');
848 | }
849 |
850 | clamp(value, min, max) {
851 | return Math.min(Math.max(value, min), max);
852 | }
853 |
854 | animate(currentTime, effect) {
855 | const scroll = currentTime; // scroll is in [0, 1000] range
856 |
857 | // Drive the output group effect by setting its children local times individually.
858 | effect.children[0].localTime = scroll;
859 | effect.children[1].localTime = this.timing_(clamp(scroll, 0, 500));
860 | }
861 | });
862 |
863 |
864 | Example 3: Parallax backgrounds. {#example-3}
865 | -----------------------------------------
866 | A simple parallax background example.
867 |
868 |
869 |
877 |