@@ -73,6 +73,7 @@ urlPrefix: https://tc39.github.io/ecma262/#sec-; type: dfn;
7373 url: map-objects; text:map object
7474 url: get-o-p; text: Get
7575 url: set-o-p-v-throw; text: Set
76+ url: samevalue; text: SameValue
7677 urlPrefix: native-error-types-used-in-this-standard-
7778 text: TypeError
7879urlPrefix: https://www.w3.org/TR/hr-time-2/#dom-; type: dfn
@@ -182,18 +183,44 @@ partial namespace CSS {
182183};
183184</xmp>
184185
186+
187+
188+ Animator {#animator-desc}
189+ ========
190+
191+ An <dfn>Animator</dfn> represents an animation instance that is running on the animation thread.
192+ Animators are identified by a unique name and determine how the animation progresses its keyframe
193+ effects given current input time. <a>Animator</a> instances live in {{AnimationWorkletGlobalScope}}
194+ and each one is associated with a {{WorkletAnimation}} instance. An animator can only be
195+ instantiated by construction of a {{WorkletAnimation}} .
196+
197+
198+ Two animators types are supported: {{StatelessAnimator}} and {{StatefulAnimator}} each
199+ providing a different state management strategy.
200+
201+
202+ StatelessAnimator Interface {#stateless-animator-desc}
203+ ---------------------------
204+
205+ This interface represents a stateless animator. This type of animator does not depend on any local
206+ state either stored on the instance or global scope. Effectively, the animate function of an
207+ {{StatelessAnimator}} can be treated as a pure function with the expectation that given the same
208+ input, it produces the same output.
209+
210+
211+
185212<xmp class='idl'>
186- [Exposed=AnimationWorklet, Global=AnimationWorklet]
187- interface AnimationWorkletGlobalScope : WorkletGlobalScope {
188- void registerAnimator(DOMString name, VoidFunction animatorCtor);
213+ [Exposed=AnimationWorklet, Global=AnimationWorklet, Constructor (optional any options)]
214+ interface StatelessAnimator {
189215};
190216</xmp>
191217
192218
219+
193220<div class='note'>
194- Note: This is how the class should look.
221+ This is how the class should look.
195222 <pre class='lang-javascript'>
196- class FooAnimator {
223+ class FooAnimator extends StatelessAnimator {
197224 constructor(options) {
198225 // Called when a new animator is instantiated.
199226 }
@@ -204,10 +231,70 @@ interface AnimationWorkletGlobalScope : WorkletGlobalScope {
204231 </pre>
205232</div>
206233
234+ Note: The statelessness allows an animation worklet to perform optimization such as producing
235+ multiple animation frames in parallel and performing very cheap teardown and setup. Using
236+ StatelessAnimator is highly recommended to enable such optimizations.
237+
238+ StatefulAnimator Interface {#stateful-animator-desc}
239+ ---------------------------
240+
241+ This interface represents a stateful animator. This animator can have local state and animation
242+ worklet guarantees that it maintains this state as long as the stateful animator fulfills the
243+ contract required by this interface as described following.
244+
245+
246+ <a>Animation worklet</a> maintains a set of {{WorkletGlobalScope}} s which may exist across different
247+ threads or processes. Animation worklet may temporarily terminate a global scope (e.g., to preserve
248+ resources) or move a running <a>animator instance</a> across different global scopes (e.g., if its
249+ effect is mutable only in a certain thread). Animation worklet guarantees that a stateful animator
250+ instance's state is maintained even if the instance is respawned in a different global scope.
251+
252+ The basic mechanism for maintaining the state is that the animation worklet snapshots the local
253+ state that is exposed via {{StatefulAnimator/state}} attribute and then reifies it at a later time
254+ to be passed into the constructor when the animator instance is respawned in a potentially different
255+ global scope. This processes is specified is details in <a>migrate an animator instance</a>
256+ algorithm.
257+
258+ A user-defined stateful animator is expected to fulfill the required contract which is that its
259+ state attribute is an object representing its state that can be serialized using structured
260+ serialized algorithm and that it can also recreate its state given that same object passed to
261+ its constructor.
262+
263+ <xmp class='idl'>
264+ [Exposed=AnimationWorklet, Global=AnimationWorklet,
265+ Constructor (optional any options, optional any state)]
266+ interface StatefulAnimator {
267+ attribute any state;
268+ };
269+ </xmp>
270+
271+
272+ <div class='note'>
273+ This is how the class should look.
274+ <pre class='lang-javascript'>
275+ class BarAnimator extends StatefulAnimator {
276+ constructor(options, state) {
277+ // Called when a new animator is instantiated (either first time or after being respawned).
278+ this.currentVelocity = state ? state.velocity : 0;
279+ }
280+ animate(currentTime, effect) {
281+ // Animation frame logic goes here and can rely on this.currentVelocity.
282+ this.currentVelocity += 0.1;
283+ }
284+ get state {
285+ // The returned object should be serializable using structured clonable algorithm.
286+ return {
287+ velocity: this.currentVelocity;
288+ }
289+ }
290+ }
291+ </pre>
292+ </div>
207293
208294
209295Animator Definition {#animator-definition-desc}
210- ====================
296+ -------------------
297+
211298An <dfn>animator definition</dfn> is a <a>struct</a> which describes the author defined custom
212299animation as needed by {{AnimationWorkletGlobalScope}} . It consists of:
213300
@@ -217,14 +304,22 @@ animation as needed by {{AnimationWorkletGlobalScope}}. It consists of:
217304
218305 - An <dfn>animate function</dfn> which is a <a>Function</a> <a>callback function</a> type.
219306
220- - A <dfn>destroy function </dfn> which is a <a>Function</a> <a>callback function</a> type.
307+ - A <dfn>stateful flag </dfn>
221308
222309
223310Registering an Animator Definition {#registering-animator-definition}
224311-------------------------------------
225312An {{AnimationWorkletGlobalScope}} has a <dfn>animator name to animator definition map</dfn> .
226313The map gets populated when {{registerAnimator(name, animatorCtorValue)}} is called.
227314
315+
316+ <xmp class='idl'>
317+ [ Exposed=AnimationWorklet, Global=AnimationWorklet ]
318+ interface AnimationWorkletGlobalScope : WorkletGlobalScope {
319+ void registerAnimator(DOMString name, VoidFunction animatorCtor);
320+ };
321+ </xmp>
322+
228323<div algorithm="register-animator">
229324
230325When the <dfn method for=AnimationWorkletGlobalScope>registerAnimator(|name|, |animatorCtorValue|)</dfn>
@@ -246,22 +341,18 @@ following steps:
246341
247342 4. Let |prototype| be the result of <a>Get</a> (|animatorCtorValue|, "prototype").
248343
249- 5. If the result of <a>Type</a> (|prototype|) is not Object, <a>throw</a> a <a>TypeError</a>
250- and abort all these steps.
344+
345+ 5. If <a>SameValue</a> (|prototype|, {{StatelessAnimator}} ) is <b> true</b> set |stateful| to be
346+ <b> false</b> , otherwise if <a>SameValue</a> (|prototype|, {{StatefulAnimator}} ) is
347+ <b> true</b> set |stateful| to be <b> true</b> , otherwise <a>throw</a> a <a>TypeError</a> and
348+ abort all these steps.
251349
252350 6. Let |animateValue| be the result of <a>Get</a> (|prototype|, "animate").
253351
254352 7. Let |animate| be the result of <a>converting</a> |animateValue| to the <a>Function</a>
255353 <a>callback function</a> type. If an exception is thrown, rethrow the exception and abort
256354 all these steps.
257355
258- 8. Let |destroyValue| be the result of <a>Get</a> (|prototype|, "onDestroy").
259-
260- 9. Let |destroy| be the result of <a>converting</a> |destroyValue| to the <a>Function</a>
261- <a>callback function</a> type. If an exception is thrown, rethrow the exception and abort
262- all these steps.
263-
264-
265356 8. Let |definition| be a new <a>animator definition</a> with:
266357
267358 - <a>animator name</a> being |name|
@@ -270,7 +361,7 @@ following steps:
270361
271362 - <a>animate function</a> being |animate|
272363
273- - <a>destroy function </a> being |destroy |
364+ - <a>stateful flag </a> being |stateful |
274365
275366
276367 9. Add the key-value pair (|name| - |definition|) to the <a>animator name to animator
@@ -300,6 +391,9 @@ and owns the instance specific state such as animation effect and timelines. It
300391
301392 - An <dfn>animator serialized options</dfn> which is a serializable object.
302393
394+ A <dfn>stateful animator instance</dfn> is an <a>animator instance</a> whose corresponding
395+ <a>animator definition</a> 's <a>stateful flag</a> is <b> true</b> .
396+
303397
304398Creating an Animator Instance {#creating-animator-instance}
305399-----------------------------------------------------------
@@ -349,7 +443,7 @@ To <dfn>create a new animator instance</dfn> given a |name|, |timeline|, |effect
349443Running Animators {#running-animators}
350444--------------------------------------
351445
352- When a user agent wants to produce a new animation frame, if for any <a>animator instance</a> the
446+ When the user agent wants to produce a new animation frame, if for any <a>animator instance</a> the
353447associated <a>animation requested flag</a> is <a>frame-requested</a> then the the user agent
354448<em> must</em> <a>run animators</a> for the current frame.
355449
@@ -390,6 +484,11 @@ instance set</a>. For each such |instance| the user agent <em>must</em> perform
390484Note: Although inefficient, it is legal for the user agent to <a>run animators</a> multiple times
391485in the same frame.
392486
487+
488+ Issue: should be explicit as to what happens if the animateFunction throws an exception. At least
489+ we should have wording that the localTime values of the keyframes are ignored to avoid incorrect
490+ partial updates.
491+
393492Removing an Animator Instance {#removing-animator}
394493-----------------------------------------
395494
@@ -406,11 +505,8 @@ To <dfn>remove an animator instance</dfn> given |instance| and |workletGlobalSco
406505Migrating an Animator Instance {#migrating-animator}
407506-----------------------------------------
408507
409- User agents are responsible for assigning an <a>animator instance</a> to a {{WorkletGlobalScope}} .
410- There can be many such {{WorkletGlobalScope}} s, which may exist across different threads or
411- processes. To give the most flexibility to user agents in this respect, we allow migration of an
412- <a>animator instance</a> while it is running. The basic mechanism is to serialize the internal state
413- of any author-defined effect, and restore it after migration.
508+ The migration process allows <a>stateful animator instance</a> to be migrated to a different
509+ {{WorkletGlobalScope}} without losing their local state.
414510
415511<div algorithm="migrate-animator">
416512
@@ -429,17 +525,17 @@ To <dfn>migrate an animator instance</dfn> from one {{WorkletGlobalScope}} to an
429525
430526 If |definition| does not exist then abort the following steps.
431527
432- 3. Let |destroyFunction | be the <a>destroy function </a> of |definition|.
528+ 3. Let |stateful | be the <a>stateful flag </a> of |definition|.
433529
530+ 4. If |stateful| is <a>false</a> then abort the following steps.
434531
435- 4. <a>Invoke</a> |destroyFunction| with |instance| as the <a>callback this value</a> and
436- let |state| be the result of the invocation. If any exception is thrown, rethrow the
437- exception and abort the following steps.
532+ 5. Let |state| be the result of <a>Get</a> (|instance|, "state"). If any exception is thrown,
533+ rethrow the exception and abort the following steps.
438534
439- 5 . Set |serializedState| to be the result of <a>StructuredSerialize</a> (|state|).
535+ 6 . Set |serializedState| to be the result of <a>StructuredSerialize</a> (|state|).
440536 If any exception is thrown, then abort the following steps.
441537
442- 6 . Run the procedure to <a>remove an animator instance</a> given |instance|, and
538+ 7 . Run the procedure to <a>remove an animator instance</a> given |instance|, and
443539 |sourceWorkletGlobalScope|.
444540
445541 2. Wait for the above task to complete. If the task is aborted, abort the following steps.
@@ -456,6 +552,9 @@ To <dfn>migrate an animator instance</dfn> from one {{WorkletGlobalScope}} to an
456552
457553</div>
458554
555+ If an animator state getter throws the user agent will remove the animator but does not recreate it.
556+ This effectively removes the animator instance.
557+
459558
460559Requesting Animation Frames {#requesting-animation-frames}
461560----------------------------------------------------------
@@ -772,10 +871,15 @@ animation.play();
772871
773872// Inside AnimationWorkletGlobalScope
774873
775- registerAnimator('hidey-bar' , class {
776- constructor(options) {
874+ registerAnimator('hidey-bar' , class HidybarAnimator extends StatefulAnimator {
875+ constructor(options, state ) {
777876 this.scrollTimeline_ = options.scrollTimeline;
778877 this.documentTimeline_ = options.documentTimeline;
878+
879+ if (state) {
880+ this.startTime_ = state.startTime;
881+ this.direction_ = state.direction;
882+ }
779883 }
780884
781885 animate(currentTime, effect) {
@@ -800,6 +904,13 @@ registerAnimator('hidey-bar', class {
800904 // Drive the output effect by setting its local time.
801905 effect.localTime = localTime;
802906 }
907+
908+ getter state() {
909+ return {
910+ startTime: this.startTime_,
911+ direction: this.direction_
912+ }
913+ }
803914});
804915
805916</xmp>
@@ -850,15 +961,11 @@ animation.play();
850961
851962<xmp class='lang-javascript'>
852963// Inside AnimationWorkletGlobalScope.
853- registerAnimator('twitter-header' , class {
964+ registerAnimator('twitter-header' , class HeaderAnimator extends StatelessAnimator {
854965 constructor(options) {
855966 this.timing_ = new CubicBezier('ease-out' );
856967 }
857968
858- clamp(value, min, max) {
859- return Math.min(Math.max(value, min), max);
860- }
861-
862969 animate(currentTime, effect) {
863970 const scroll = currentTime; // scroll is in [0, 1000] range
864971
@@ -867,6 +974,11 @@ registerAnimator('twitter-header', class {
867974 effect.children[1] .localTime = this.timing_(clamp(scroll, 0, 500));
868975 }
869976});
977+
978+ function clamp(value, min, max) {
979+ return Math.min(Math.max(value, min), max);
980+ }
981+
870982</xmp>
871983
872984Example 3: Parallax backgrounds. {#example-3}
@@ -917,7 +1029,7 @@ fastParallax.play();
9171029
9181030<xmp class='lang-javascript'>
9191031// Inside AnimationWorkletGlobalScope.
920- registerAnimator('parallax' , class {
1032+ registerAnimator('parallax' , class ParallaxAnimator extends StatelessAnimator {
9211033 constructor(options) {
9221034 this.rate_ = options.rate;
9231035 }
0 commit comments