Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-view-transitions-2] Handling DOM updates when a transition's animation is reverse back to start/canceled #7957

Open
ydaniv opened this issue Oct 26, 2022 · 7 comments
Labels
css-view-transitions-2 View Transitions; New feature requests

Comments

@ydaniv
Copy link
Contributor

ydaniv commented Oct 26, 2022

Consider a case where the user has interacted with a page in a way which caused the animation playing in reverse back to the start, or was seeked back to start, like mentioned here for example. In such case the animation would either finish back on the "old" state or get cancel()'ed by the author, and should result in showing the "old" DOM again.

Such a case will require additional DOM update to render back previous state, prior to transition start. This may be both tricky for the author to handle, and slow since the transition finishes but the DOM's state isn't ready beforehand.

I see several options here:

  1. User needs to create a new ViewTransition back to previous state - same as a "back" navigation - which will trigger the flow of a new transition starting and aborting the current one.
  2. The browser can somehow cache the old DOM (like in a DocumentFragments or something?) - either declaratively or magically - for a quick reinsertion (though a framework would still need to "rehydrate" that).
  3. Provide other endpoints on the current API for such a case.

Not sure which, I guess 1 could be the easiest default perhaps.

cc @jakearchibald @khushalsagar @vmpstr

@jakearchibald
Copy link
Contributor

I think 1 is fine. 2 seems leaky, and unlikely to do what the developer wants.

For example, say a counter is updated via a websocket. Meanwhile, a transition is 'reversed'. The developer may want to go back to the previous view, but it's unlikely that they'd want the counter to decrement too. DOM caching wouldn't be able to differentiate between the two.

@khushalsagar
Copy link
Member

Expanding on the flow to make sure I got the details right:

  1. Developer starts the transition.
  2. While the animation is progressing, the user takes an action which should restore the page back to the old state. For example, they press the back button.
  3. The developer would like the animation to play in reverse (from the current state) and end with the DOM back to the old state.

First option would be easy but creating a new ViewTransition would cause us to first jump to the DOM state that the current transition will end at.

I'm trying to think whether this could be doable with the current API. Something like:

function goBack(transition) {
  // Reverse animations on all pseudo-elements.
  // Would UA CSS animations show up in this list? And would reverse() have them go back from current state.
  for (animation: document.getAnimations())
    animation.reverse();

  // When all the reverse animations are done, update DOM back to the old state.
  transition.finished.then(updateDomToOldState);
}

The other option would be that we add an API to do the animation reverse as a convenience:

function goBack(transition) {
  transition.reverse();
  // When all the reverse animations are done, update DOM back to the old state.
  transition.finished.then(updateDomToOldState);
}

Or, include the DOM update back to old state in the API since I'm assuming it'll need to be done for every use of the API:

function goBack(transition) {
  // The reverse API takes a callback which is invoked when all animations are done but before pseudo-elements
  // are removed.
  transition.reverse(updateDomToOldState);
}

@ydaniv
Copy link
Contributor Author

ydaniv commented Oct 29, 2022

@khushalsagar yes.
After I opened this issue I realized creating a new transition won't give us the result we need, because, as you say:

First option would be easy but creating a new ViewTransition would cause us to first jump to the DOM state that the current transition will end at.

So what we really need is reverse the animations so that the effect is running backwards from current position, not have it jump to end result and play from there. That means we're animating back from the "new" images to the "old" ones. And as the effect finishes we need to update the DOM again back to the old state and remove the "old" image that should be identical to the current state (or perhaps fade it out?).
And for that I guess we need to change the API to support this.

We don't need to take another snapshot of the UI, since we already have one, we probably want to simply reverse all running animations that are playing on the pseudo-tree of the transition, not really document.getAnimations() since that will return all CSS Animations, Web Animations, and CSS Transitions on the document and its descendants.
Maybe have something like transition.getAnimations()?
If we have paused animations, like mentioned in #7785, they will probably be scrubbed all the way back, and then signal to the transition that we want to update DOM back to old state and finish it, could be using same method as transition.reverse().

So, I suppose it could use a method like:

transition.reverse();

As a handy way to reverse the animations, and then when it's actually done we want to update the DOM back to old state.

What I'm still not sure of is how to handle the finished promise. Should it reject?

@khushalsagar
Copy link
Member

Ok. I'd be supportive of an API to reverse the transition to the old state. The assumption being that we don't need to re-snapshot anything since the old state should be identical to the snapshots we cached. Just need to ensure that the developer doesn't switch the DOM back to the old state until the reverse animations are done, since until then we're using the DOM for live snapshots of the current state.

The pattern for when it's ok to update the DOM back to the old state is:

  • Use the finished promise:
      transition.reverse();
      transition.finished.then(updateDOMToOldState);
  • Add it to the API
       transition.reverse(updateDOMToOldState);

I think the finished promise should still resolve. The reverse API simply changes the notion of what the end state is. And if that's the case then option 1 is simpler. If the developer keeps calling reverse multiple times then we'd keep flipping between going forwards/backwards? :)

@jakearchibald
Copy link
Contributor

I handled this when creating the gesture demo and it worked pretty well.

I reversed the animation, then held the final state until I'd reset the DOM back to its original state.

The only really hacky bit, is I had to create a long dummy animation to prevent the view transition ending before the old DOM was back in place. #8132 would solve that.

I think we can close this until we have more concrete info on the patterns and what could be done to make them easier.

@khushalsagar khushalsagar changed the title [css-view-transitions-1] Handling DOM updates when a transition's animation is reverse back to start/canceled [css-view-transitions-2] Handling DOM updates when a transition's animation is reverse back to start/canceled Jan 5, 2023
@khushalsagar khushalsagar added css-view-transitions-2 View Transitions; New feature requests and removed css-view-transitions-1 View Transitions; Bugs only labels Jan 5, 2023
@khushalsagar
Copy link
Member

@ydaniv wdyt? Does reversing the animation (as-in Jake's demo) + #8132 make this easy enough or do we need a better API for this pattern?

@ydaniv
Copy link
Contributor Author

ydaniv commented Jan 10, 2023

Yeah, what Jake showed is what I was referring to in #7785. As I understood from that resolution is that an author can pause the animation and control it via rAF to either direction, and it should keep the pseudo tree alive while the animations are paused.
So I'm a little confused why we still need #8132 to solve that.

Regardless, to get the reversed transition working you need to "hold" the progress at ~0.001%, update the DOM back to previous state "manually", and then reverse all of them manually, and then the hacky bit with cancelling the "holding animation". And all of this for a rAF controlled animation.

So I think there should be a more dev-friendly way to reverse all underlaying animations and/or cancel the transition in case of:

  • Transition's animations are driven by rAF (later may be scroll-driven or future pointer-driven?) and then author wants to essentially cancel the Transition (or reverse it back to 0% and cancel) once the user made a gesture all the way back to initial position.
  • Transition's animations are linked to the document timeline and the user reversed the navigation in mid-transition, e.g. clicked "backspace"

And then we can still reuse current pseudo tree, and have a way of holding 0% progress, update DOM back, and then cancel and remove the pseudo tree.

@ydaniv ydaniv changed the title [css-view-transitions-2] Handling DOM updates when a transition's animation is reverse back to start/canceled [css-view-transitions-1] Handling DOM updates when a transition's animation is reverse back to start/canceled Jan 10, 2023
@ydaniv ydaniv changed the title [css-view-transitions-1] Handling DOM updates when a transition's animation is reverse back to start/canceled [css-view-transitions-2] Handling DOM updates when a transition's animation is reverse back to start/canceled Jan 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-view-transitions-2 View Transitions; New feature requests
Projects
None yet
Development

No branches or pull requests

3 participants