diff --git a/web-animations-2/Overview.bs b/web-animations-2/Overview.bs index 58a1cca0aef..bd21d0d9d76 100644 --- a/web-animations-2/Overview.bs +++ b/web-animations-2/Overview.bs @@ -107,6 +107,7 @@ previous level of this specification: playback rate, * custom effects. * Support for non-monotonic (scroll) timelines. +* Adds Animation hold phase.

Timing model

@@ -148,6 +149,53 @@ Along with the following updated description:
This section is non-normative +The children of a timeline are called animations. +An animation takes an animation effect which is a static +description of some timed behavior and binds it to a timeline +so that it runs. +An animation also allows run-time control of the connection between the +animation effect and its timeline by providing pausing, +seeking, and speed control. +The relationship between an animation and an animation effect is +analogous to that of a DVD player and a DVD. +
+ +An animation connects a single animation +effect, called its associated effect, to a timeline and +provides playback control. +Both of these associations are optional and configurable such that +an animation may have no associated effect or +timeline at a given moment. + +An [=animation's=] document for timing is the {{Document}} with which +its [=timeline=] is associated. +If an animation is not associated with a timeline, or its timeline is not +associated with a document, then it has no [=document for timing=]. + +An animation's start time is the +time value of its timeline when its associated effect +is scheduled to begin playback. +An animation's start time is initially unresolved. + +An animation also maintains a hold time time value +which is used to fix the animation's output time value, called its +current time, in circumstances such as pausing. +The hold time is initially unresolved. + +In addition to the hold time, an animation maintains a +hold phase which is set along with the hold time to determine +the current phase of an animation with a fixed +current time. +The hold phase has the same range of values as the [=timeline phase=] and +can be set or unset. +The hold phase is initially unset. + +In order to establish the relative ordering of conflicting animations, +animations are appended to a global animation list in the order +in which they are created. Certain classes of +animations, however, may provide alternative means of ordering animations +(see [[#animation-classes]]). +

Setting the timeline of an animation

The procedure to set the timeline of an animation, @@ -160,6 +208,7 @@ follows: abort this procedure. 1. Let |previous play state| be |animation|'s [=play state=]. 1. Let |previous current time| be the |animation|'s [=current time=]. +1. Let |previous current phase| be the |animation|'s [=current phase=]. 1. Let |from finite timeline| be true if |old timeline| is not null and not [=monotonically increasing=]. 1. Let |to finite timeline| be true if |timeline| is not null and not @@ -193,6 +242,7 @@ follows: :: 1. Set the flag |reset current time on resume| to true. 1. Set [=start time=] to unresolved. 1. Set [=hold time=] to |previous current time|. + 1. Set [=hold phase=] to |previous current phase|.

This step ensures that the [=current time=] is preserved @@ -214,7 +264,7 @@ follows: 1. If the [=start time=] of animation is resolved, make animation's hold time - unresolved. + unresolved and make hold phase unset.

This step ensures that the finished play state of @@ -271,6 +321,39 @@ descendant effects and custom effects such that the first condition is: > animation effect. +

The current phase of an +Animation

+ +In addition to their current time, animations provide their +associated effects with a current phase. +This is used in determining the animations effect's phase in certain boundary +conditions. + +An animation's current phase has the same range of values as the +[=timeline phase=]. + +The current phase is calculated from the first matching condition from +below: + +
+ +: If the animation's hold phase is set, +:: The current phase is the animation's hold phase. + +: If either of the following are true: + + 1. the animation has no associated timeline, or + 1. the animation's [=start time=] is unresolved. + +:: The current phase is [=timeline inactive phase|inactive=]. + +: Otherwise, +:: The current phase is the [=timeline phase=] of the associated + timeline. + +
+ +

Setting the current time of an Animation

@@ -305,7 +388,9 @@ an animation, animation, to seek time is as follows: inactive, or * animation's [=playback rate=] is 0, - :: Set animation's hold time to seek time. + :: 1. Set animation's hold time to seek time. + :: 1. Set animation's hold phase to + [=timeline active phase|active=]. : Otherwise, :: Set animation's [=start time=] to the result of evaluating @@ -338,6 +423,8 @@ The procedure to set the current time of an animation, 1. If animation has a pending pause task, synchronously complete the pause operation by performing the following steps: 1. Set animation's hold time to seek time. + 1. Set animation's hold phase to + [=timeline active phase|active=]. 1. [=Apply any pending playback rate=] to |animation|. 1. Make animation's [=start time=] unresolved. 1. Cancel the pending pause task. @@ -363,7 +450,7 @@ is as follows: 1. If timeline time is unresolved and new start time is resolved, make animation's - hold time unresolved. + hold time unresolved and make hold phase unset.

This preserves the invariant that when we don't have an active timeline it @@ -378,25 +465,31 @@ is as follows: previous step which may cause the current time to become unresolved. +1. Let previous current phase be animation's current + phase. + 1. [=Apply any pending playback rate=] on |animation|. 1. Set animation's [=start time=] to new start time. 1. Set the |reset current time on resume| flag to false. -1. Update animation's hold time based on the first matching - condition from the following, +1. Update animation's hold time and hold phase based + on the first matching condition from the following,

: If new start time is resolved, - :: If animation's [=playback rate=] is not zero, - make animation's hold time unresolved. + :: If animation's [=playback rate=] is not zero, + make animation's hold time unresolved and make + hold phase unset. : Otherwise (new start time is unresolved), :: Set animation's hold time to previous current time even if previous current time is unresolved. + :: Set animation's hold phase to previous current + phase.
@@ -477,10 +570,14 @@ as CSS Animations [[CSS-ANIMATIONS-1]]. : If |has finite timeline| is true, :: 1. Set animation's start time to seek time. - 1. Let |animation|'s [=hold time=] be unresolved. + 1. Let |animation|'s [=hold time=] be unresolved and + hold phase be unset. 1. [=Apply any pending playback rate=] on |animation|. : Otherwise, - :: Set animation's hold time to seek time. + :: 1. Set animation's hold time to seek + time. + 1. Set animation's hold phase to + [=timeline active phase|active=]. @@ -534,7 +631,7 @@ as CSS Animations [[CSS-ANIMATIONS-1]]. 1. Set the [=start time=] of |animation| to |new start time|. 1. If |animation|'s [=playback rate=] is not 0, make |animation|'s - [=hold time=] [=unresolved=]. + [=hold time=] [=unresolved=] and make [=hodl phase=] unset. : If |animation|'s [=start time=] is resolved and |animation| has a [=pending playback rate=], @@ -546,7 +643,8 @@ as CSS Animations [[CSS-ANIMATIONS-1]]. 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|. + [=hold time=] be |current time to match| and let [=hold phase=] + be the |animation|'s [=current phase=]. 1. Let |new start time| be the result of evaluating |ready time| - |current time to match| / @@ -615,25 +713,442 @@ updating [=custom effects=].

Pausing an animation

-The procedure to [=pause an animation=] needs to refer to not on the [=target -effect=] but also any descedants of the [=target effect=]. +Whenever an animation has an unresolved [=start time=], +its current time will be suspended. + +As with playing an animation, pausing may not +happen instantaneously (see [[#waiting-for-the-associated-effect]]). +For example, if animation is performed by a separate process, it may +be necessary to synchronize the current time to ensure that it +reflects the state drawn by the animation process. -Likewise, the procedure to [=pause an animation=] needs to include scheduling -a task for updating [=custom effects=]. +The procedure to pause an animation, animation, is as +follows: + +1. If animation has a pending pause task, abort these steps. +1. If the play state of animation is paused, abort these steps. +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 current time is unresolved, + perform the steps according to the first matching condition from below: + +
+ + : If animation's [=playback rate=] is ≥ 0, + :: Set seek time to zero. + : Otherwise, + :: +
+ + : 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. + +
+ +
+ +1. If |seek time| is resolved, + +
+ + : If |has finite timeline| is true, + :: Set animation's start time to seek time. + : Otherwise, + :: 1. Set animation's hold time to + seek time. + 1. Set animation's hold phase to + [=timeline active phase|active=]. + +
+ +1. Let has pending ready promise be a boolean flag that is + initially false. +1. If animation has a pending play task, cancel that task + and let has pending ready promise be true. +1. If has pending ready promise is false, + set animation's current ready promise to + a new promise in the relevant Realm of animation. +1. 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. + + The task shall perform the following steps: + + 1. Let ready time be the time value of the timeline associated + with animation at the moment when the user agent completed + processing necessary to suspend playback of animation's + associated effect. + + 1. If animation's [=start time=] + is resolved and its hold time is + not resolved, + let animation's hold time be the result of evaluating + (ready time - [=start time=]) × + [=playback rate=] and let animation's + hold phase be the animation's current phase. + + Note: The hold time might be already set if the animation + is finished, or if the animation + has a pending play task. + In either case we want to preserve the hold time as we + enter the paused state. + + 1. [=Apply any pending playback rate=] on |animation|. + + 1. Make animation's [=start time=] unresolved. + + 1. Resolve animation's current + ready promise with animation. + + 1. Run the procedure to update an animation's finished state for + animation with the did seek flag set to false, + and the synchronously notify flag set to false. + + So long as the above task is scheduled but has yet to run, + animation is described as having a pending pause task. + While the task is running, however, animation does + not have a pending pause task. + + As with the [=pending play task=], the user agent must run the [=pending + pause task=] asynchronously, although that may be as soon as the next microtask checkpoint. + +1. Run the procedure to update an animation's finished state for + animation with the did seek flag set to false, + and the synchronously notify flag set to false. + + +

Updating the finished state

+ +For an animation with a positive [=playback rate=], the current time +continues to increase until it reaches the associated effect end. + +The associated effect end of an animation is equal to the end +time of the animation's associated effect. +If the animation has no associated effect, the associated effect +end is zero. + +For an animation with a negative [=playback rate=], the current time +continues to decrease until it reaches zero. + +A running animation that has reached this boundary (or overshot it) and has a +resolved [=start time=] +is said to be finished. + +The crossing of this boundary is checked on each modification to the +animation object using the procedure to update an animation's finished +state defined below. This procedure is also run as part of the [=update +animations and send events=] procedure. In both cases the did seek +flag, defined below, is set to false. + +For each animation, the user agent maintains a previous current +time time value that is originally unresolved. + +Whilst during normal playback the current time of an animation is +limited to the boundaries described above, it is possible to seek the current +time of an animation to times outside those boundaries using the +procedure to set the current time of an animation. + +The procedure to update an animation's finished state for +animation, given a flag did seek (to indicate if +the update is being performed after setting the +current time), and a flag synchronously notify (to indicate +the update was called in a context where we expect finished event queueing +and finished promise resolution to happen immediately, if at all) is as +follows: + +1. Let the unconstrained current time be the result of calculating + the current time substituting an unresolved time value for the + hold time if did seek is false. + If did seek is true, the unconstrained current time + is equal to the current time. + + Note: This is required to accommodate timelines that may change direction. + Without this definition, a once-finished animation would remain finished + even when its timeline progresses in the opposite direction. + +1. If all three of the following conditions are true, + + * the unconstrained current time is resolved, and + * animation's [=start time=] is + resolved, and + * animation does not have a pending play task + or a pending pause task, + + then update animation's hold time and hold phase + based on the first matching condition for animation from below, + if any: + +
+ + : If [=playback rate=] > 0 and + unconstrained current time is greater than or equal to + associated effect end, + :: If did seek is true, let the hold time + be the value of unconstrained current time and let + hold phase be [=timeline active phase|active=]. + + If did seek is false, let the hold time be the maximum + value of previous current time and associated effect end + and let hold phase be the current phase. + If the previous current time is + unresolved, let the hold time be associated + effect end. + : If [=playback rate=] < 0 and + unconstrained current time is less than or equal to 0, + :: If did seek is true, let the hold time + be the value of unconstrained current time and let + hold phase be [=timeline active phase|active=]. + + If did seek is false, let the hold time be the + minimum value of previous current time and zero and let + hold phase be the current phase. + If the previous current time is + unresolved, let the hold time be zero. + : If [=playback rate=] ≠ 0, and + animation is associated with an + active timeline, + :: Perform the following steps: + + 1. If did seek is true and the hold time is resolved, let animation's + [=start time=] be equal to the result of evaluating + |timeline time| - ([=hold time=] / [=playback rate=]) + where timeline time is the current time value + of timeline associated with animation. + + 1. Let the hold time be unresolved. + 1. Let the hold phase be unset. + +
+ +1. Set the previous current time of animation be the + result of calculating its current time. +1. Let current finished state be true if the play + state of animation is + finished. + Otherwise, let it be false. +1. If current finished state is true and the current finished + promise is not yet resolved, perform the following steps: + + 1. Let finish notification steps refer to the following + procedure: + 1. If animation's play state is not equal to finished, abort these steps. + 1. Resolve animation's + current finished promise object with + animation. + 1. Create an {{AnimationPlaybackEvent}}, + |finishEvent|. + 1. Set |finishEvent|'s {{Event/type}} attribute to finish. + 1. Set |finishEvent|'s {{AnimationPlaybackEvent/currentTime}} attribute + to the [=current time=] of |animation|. + 1. Set |finishEvent|'s {{AnimationPlaybackEvent/timelineTime}} + attribute to the current time + of the [=timeline=] with which |animation| is associated. + If |animation| is not associated with a timeline, or the timeline + is inactive, let + {{AnimationPlaybackEvent/timelineTime}} be + null. + 1. If |animation| has a [=document for timing=], then append + |finishEvent| to its [=document for timing=]'s [=pending animation + event queue=] along with its target, |animation|. + For the [=scheduled event time=], use the result of converting + |animation|'s [=associated effect end=] to an origin-relative time. + + Otherwise, [=queue a task=] to [=dispatch=] |finishEvent| at + |animation|. + The task source for this task is the [=DOM manipulation task + source=]. + 1. If synchronously notify is true, cancel any queued + microtask to run the finish notification steps for this + animation, and run the finish notification + steps immediately. + + Otherwise, if synchronously notify is false, queue + a microtask to run finish notification steps for + animation unless there is already a microtask queued to run + those steps for animation. +1. If current finished state is false and + animation's current finished promise is already + resolved, set animation's current finished promise to + a new promise in the relevant Realm of animation. + +
+ +Typically, notification about the finished state of an animation is +performed asynchronously. This allows for the animation to temporarily +enter the finished state without +triggering events to be fired or promises to be resolved. + +For example, in the following code fragment, animation temporarily +enters the finished state. If notification of the finished state occurred +synchronously this code would cause the finish event to be queued +and the current finished promise to be resolved. However, if we +reverse the order of the two statements such that the +iterations is updated first, this would not happen. +To avoid this surprising behavior, notification about the finished state of +an animation is typically performed asynchronously. + +
+var animation = elem.animate({ left: '100px' }, 2000);
+animation.playbackRate = 2;
+animation.currentTime = 1000; // animation is now finished
+animation.effect.updateTiming({ iterations: 2 }); // animation is no longer finished
+
+ +The one exception to this asynchronous behavior is when the finish an +animation procedure is performed (typically by calling the +{{Animation/finish()}} method). In this case the author's intention to finish +the animation is clear so the notification about the finished state of the +animation occurs synchronously as demonstrated below. + +
+var animation = elem.animate({ left: '100px' }, 1000);
+animation.finish(); // finish event is queued immediately and finished promise
+                    // is resolved despite the fact that the following statement
+                    // causes the animation to leave the finished state
+animation.currentTime = 0;
+
+ +Note that like the procedure to finish an animation, +the procedure to cancel an animation similarly queues the +cancel event and rejects the current finished promise and +current ready promise in a synchronous manner. + +
+ + +

Finishing an animation

+ +An animation can be advanced to the natural end of its current playback +direction by using the procedure to finish an animation +for animation defined below: + +1. If |animation|'s [=effective playback rate=] is zero, + or if |animation|'s [=effective playback rate=] > 0 + and associated effect end is infinity, + throw an "{{InvalidStateError}}" {{DOMException}} and + abort these steps. + +1. [=Apply any pending playback rate=] to |animation|. + +1. Set limit as follows: + +
+ + : If [=playback rate=] > 0, + :: Let limit be associated effect end. + + : Otherwise, + :: Let limit be zero. + +
+ +1. Silently set the current time to limit. + +1. If animation's [=start time=] is + unresolved and animation has an associated active timeline, let the + [=start time=] be the result of evaluating + timeline time - + (limit / [=playback rate=]) + where timeline time is the current time + value of the associated timeline. + + 1. If there is a pending pause task and + [=start time=] is resolved, + + 1. Let the hold time be unresolved. + +
Typically the hold time will already be + unresolved except in the case when the animation was previously idle.
+ + 1. Let the hold phase be unset. + + 1. Cancel the pending pause task. + + 1. Resolve the current ready promise + of animation with animation. + +1. If there is a pending play task and [=start time=] is + resolved, cancel that task and resolve the current ready promise of + animation with animation. + +1. Run the procedure to update an animation's finished state for + animation with the did seek flag set to true, + and the synchronously notify flag set to true.

Canceling an animation

-The procedure to [=cancel an animation=] needs to include the final step: +An animation can be canceled which causes the current time to +become unresolved hence removing any effects caused by the +associated effect. -> 1. Queue a task to call any custom effects associated with -> inclusive descendants of -> animation's target effect -> with an unresolved 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. +The procedure to cancel an animation for animation is +as follows: + +1. If animation's play state is not + idle, perform the following steps: + 1. Run the procedure to reset an animation's pending tasks on + animation. + 1. Reject the current finished promise + with a DOMException named "AbortError". + 1. Set the \[[PromiseIsHandled]] internal slot of + the current finished promise to true. + 1. Let current finished promise be + a new promise in the relevant Realm of + animation. + 1. Create an {{AnimationPlaybackEvent}}, + |cancelEvent|. + 1. Set |cancelEvent|'s {{Event/type}} attribute to cancel. + 1. Set |cancelEvent|'s {{AnimationPlaybackEvent/currentTime}} to + null. + 1. Let |timeline time| be the current + time of the [=timeline=] with which |animation| is associated. + If |animation| is not associated with an active timeline, let |timeline time| be n + [=unresolved=] [=time value=]. + 1. Set |cancelEvent|'s {{AnimationPlaybackEvent/timelineTime}} to + |timeline time|. If |timeline time| is [=unresolved=], set it to + null. + 1. If |animation| has a [=document for timing=], then append + |cancelEvent| to its [=document for timing=]'s [=pending animation + event queue=] along with its target, |animation|. + If |animation| is associated with an active + timeline that defines a procedure to convert timeline times to origin-relative + time, let the [=scheduled event time=] be the result of applying + that procedure to |timeline time|. + Otherwise, the [=scheduled event time=] is an [=unresolved=] [=time + value=]. + + Otherwise, [=queue a task=] to [=dispatch=] |cancelEvent| at + |animation|. + The task source for this task is the [=DOM manipulation task + source=]. + +1. Make animation's hold time unresolved. +1. Make animation's hold phase unset. +1. Make animation's [=start time=] unresolved.

Speed control

@@ -697,8 +1212,6 @@ Finally, the description of the [=end delay=] is updated to: > An end delay may also be specified but is primarily only of > use when sequencing animations such as by using a sequence effect. - - The normative description is updated as follows: > The lower bound of the active interval is defined by the @@ -877,6 +1390,36 @@ condition: > * the animation effect has a parent group and the > parent group is current. +Additional conditions have been added for determining if an effect is in the +before phase or in the after phase. With the new +conditions it should be as follows: + +> An animation effect is in the before phase if the +> animation effect's local time is not unresolved and +> any of the following conditions are met: +> +> 1. the local time is less than the before-active boundary time, +> or +> 1. the effect is associated with an animation and the +> associated animation's current phase is +> [=timeline before phase|before=] and the local time is equal +> to the before-active boundary time, or +> 1. the animation direction is ‘backwards’ and the local +> time is equal to the before-active boundary time. +> +> An animation effect is in the after phase if the +> animation effect's local time is not unresolved and +> any of the following conditions are met: +> +> 1. the local time is greater than the active-after boundary +> time, or +> 1. the effect is associated with an animation and the +> associated animation's current phase is +> [=timeline after phase|after=] and the local time is equal +> to the active-after boundary time, or +> 1. the animation direction is ‘forwards’ and the local +> time is equal to the active-after boundary time. +

Fill modes