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.
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. +
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,
|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