Open bokand opened 5 months ago
Thank you @bokand for filing this. Had this on my backlog to do :)
I did indeed find the need to prevent a View Transition from automatically finishing. This when using a non-monotonic timeline as the source of time progress that drives the animations – in my case a ScrollTimeline
.
While you could say this auto-finish behavior is fine for ScrollTimeline
in my specific demo – the VT should after all end at a certain point in time, it becomes more tricky when the timeline’s animation range is the full 100% or for things like a (currently spec-fictional) GestureTimeline
.
Practically, continuing with GestureTimeline
, I’m thinking of a situation where you want to drive a VT via a drag gesture over a dragdistance of 200px. When crossing the 200px boundary you don’t want the VT to finish immediately as the user – while still dragging – might change their mind and drag back to the 180px point. When doing so the same VT should then rewind a little bit, instead of creating a new one. Only on pointerup
that VT should either snap back to its original state (when dragging back) or play to its end state (when dragging forward).
That to say: It depends on the type of timeline (ScrollTimeline, ViewTimeline, GestureTimeline, MediaPlaybackTimeline, …) and the use case.
So maybe this should be an opt-in, e.g. vtObject.preventAutoFinish()
or document.startViewTransition({ callback, autofinish: false });
?
(I also thought of maybe using vtObject.pause()
instead of vtObject.preventAutoFinish()
here but that doesn’t completely make sense here because you aren’t always pausing the VT’s animations)
I think authors can finish the view transition manually by calling cancel() on the transition animations?
What about vtObject.finish()
? I choose finish()
here because the vt would first play (forwards) and reach the finished
state.
Along with vtObject.finish()
, it would be nice here if there also were something like vtObject.revert()
to have the animations play back to the start and then have the VT undo the DOM update. This for the situation where one starts dragging in one direction but then changes their mind and drags a little bit back in the opposite direction.
What about vtObject.finish()? I choose finish() here because the vt would first play (forwards) and reach the finished state.
Yeah, if we had some manual control on the vtObject that might make sense. I was considering the case where we just make it manual by default (in the case of a non-monotonic timeline). In that scenario, calling animation.finish
wouldn't work since the VT no longer ends when the animations are finished; you'd have to use animation.cancel
. But I suppose we could also add a vtObject.finish
for that case as well...
Along with vtObject.finish(), it would be nice here if there also were something like vtObject.revert() to have the animations play back to the start and then have the VT undo the DOM update.
The hard part is undoing the DOM update - I don't think it's feasible to undo the update automatically; the author would have to pass in an "undo" callback. If this is common enough though it might be a nice ergonomic improvement (the author can already do all this themselves today)
the author would have to pass in an "undo" callback. If this is common enough though it might be a nice ergonomic improvement
This is effectively what I did in the gesture demo. When the VT has finished but it turned out to be a no-op (i.e. the animations reverted to the old state), I execute some logic to undo the DOM update.
The idea of a VT not automatically finishing SGTM. We already had a use-case for this with rAF driven animations in https://github.com/w3c/csswg-drafts/issues/8132 and its trivial to implement. There's an alternate API suggestion there which does both: indicate that the transition shouldn't automatically finish and a promise to listen to for finishing it.
With the above, I'm assuming we wouldn't need to change anything about our logic for how "auto finish" works. Both conditions need to happen for a transition to finish:
waitUntil
promises, they have to settle.I think 2) also helps with: "The hard part is undoing the DOM update". If the transition needs to be aborted, the author plays the animation in reverse. Now the DOM is only showing ::view-transition-old
snapshots. The author updates the DOM and resolves the waitUntil
promises. The DOM state should match the old snapshots being displayed so flip from VT pseudo-DOM to live old DOM is seamless.
The is current property in web-animations captures the concept of animations on non-monotonic timelines which may still be animating.
The CSS Working Group just discussed [css-view-transitions-1] How should scroll timeline animations be treated?
, and agreed to the following:
RESOLVED: Add .waitUntil() promise to the VT object, prevents VT from finishing until the promises settle
A view transition is removed when all constituent animations are no longer running or paused. However, the spec only defines this for document timelines:
We should at least define what happens for other kinds of timelines.
However, as @bramus found it might be more useful to keep a view transition alive while it has a scroll timeline regardless of its current state, since it can be reversed by the user (unlike a document timeline). If we do that, I think authors can finish the view transition manually by calling
cancel()
on the transition animations? Perhaps we could make this more convenient via theViewTransition
object?