WICG / view-transitions

https://drafts.csswg.org/css-view-transitions-1/
Other
806 stars 51 forks source link

Support interupption #157

Open mattgperry opened 2 years ago

mattgperry commented 2 years ago

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 commented 2 years ago

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

vmpstr commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

@daKmoR

https://user-images.githubusercontent.com/7850794/171050907-6a0a4370-9e7c-433c-a554-d6ccf09683b0.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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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

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

vmpstr commented 2 years ago
  • 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 commented 3 weeks ago

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 commented 3 weeks ago

@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: https://github.com/w3c/csswg-drafts/issues/8687. That'll help us when this issue is taken up.