-
Notifications
You must be signed in to change notification settings - Fork 689
/
Copy pathOverview.bs
3807 lines (2927 loc) · 142 KB
/
Overview.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<style>
.informative-bg {
margin: 1.5em 0 1em;
padding: 1em;
margin-top: 1em;
background: #efe;
border: green 1px dotted;
}
@media (prefers-color-scheme: dark) {
.informative-bg {
background: rgba(255, 255, 255, .05);
}
}
div.informative-bg *:last-child {
margin-bottom: 0;
}
div.informative-bg p:first-child {
margin-top: 0;
}
div.informative-bg h2,
div.informative-bg h3,
div.informative-bg h4 {
background: none;
}
/* crbug.com/1471465 */
dl.switch > dt {
counter-increment: list-item 0;
}
.attributes::before, .methods::before,
.parameters::before, .exceptions::before,
.constructors::before, .members::before {
font: bold 100% sans-serif;
text-align: left;
margin: 1.33em 0px;
color: var(--heading-text, #005A9C);
}
.attributes::before { content: 'Attributes' }
.methods::before { content: 'Methods' }
.parameters::before { content: 'Parameters' }
.exceptions::before { content: 'Exceptions' }
.constructors::before { content: 'Constructors' }
.members::before { content: 'Dictionary members' }
dl.switch > [data-md] > p {
display: inline;
}
dl.switch > dt > ul > li {
text-indent: 0;
}
</style>
<pre class='metadata'>
Title: Web Animations Level 2
Status: ED
Warning: not ready
Work Status: Exploring
Shortname: web-animations
ED: https://drafts.csswg.org/web-animations-2/
TR: https://www.w3.org/TR/web-animations-2/
Version history: https://github.com/w3c/csswg-drafts/commits/main/web-animations-2
Level: 2
Group: csswg
!Participate: <a href="https://github.com/w3c/csswg-drafts/tree/main/web-animations-2">Fix the text through GitHub</a>
!Participate: Join the ‘waapi’ channel on the <a href="https://damp-lake-50659.herokuapp.com/">Animation at Work</a> slack
!Participate: IRC: <a href="ircs://irc.w3.org:6667/webanimations">#webanimations</a> on W3C's IRC
Repository: w3c/csswg-drafts
!Issue Tracking: <a href="https://github.com/w3c/csswg-drafts/labels/web-animations-2">GitHub</a>
Editor: Brian Birtles 43194, Invited Expert, brian@birchill.co.jp
Editor: Robert Flack 98451, Google, flackr@google.com
Former Editor: Shane Stephens 47691, Google, shans@google.com
Former Editor: Douglas Stockwell, Google, dstockwell@google.com
Abstract: This specification defines a model for synchronization and
timing of changes to the presentation of a Web page.
This specification also defines an application programming interface
for interacting with this model and it is expected that further
specifications will define declarative means for exposing these
features.
Ignored Vars: auto-rewind, current direction, index, initial progress,
timeline, new timeline, t, going forwards, did trigger
</pre>
<pre class="anchors">
urlPrefix: https://drafts.csswg.org/web-animations-1/; type: dfn; spec: web-animations-1
text: active-after boundary time
text: active interval
text: active time
text: animation
text: animation direction
text: animation effect
text: Apply any pending playback rate
text: associated effect end
text: before-active boundary time
text: cancel an animation
text: current iteration
text: current ready promise
text: current finished promise
text: directed progress
text: effective playback rate
text: effect value
text: end delay
text: end time
text: fill mode
text: in effect
text: iteration count
text: iteration duration
text: iteration progress
text: iteration start
text: keyframe effect
text: not animatable
text: overall progress
text: pending pause task
text: pending play task
text: pending playback rate
text: play an animation
text: playback direction
text: pause an animation
text: reverse an animation
text: ready
text: set the timeline of an animation
text: simple iteration progress
text: target property
text: time value
text: timeline
text: transformed progress
text: update animations and send events
text: update the timing properties of an animation effect
text: unresolved
text: update an animation's finished state
text: default document timeline
urlPrefix: https://webidl.spec.whatwg.org/; type: dfn; spec: webidl
text: resolve a promise; url: resolve
urlPrefix: http://www.ecma-international.org/ecma-262/6.0/#sec-; type: dfn; spec: ecma-262
text: code realms
text: execution contexts
urlPrefix: https://drafts.csswg.org/scroll-animations-1/; type: dfn; spec: scroll-animations-1
text: html-processing-model-event-loops
</pre>
<pre class=link-defaults>
spec:webidl; type:dfn; text:throw
</pre>
<h2 id="delta">Delta specification</h2>
<p>This is a delta specification, meaning that it currently contains
only the differences from Web Animations Level 1 [[!WEB-ANIMATIONS-1]].
Once the Level 1 specification is closer to complete, it will be merged
with the additions here into a complete level 2 specification.</p>
<h2 id="changes-since-level-1">Changes since level 1</h2>
This specification introduces the following changes compared to the
previous level of this specification:
* <a>group effects</a> and <a>sequence effects</a>,
* an <a>animation effect</a>-specific
[=animation effect/playback rate=],
* Support for non-monotonic (scroll) timelines.
<div class="amendment" id=amend-CE>
This module also includes some exploratory proposals for [[#custom-effects]],
however there are some concerns about the design of this feature
and it may be replaced in a future revision.
See <a href="https://github.com/w3c/csswg-drafts/issues/6861">discussion</a>.
</div>
<h2 id="timing-model">Timing model</h2>
This section describes and defines the behavior of the Web Animations
timing model.
<h3 id="timing-model-overview">Timing model overview</h3>
<div class='informative-bg'>
<em>This section is non-normative</em>
<h4 id="hierarchical">Hierarchical</h4>
This level of the specification includes an updated timing hierarchy diagram.
<figure>
<img src="images/time-hierarchy.svg" width="600"
alt="A hierarchy of timing nodes">
<figcaption>
A hierarchy of timing nodes.
Each node in the tree derives its time from its parent node.
</figcaption>
</figure>
Along with the following updated description:
> A consequence of this hierarchical arrangement is that complex
> animation arrangements can be reversed, scheduled, accelerated and so
> on as a whole unit since the manipulations applied to the parent,
> cascade down to its <a>descendants</a>.
> Furthermore, since time has a common source, it is easy to synchronize
> animations.
</div>
<h3 id="timelines">Timelines</h3>
Add:
> The <dfn lt="timeline duration">duration</dfn> of a timeline gives the
> maximum value a timeline may generate for its current time. This value is
> used to calculate the [=intrinsic iteration duration=] for the target effect
> of an animation that is associated with the timeline when the effect's
> [=iteration duration=] is "auto". The value is computed such that the effect
> fills the available time. For a monotonic timeline, there is no upper bound
> on current time, and [=timeline duration=] is unresolved. For a non-monotonic
> (e.g. scroll) timeline, the duration has a fixed upper bound. In this
> case, the timeline is a <dfn lt="progress-based timeline">progress-based
> timeline</dfn>, and its [=timeline duration=] is 100%.
<h3 id="animation-frame-loop">Animation Frames</h3>
Update the note on the first step of the algorithm by adding the following bullet:
> * Updating the [=animation trigger state|state=] of [=animation triggers=]
> by running the [=updating animation trigger state=] procedure.
<h3 id="animations">Animations</h3>
Append:
> Animation effects associated with a [=progress-based timeline=] require their
> timing properties to be converted to proportions. The procedure for converting
> a <dfn lt="time-based animation to a proportional animation"></dfn> is as
> follows:
>
> 1. If the [=iteration duration=] is auto, then perform the following steps.
> * Set [=start delay=] and [=end delay=] to 0, as it is not
> possible to mix time and proportions.
>
> Note: Future versions may allow these properties to be assigned
> percentages, at which point the delays are only to be ignored if
> their values are expressed as times and not as percentages.
>
> Otherwise:
> 1. Let <var>total time</var> be equal to [=end time=]
> 1. Set [=start delay=] to be the result of evaluating
> <code>|specified start delay| / |total time| *
> |timeline duration|</code>.
> 1. Set [=iteration duration=] to be the result of evaluating
> <code>|specified iteration duration| / |total time| *
> |timeline duration|</code>.
> 1. Set [=end delay=] to be the result of evaluating
> <code>|specified end delay| / |total time| *
> |timeline duration|</code>.
>
> The procedure to <dfn>normalize specified timing</dfn> is as follows:
>
> If [=timeline duration=] is resolved:
> * Follow the procedure to convert a [=time-based animation to a
> proportional animation=]
> Otherwise:
> 1. Set [=start delay=] = |specified start delay|
> 1. Set [=end delay=] = |specified end delay|
> 1. If [=iteration duration=] is auto:
> * Set [=iteration duration=] = [=intrinsic iteration duration=]
> Otherwise:
> * Set [=iteration duration=] = |specified iteration duration|
### Setting the timeline of an animation ### {#setting-the-timeline}
The procedure to <dfn export for=animation>set the timeline of an animation</dfn>,
<var>animation</var>, to <var>new timeline</var> which may be null, is as
follows:
1. Let <var>old timeline</var> be the current <a>timeline</a> of
<var>animation</var>, if any.
1. If <var>new timeline</var> is the same object as <var>old timeline</var>,
abort this procedure.
1. Let |previous play state| be |animation|'s [=play state=].
1. Let |previous current time| be the |animation|'s [=animation/current time=].
1. Set |previous progress| based in the first condition that applies:
<dl class=switch>
: If |previous current time| is unresolved:
:: Set |previous progress| to unresolved.
: If [=end time=] is zero:
:: Set |previous progress| to zero.
: Otherwise
:: Set |previous progress| = <code>|previous current time| /
[=end time=]</code>
</dl>
1. Let |from finite timeline| be true if |old timeline| is not null and
not [=monotonically increasing timeline|monotonically increasing=].
1. Let |to finite timeline| be true if |timeline| is not null and not
[=monotonically increasing timeline|monotonically increasing=].
1. Let the <a>timeline</a> of <var>animation</var> be <var>new timeline</var>.
1. Perform the steps corresponding to the <em>first</em> matching
condition from the following, if any:
<dl class="switch">
: If |to finite timeline|,
:: 1. [=Apply any pending playback rate=] on |animation|
1. set |auto align start time| to true.
1. Set [=animation/start time=] to unresolved.
1. Set [=animation/hold time=] to unresolved.
1. If |previous play state| is "finished" or "running"
1. Schedule a [=pending play task=]
1. If |previous play state| is "paused" and
|previous progress| is resolved:
1. Set [=hold time=] to |previous progress| * [=end time=].
<p class="note">
This step ensures that |previous progress| is preserved
even in the case of a pause-pending animation with a
resolved [=animation/start time=].
</p>
: If |from finite timeline| and |previous progress| is resolved,
:: Run the procedure to <a>set the current time</a> to
|previous progress| * [=end time=].
</dl>
1. If the [=animation/start time=] of <var>animation</var> is <a
lt=unresolved>resolved</a>, make <var>animation</var>'s <a>hold time</a>
<a>unresolved</a>.
<p class="note">
This step ensures that the [=play state/finished|finished play state=] of
<var>animation</var> is not “sticky” but is re-evaluated
based on its updated [=animation/current time=].
<p>
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to false, and
the <var>synchronously notify</var> flag set to false.
<ins cite="#amend-CE">
Issue: If <var>new timeline</var> is null, we should ensure that <a>custom
effects</a> get called with an <a>unresolved</a> <a>iteration progress</a>
(unless a subsequent change in the same script execution context makes this
redundant).
</ins>
<h4 id="setting-the-target-effect">Setting the target effect of an
animation</h4>
After the step to reschedule a <a>pending play task</a> add the following step:
> 1. If <var>new effect</var> is not <code>null</code> and
> if <var>new effect</var> has a <a>parent group</a>,
> <a lt="remove an animation effect">remove</a> <var>new effect</var> from
> its <a>parent group</a>.
<ins cite="#amend-CE">
After the step to assign <var>new effect</var> as <var>animation</var>'s
<a>associated effect</a> include the following step:
> 1. If <var>old effect</var> is not <code>null</code>, queue a task to call
> any <a>custom effects</a> associated with <a>inclusive descendants</a> of
> <var>old effect</var> with an <a>unresolved</a> <a>iteration progress</a>.
>
> <div class="issue">
> This is not quite right. If <var>old effect</var> is attached to another
> animation in the same task then we should probably not do an extra
> callback with <a>unresolved</a>.
>
> The definition of when <a>custom effects</a> gets called needs to be
> audited and probably rewritten.
> </div>
</ins>
<h4 id='waiting-for-the-associated-effect'>Waiting for the associated effect</h4>
Replace:
> An animation is ready at the first moment
> where <em>both</em> of the following conditions are true:
>
> * the user agent has completed any setup required
> to begin the playback of the animation's [=associated effect=],
> including rendering the first frame of any [=keyframe effect=].
>
> * the animation is associated with a [=timeline=]
> that is not [=inactive timeline|inactive=].
With:
> An animation is <dfn>ready</dfn> at the first moment
> where <em>all</em> of the following conditions are true:
>
> * the user agent has completed any setup required to begin the playback of
> each <a>inclusive descendant</a> of
> the animation's <a>associated effect</a>
> including rendering the first frame of any <a>keyframe
> effect</a><ins cite="#amend-CE"> or executing any <a>custom effects</a> associated with an
> <a>animation effect</a></ins>.
>
> * the animation is associated with a [=timeline=]
> that is not [=inactive timeline|inactive=].
>
> * the animation's [=hold time=] or [=animation/start time=] is resolved.
Note: an animation is not <a>ready</a> while it has no [=animation/start time=] or [=hold time=].
For a [=scroll-driven animation=], the start-time is determined when [=html-processing-model-event-loop|updating the timeline=] if |auto align start time| is true.
<h4 id='validating-a-css-numberish-time'>Validating a CSSNumberish time</h4>
The procedure to <dfn>validate a CSSNumberish time</dfn> for an input
value of <var>time</var> is based on the first condition that matches:
<dl class="switch">
: If <em>all</em> of the following conditions are true:
* The animation is associated with a [=progress-based timeline=], and
* <var>time</var> is not a CSSNumeric value with percent units:
:: <a>throw</a> a <span class="exceptionname">TypeError</span>.
:: return false;
: If <em>all</em> of the following conditions are true:
* The animation is not associated with a [=progress-based timeline=],
and
* <var>time</var> is a CSSNumericValue, and
* the units of time are not <a type lt="<time>">duration units</a>:
:: <a>throw</a> a <span class="exceptionname">TypeError</span>.
:: return false.
: Otherwise
:: return true.
</dl>
<h4 id='setting-the-current-time-of-an-animation'>Setting the current time of an
Animation</h4>
The [=animation/current time=] of an animation can be set to a new value to
<em>seek</em> the animation.
The procedure for setting the current time is defined in two parts.
The procedure to <dfn>silently set the current time</dfn> of
an animation, <var>animation</var>, to <var>seek time</var> is as follows:
1. If <var>seek time</var> is an <a>unresolved</a> time value,
then perform the following steps.
1. If the [=animation/current time=] is <a lt=unresolved>resolved</a>, then
<a>throw</a> a <span class=exceptionname>TypeError</span>.
1. Abort these steps.
1. Let <var>valid seek time</var> be the result of running the
[=validate a CSSNumberish time=] procedure with <var>seek time</var>
as the input.
1. If <var>valid seek time</var> is false, abort this procedure.
1. Set |auto align start time| to false.
1. Update either <var>animation</var>'s <a>hold time</a> or
[=animation/start time=] as follows:
<dl class="switch">
: If <em>any</em> of the following conditions are true:
* <var>animation</var>'s <a>hold time</a> is
<a lt="unresolved">resolved</a>, or
* <var>animation</var>'s [=animation/start time=]
is <a lt="unresolved">unresolved</a>, or
* <var>animation</var> has no associated <a>timeline</a> or
the associated <a>timeline</a> is
<a lt="inactive timeline">inactive</a>, or
* <var>animation</var>'s [=playback rate=] is 0,
:: 1. Set <var>animation</var>'s <a>hold time</a> to
<var>seek time</var>.
: Otherwise,
:: Set <var>animation</var>'s [=animation/start time=] to the result of
evaluating
<code>|timeline time| - (|seek time| / [=playback rate=])</code>
where <var>timeline time</var> is the current <a>time value</a>
of <a>timeline</a> associated with <var>animation</var>.
</dl>
1. If <var>animation</var> has no associated <a>timeline</a> or the associated
<a>timeline</a> is <a lt="inactive timeline">inactive</a>,
make <var>animation</var>'s [=animation/start time=] <a>unresolved</a>.
<p class=note>
This preserves the invariant that when we don't have an active timeline it
is only possible to set <em>either</em> the [=animation/start time=]
<em>or</em> the animation's [=animation/current time=].
</p>
1. Make <a>animation</a>'s <var>previous current time</var> <a>unresolved</a>.
The procedure to <dfn>set the current time</dfn> of an animation,
<var>animation</var>, to <var>seek time</var> is as follows:
1. Run the steps to <a>silently set the current time</a> of
<var>animation</var> to <var>seek time</var>.
1. If <var>animation</var> has a <a>pending pause task</a>, synchronously
complete the pause operation by performing the following steps:
1. Set <var>animation</var>'s <a>hold time</a> to <var>seek time</var>.
1. [=Apply any pending playback rate=] to |animation|.
1. Make <var>animation</var>'s [=animation/start time=] <a>unresolved</a>.
1. Cancel the <a>pending pause task</a>.
1. <a lt="resolve a Promise">Resolve</a> <var>animation</var>'s
<a>current ready promise</a> with <var>animation</var>.
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to true, and
the <var>synchronously notify</var> flag set to false.
<h4 id='setting-the-start-time-of-an-animation'>Setting the start time of an
Animation</h4>
The procedure to <dfn>set the start time</dfn>
of <a>animation</a>, <var>animation</var>, to <var>new start time</var>,
is as follows:
1. Let <var>valid start time</var> be the result of running the
[=validate a CSSNumberish time=] procedure with
<var>new start time</var> as the input.
1. If <var>valid start time</var> is false, abort this procedure.
1. Set |auto align start time| to false.
1. Let <var>timeline time</var> be the current <a>time value</a> of the
<a>timeline</a> that <var>animation</var> is associated with.
If there is no <a>timeline</a> associated with <var>animation</var> or the
associated timeline is <a lt="inactive timeline">inactive</a>,
let the <var>timeline time</var> be <a>unresolved</a>.
1. If <var>timeline time</var> is <a>unresolved</a> and <var>new start
time</var> is <a lt="unresolved">resolved</a>, make <var>animation</var>'s
<a>hold time</a> <a>unresolved</a>.
<p class=note>
This preserves the invariant that when we don't have an active timeline it
is only possible to set <em>either</em> the [=animation/start time=]
<em>or</em> the animation's [=animation/current time=].
</p>
1. Let <var>previous current time</var> be <var>animation</var>'s
[=animation/current time=].
Note: This is the [=animation/current time=] after applying the changes from
the previous step which may cause the [=animation/current time=] to become
<a>unresolved</a>.
1. [=Apply any pending playback rate=] on |animation|.
1. Set <var>animation</var>'s [=animation/start time=] to
<var>new start time</var>.
1. Update <var>animation</var>'s <a>hold time</a> based
on the first matching condition from the following,
<dl class="switch">
: If <var>new start time</var> is <a lt="unresolved">resolved</a>,
:: If <var>animation</var>'s [=playback rate=] is not zero,
make <var>animation</var>'s <a>hold time</a> <a>unresolved</a>.
: Otherwise (<var>new start time</var> is <a>unresolved</a>),
:: 1. Set <var>animation</var>'s <a>hold time</a> to
<var>previous current time</var> even if
<var>previous current time</var> is <a>unresolved</a>.
</dl>
1. If <var>animation</var> has a <a>pending play task</a> or
a <a>pending pause task</a>, cancel that task and
<a lt="resolve a Promise">resolve</a> <var>animation</var>'s
<a>current ready promise</a> with <var>animation</var>.
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to true, and
the <var>synchronously notify</var> flag set to false.
<h4 id='playing-an-animation-section'>Playing an animation</h4>
The procedure to <dfn>play an animation</dfn>, <var>animation</var>, given
a flag <var>auto-rewind</var>, is as follows:
Note: The <var>auto-rewind</var> flag is provided for other specifications
that build on this model but do not require the rewinding behavior, such
as CSS Animations [[CSS-ANIMATIONS-1]].
1. Let <var>aborted pause</var> be a boolean flag that is true if
<var>animation</var> has a <a>pending pause task</a>, and false otherwise.
1. Let <var>has pending ready promise</var> be a boolean flag that is
initially false.
1. Let <var>has finite timeline</var> be true if |animation| has an associated
<a>timeline</a> that is not
[=monotonically increasing timeline|monotonically increasing=].
1. Let <var>previous current time</var> be the |animation|'s
[=animation/current time=]
1. Let <var>enable seek</var> be true if the <var>auto-rewind</var> flag is
true and <var>has finite timeline</var> is false.
Otherwise, initialize to false.
1. Perform the steps corresponding to the <em>first</em> matching
condition from the following, if any:
<dl class="switch">
: If |animation|'s [=effective playback rate=] > 0,
<var>enable seek</var> is true and <em>either</em>
<var>animation</var>'s:
* <var>previous current time</var> is <a>unresolved</a>, or
* <var>previous current time</var> < zero, or
* <var>previous current time</var> ≥ <a>associated effect end</a>,
:: Set the |animation|'s <a>hold time</a> to zero.
: If |animation|'s [=effective playback rate=] < 0,
<var>enable seek</var> is true and <em>either</em>
<var>animation</var>'s:
* <var>previous current time</var> is <a>unresolved</a>, or
* <var>previous current time</var> ≤ zero, or
* <var>previous current time</var> > <a>associated effect end</a>,
::
<dl class="switch">
: If <a>associated effect end</a> is positive infinity,
:: <a>throw</a> an "{{InvalidStateError}}" {{DOMException}} and
abort these steps.
: Otherwise,
:: Set the |animation|'s <a>hold time</a> to the
|animation|'s <a>associated effect end</a>.
</dl>
: If |animation|'s [=effective playback rate=] = 0 and |animation|'s
[=animation/current time=] is [=unresolved=],
:: Set the |animation|'s <a>hold time</a> to zero.
</dl>
1. If |has finite timeline| and |previous current time| is unresolved:
* Set the flag |auto align start time| to true.
Note: If play is called for a CSS animation during style update,
the |animation|'s [=animation/start time=] cannot be reliably calculated until post layout since the start time is to align with the start or end of the animation range (depending on the [=playback rate=]).
In this case, the animation is said to have an <dfn export for="animation">auto-aligned start
time</dfn>,
whereby the start time is automatically adjusted as needed to align the animation's progress to the animation range.
1. If <var>animation</var>'s <a>hold time</a> is <a lt=unresolved>resolved</a>,
let its [=animation/start time=] be <a>unresolved</a>.
1. If <var>animation</var> has a <a>pending play task</a> or a
<a>pending pause task</a>,
1. Cancel that task.
1. Set <var>has pending ready promise</var> to true.
1. If the following three conditions are <em>all</em> satisfied:
* |animation|'s [=hold time=] is [=unresolved=], and
* |aborted pause| is false, and
* |animation| does <em>not</em> have a [=pending playback rate=],
abort this procedure.
1. If <var>has pending ready promise</var> is false,
let <var>animation</var>'s <a>current ready promise</a> be
<a>a new promise</a> in the <a>relevant Realm</a> of <var>animation</var>.
1. Schedule a task to run as soon as <var>animation</var> is <a>ready</a>.
The task shall perform the following steps:
1. Assert that at least one of |animation|'s [=animation/start time=] or
[=hold time=] is <a lt=unresolved>resolved</a>.
1. Let <var>ready time</var> be the <a>time value</a> of
the <a>timeline</a> associated with <var>animation</var> at the moment
when <var>animation</var> became <a>ready</a>.
1. Perform the steps corresponding to the first matching condition below,
if any:
<dl class="switch">
: If |animation|'s [=hold time=] is <a lt=unresolved>resolved</a>,
:: 1. [=Apply any pending playback rate=] on |animation|.
1. Let |new start time| be the result of evaluating
<code>|ready time| - [=hold time=] / [=playback rate=]</code>
for |animation|.
If the [=playback rate=] is zero, let
|new start time| be simply |ready time|.
1. Set the [=animation/start time=] of |animation| to
|new start time|.
1. If |animation|'s [=playback rate=] is not 0, make
|animation|'s [=hold time=] [=unresolved=].
: If |animation|'s [=animation/start time=] is resolved and
|animation| has a [=pending playback rate=],
:: 1. Let |current time to match| be the result of evaluating
<code>(|ready time| - [=animation/start time=]) ×
[=playback rate=]</code> for |animation|.
1. [=Apply any pending playback rate=] on |animation|.
1. If |animation|'s [=playback rate=] is zero, let
|animation|'s [=hold time=] be |current time to match|.
1. Let |new start time| be the result of evaluating
<code>|ready time| - |current time to match| /
[=playback rate=]</code> for |animation|.
If the [=playback rate=] is zero, let |new start time| be simply
|ready time|.
1. Set the [=animation/start time=] of |animation| to
|new start time|.
</dl>
1. <a lt="resolve a promise">Resolve</a> <var>animation</var>'s <a>current
ready promise</a> with <var>animation</var>.
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to false,
and the <var>synchronously notify</var> flag set to false.
<div class="note">
Note that the order of the above two steps is important
since it means that an animation with zero-length <a>associated
effect</a> will resolve its <a>current ready promise</a>
before its <a>current finished promise</a>.
</div>
So long as the above task is scheduled but has yet to run,
<var>animation</var> is described as having a
<dfn>pending play task</dfn>.
While the task is running, however, |animation| does <em>not</em> have
a [=pending play task=].
If a user agent determines that |animation| is immediately <a>ready</a>, it
may schedule the above task as a microtask such that it runs at the next
<a lt="perform a microtask checkpoint">microtask checkpoint</a>, but it must
<em>not</em> perform the task synchronously.
<div class="note">
The above requirement to run the <a>pending play task</a> asynchronously
ensures that code such as the following behaves consistently between
implementations:
<div class='example'><pre class='lang-javascript'>
animation.play();
animation.ready.then(
() => { console.log('Playback commenced'); },
() => { console.log('Playback was canceled'); }
);
// Suppose some condition requires playback to be canceled...
animation.cancel();
// "Playback was canceled" will be printed to the console.
</pre></div>
In the above code, were the [=pending play task=] run synchronously, the
[=current ready promise=] would not be rejected.
</div>
1. Run the procedure to <a>update an animation's finished state</a> for
<var>animation</var> with the <var>did seek</var> flag set to false, and
the <var>synchronously notify</var> flag set to false.
Issue: The procedure to [=play an animation=] needs to include scheduling a task for
updating [=custom effects=].
<h5 id='auto-aligning-start-time'>Auto-aligning the start time</h5>
When attached to a non-monotonic timeline, the start time of the animation may be layout dependent.
In this case, we defer calculation of the start time until the timeline has been updated post
layout.
When updating timeline current time, the [=animation/start time=] of any attached animation is conditionally updated.
The procedure for <dfn export for="animation">calculating an auto-aligned start time</dfn> is as follows:
1. If the |auto-align start time| flag is false, abort this procedure.
1. If the timeline is inactive, abort this procedure.
1. If [=play state=] is idle, abort this procedure.
1. If [=play state=] is paused, and [=hold time=] is resolved,
abort this procedure.
1. Let |start offset| be the resolved timeline time corresponding to the start of the [=animation attachment range=].
In the case of view timelines, it requires a calculation based on the proportion of the cover range.
1. Let |end offset| be the resolved timeline time corresponding to the end of the [=animation attachment range=].
In the case of view timelines, it requires a calculation based on the proportion of the cover range.
1. Set [=animation/start time=] to |start offset| if [=effective playback rate=] ≥ 0,
and |end offset| otherwise.
1. Clear [=hold time=].
<h4 id='pausing-an-animation-section'>Pausing an animation</h4>
The procedure to [=pause an animation=] needs to refer not only to the
[=associated effect=] but also any descendants of the [=associated effect=].
<ins cite="#amend-CE">Likewise, the procedure to [=pause an animation=] needs to include scheduling
a task for updating [=custom effects=].</ins>
Update the constraints for setting the seek time to only apply when using a
monotonic timeline. A [=scroll-driven animation=] needs to defer setting the
hold time until the animation range has been computed.
Replace:
> 1. Let |seek time| be
> a [=time value=] that is initially [=unresolved=].
> 1. Let |has finite timeline| be true
> if |animation| has an associated [=timeline=]
> that is not [=monotonically increasing=].
>
> 1. If the |animation|'s [=animation/current time=] is [=unresolved=],
> perform the steps according to the first matching condition below:
>
> <dl class=switch>
>
> : If |animation|'s [=playback rate=] is ≥ 0,
> ::
> Set |seek time| to zero.
>
> : Otherwise,
> ::
> <dl class=switch>
>
> : If [=associated effect end=] for |animation| is positive infinity,
> ::
> [=throw=] an "{{InvalidStateError}}" {{DOMException}}
> and abort these steps.
>
> : Otherwise,
> ::
> Set |seek time| to |animation|'s [=associated effect end=].
>
> </dl>
>
> </dl>
>
> 1. If |seek time| is [=unresolved|resolved=],
>
> <div class=switch>
>
> : If |has finite timeline| is true,
> ::
> Set |animation|'s [=start time=] to |seek time|.
>
> : Otherwise,
> ::
> Set |animation|'s [=hold time=] to |seek time|.
>
> </div>
with:
> 1. Let |has finite timeline| be true
> if |animation| has an associated [=timeline=]
> that is not [=monotonically increasing=].
>
> 1. If the |animation|'s [=animation/current time=] is [=unresolved=] and
> |has finite timeline| is false,
> perform the steps according to the first matching condition below:
>
> <dl class=switch>
>
> : If |animation|'s [=playback rate=] is ≥ 0,
> ::
> Set [=hold time=] to zero.
>
> : Otherwise,
> ::
> <dl class=switch>
>
> : If [=associated effect end=] for |animation| is positive infinity,
> ::
> [=throw=] an "{{InvalidStateError}}" {{DOMException}}
> and abort these steps.
>
> : Otherwise,
> ::
> Set [=hold time=] to |animation|'s [=associated effect end=].
>
> </dl>
>
> </dl>
>
> 1. If |has finite timeline| is true,
> and the |animation|'s [=animation/current time=] is [=unresolved=]
> * Set the |auto align start time| flag to true.
Replace:
> Schedule a task to be executed at the first possible moment where both of the following conditions are true:
>
> * the user agent has performed any processing necessary to suspend the playback of animation’s associated effect, if any.
>
> * the animation is associated with a timeline that is not inactive.
With:
> Schedule a task to be executed at the first possible moment where all of the following conditions are true:
>
> * the user agent has performed any processing necessary to suspend the playback of animation’s associated effect, if any.
>
> * the animation is associated with a timeline that is not inactive.
>
> * the animation has a resolved [=hold time=] or [=animation/start time=].
Note: A [=animation/start time=] is still required for a pause-pending animation if the |auto-align start time| flag is true to properly align to the animation range.
The [=animation/start time=] is set following the procedure for [=calculating an auto-aligned start time=].
<h4 id='canceling-an-animation-section'>Canceling an animation</h4>
<ins cite="#amend-CE">
Append the final step:
> 1. Queue a task to call any <a>custom effects</a> associated with
> <a>inclusive descendants</a> of
> <var>animation</var>'s <a>associated effect</a>
> with an <a>unresolved</a> iteration progress.
>
> Issue: The procedures for calling custom effects need to be reworked.
> Currently they probably involve calling too often for changes that
> could be coalesced.
</ins>
<h4 id="speed-control">Speed control</h4>
<div class="informative-bg">
Add the following sentence:
> Note that <a>animation effects</a> also have a
> [=animation effect/playback rate=] associated
> with them that behaves differently to that defined here.
</div>
### Calculating the overall progress of an animation ### {#the-overall-progress-of-an-animation}
An animation's <dfn export for=animation>overallProgress</dfn> is the ratio of its
[=animation/current time=] to its [=associated effect end=].
<div algorithm="calculate animation overall progress">
The [=animation/overallProgress=] of an [=animation=], |animation|, is calculated as follows:
<dl class=switch>
: If <em>any</em> of the following are true:
* |animation| does not have an [=animation/associated effect=], or
* |animation|'s [=animation/current time=] is an [=unresolved=] time value,
::
|animation|'s [=animation/overallProgress=] is null.
: If |animation|'s [=associated effect end=] is zero,
::
: If |animation|'s [=animation/current time=] is negative,
:: |animation|'s [=animation/overallProgress=] is zero.