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

Support interupption #157

Open
mattgperry opened this issue May 30, 2022 · 11 comments
Open

Support interupption #157

mattgperry opened this issue May 30, 2022 · 11 comments

Comments

@mattgperry
Copy link

mattgperry commented May 30, 2022

Not sure if this is under consideration but currently the pseudo elements are blocking events. I suspect this is by design but I think it keeps the animation feeling "webby" rather than engaging.

Here's a tab switcher using layout animations:

Page Transition API: https://codesandbox.io/s/page-transition-api-tabs-demo-forked-5e9h8n
Framer Motion: https://codesandbox.io/s/framer-motion-layout-animations-snxgv

When clicking a tab mid-transition, it doesn't respond until the animation has finished. This isn't a good user experience, the Framer Motion example feels more "real".

I don't think there's a bullet-proof solution that works great in all cases but I do think there are solutions that feel better than this. So for instance it could be that the outgoing DOM is ditched immediately in terms of events and pseudo event targets forward inputs to the new element they are representing.

@daKmoR
Copy link

daKmoR commented May 30, 2022

I'm curious - can you add a video showing the "unexpected" behavior? 🤗

@vmpstr
Copy link
Collaborator

vmpstr commented May 30, 2022

This is a good question and we've discussed it several times. The problem here is that it's hard to determine what the user is looking at when they click an item that happens to hit the pseudo element. Since the outgoing elements are captured as screenshots, they are not hit-testable in the sense that we don't know which DOM elements the screenshot represents (or indeed whether that DOM element is actually still 'alive'). We also don't want to dispatch a click event if the outgoing image (the screenshot) has high opacity, since the user may be clicking something different from the thing responding to the event.

Now, we could detect the opacity of these elements and figure out if it's reasonable to dispatch the event. Another problem here is that the position of the pseudo element may be different from the element that it represents, so the thing that we can (easily) hit test is the pseudo element and dispatch the event to the whole capture. It would then be up to script to figure out what to do with that event. It is somewhat harder to reinterpret the hit test in a pseudo element by adjusting the coordinates to where the originating element would be and dispatching the event there. I'm not sure that's easily doable.

Given all this, from your perspective, what would be a good solution here?

/cc @jakearchibald @khushalsagar

@vmpstr
Copy link
Collaborator

vmpstr commented May 30, 2022

Given all this, from your perspective, what would be a good solution here?

After re-reading the initial issue, I guess you already answered this :)

@mattgperry
Copy link
Author

@daKmoR

Screen.Recording.2022-05-30.at.11.14.47.mov

Towards the end here it's rapid but you can see I click some tabs that remain unresponsive because the page is still animating.

@vmpstr Yeah that would be my preferred solution. In the linked example I suppose it's a little less cut and dry as we're essentially clicking on "root". But I would expect here, as you say, to at least be able to click these incoming elements when opacity is > 0.5 or some compromise that isn't waiting for the animating to finish entirely with the root screenshot/pseudo element passing through user input.

@vmpstr
Copy link
Collaborator

vmpstr commented May 30, 2022

The other question is how would you set up the animation. Suppose you're going from A to B, and half way through the user clicks C. The final state here is 'C' but what transitions to C? Is it B or is it a blend of A and B? Does that make sense?

@absurdprofit
Copy link

The other question is how would you set up the animation. Suppose you're going from A to B, and half way through the user clicks C. The final state here is 'C' but what transitions to C? Is it B or is it a blend of A and B? Does that make sense?

The question I have really, is the page still responsive during a transition?

@mattgperry
Copy link
Author

The other question is how would you set up the animation. Suppose you're going from A to B, and half way through the user clicks C. The final state here is 'C' but what transitions to C? Is it B or is it a blend of A and B? Does that make sense?

Yeah it makes sense, it would be the current blend of A and B - the new outgoing images would be taken from this. Otherwise it wouldn’t be truly interruptible.

@khushalsagar
Copy link
Collaborator

@vmpstr and I discussed this in person today. Summarizing the conclusions from that conversation.

  • The first part of the problem here is the desired hit testing behaviour. And we could run a hit-test on the pseudo DOM tree being created for the transition. When it hits an incoming image pseudo element, we can map it from the pseudo element's coordinate space to the corresponding shared element's coordinate space (taking any transformations applied to the image with object* properties). Then continue the hit-test on the shared element sub-tree. The following cases are unclear:

    • What should we do if the hit-test resolves to the outgoing image pseudo element (or container). It seems reasonable to keep the default behaviour where events on a pseudo element are dispatched to its originating DOM element (the documentElement in this case). We can likely add the pseudo type to provide the author with information about which exact pseudo element was hit. The pattern is similar to the |pseudoElement| string on TransitionEvent.

    • Should we do anything special for opacity vs keeping the default hit-testing behaviour.

  • The second part is how to resume the animation. And we could treat the pseudo DOM as the outgoing DOM in that case. The interesting case is that the set of shared elements (specified using page-transition-tag) could change. If you're going from A->B->C, shared elements when B is outgoing could be different from when its incoming. So we'd do something like the following assuming the author changes the set of shared elements and calls transition.start (or transition.replace) midway through a transition:

    • For each shared element in DOM B which is also a shared element in the interrupted transition, take a snapshot of the ::container pseudo element representing it. This way we get an image blending both outgoing and incoming images.
    • For each shared element in DOM B which is not a shared element in the interrupted transition, we'd compute its box size and screen space transform as usual. But also include the transform and clip on this content from being part of the incoming root image in the interrupted transition.
    • Generate a flattened image for the pseudo DOM of the interrupted transition excluding the set of shared elements above. This is the outgoing root image for the new transition.
      One problem with this approach is that we can't assure the same paint order and blending. If the interrupted transition has a paint order of A,B,root but the new transition ends up with A,root with B's contents in the outgoing root. Though this is an issue with transitions in general and authors can ensure B is marked as a shared element to not mess up the paint order.

What do other folks think about this? Also @ianvollick and @flackr fyi since this is related to #101.

@vmpstr
Copy link
Collaborator

vmpstr commented May 31, 2022

  • What should we do if the hit-test resolves to the outgoing image pseudo element (or container). It seems reasonable to keep the default behaviour where events on a pseudo element are dispatched to its originating DOM element (the documentElement in this case). We can likely add the pseudo type to provide the author with information about which exact pseudo element was hit. The pattern is similar to the |pseudoElement| string on TransitionEvent.

I think that doing the hit testing on the originating element is the right call here, since the outgoing image may not have associated DOM elements attached anymore. Also, since it is no longer representing live DOM, it becomes much harder to determine the correct coordinates to hittest, since simply applying inverse transform may not result in the correct coordinates in the live DOM.

  • Should we do anything special for opacity vs keeping the default hit-testing behaviour.

My vote here is to do what regular hit testing does. If there is an opacity: 0 element covering another, what do we hit test? Is there an epsilon such that opacity: epsilon is good enough to hit test?

transition.start (or transition.replace)

This is somewhat bikeshedding, but I think we a new function here, like transition.replace, since we may also implement the ability to have multiple independent transitions, all of which would be started with start. IOW, we need to distinguish if the developer intends to start a parallel transition to the one happening already or to replace an existing one. This is probably too early to design the API, but we also probably want some notion of a handle from transition.start so that when we replace a transition, we know which one is meant to be replaced. (it may be that the transition object itself is that handle, although currently I think we always return the same object)

[...] But also include the transform and clip on this content from being part of the incoming root image in the interrupted transition.

What do you mean by this? I think since the new element is a part of the root capture, we would still have to show it in that root capture. Or do you mean apply additional transform to this new shared element so that it aligns with its representation in the root capture?

As an aside, we probably want to add an options dictionary somewhere so that the developer has the option of not capturing the root for cases like OP's examples. Consider having something like an animated GIF elsewhere on the page that isn't a part of the transition, because there is no way to avoid root capture it would by default crossfade to the next frame when the transition is happening. I think we can avoid that by having animation: unset; opacity: 0 on the outgoing root image and similarly animation: unset; opacity: 1 on the incoming root image, but a dictionary it may be a more ergonomic (and efficient) way to do this.

  • One problem with this approach is that we can't assure the same paint order and blending.

I think if we capture the root including the interrupted transition, then all of this just works, since we do need to capture the mid-transition paint order. All of this is really complicated if we add the ability to have independent transitions, but I think we can design a nice thing where there are replacement and 'static' pseudo trees, some of which do capturing, some of which participate in being captured.

@okikio
Copy link

okikio commented Aug 20, 2024

I'm a member of the solidjs animation team, we are currently working to create an animation and transition primitive for the solidjs community. The lack of interruptibility in the view transition api is a major blocker for us. We'd like to know if there is progress on this, and if there is interest from the WICG in adding interruptibility.

From other conversations occurring in the community, it seems there is a lot of interest in interruptible transitions or sometimes called "retargeting transition" cc @bramus, it would be lovely if the spec, can potentially add support for it.

@khushalsagar
Copy link
Collaborator

@okikio unfortunately there hasn't been progress on this. It's a hard problem since view transition was designed to show 2 DOM states in a tree of pseudo-elements. This requires expanding that to include 3 (or more) DOM states since each time you interrupt and start a new transition, the existing blend of A and B DOM states in the pseudo tree now needs to be combined with a C DOM state.

The feature is being standardizes by the CSSWG. Could you please leave a comment with more details of your use-case here: w3c/csswg-drafts#8687. That'll help us when this issue is taken up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants