w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.42k stars 652 forks source link

[css-view-transitions-2] Nested transitions: what happens if there is a mismatch? #10631

Open noamr opened 1 month ago

noamr commented 1 month ago

Follow up on #10334 (nested transitions)

It's clear what happens when both the old and new state have the same view-transition-group. We need to specify, however what happens when there is a mismatch. Note that this mismatch can happen by use of keywords like nearest.

Example:

A box sliding between two clipping containers BoxToBox

Several solutions to a mismatch:

  1. new capture wins (like view-transition-class)
  2. Old capture wins
  3. Fallback to nearest common ancestor
  4. Split to two groups, one with the old pseudo and one with the new pseudo. The two groups have a default transform animation that represents moving the element from the old position to the new.

Trade-offs:

I tend to support option (4) because of the following:

noamr commented 1 month ago

//cc @khushalsagar @vmpstr @flackr @fantasai @nt1m

Psychpsyo commented 1 month ago

I just ran into this while working on a site and I'd like to propose a modified version of option 3: Creating a pseudo common ancestor that has a clipping shape that includes both containers and the space between them, somewhat like this: image This would avoid the moving child popping in or out on the opposite end of the containers.

One thing I'm not sure on is the exact shape of the ancestor since the clipping boundaries of the two containers might not be square. Ideally, the pseudo-ancestor would have its exact shape determined by some sort fill between the two containers that leaves their far edges as they are and only includes the space between that an element would be travelling through. I think the most common problem would be rounded corners and maybe more fancy, nine-sliced corners/edges.

Also, here's my real-world example of this causing issues:

CrossUniverseClipping.webm

noamr commented 1 month ago

One thing I'm not sure on is the exact shape of the ancestor since the clipping boundaries of the two containers might not be square. Ideally, the pseudo-ancestor would have its exact shape determined by some sort fill between the two containers that leaves their far edges as they are and only includes the space between that an element would be travelling through. I think the most common problem would be rounded corners and maybe more fancy, nine-sliced corners/edges.

Also, here's my real-world example of this causing issues:

CrossUniverseClipping.webm

Thanks for this! I am not sure how we can translate this into a solution though. Note that clipping is just one example, there is also opacity blending and filters. My current thinking is that "last capture wins" is the simplest and more consistent with existing stuff, especially since "nearest common ancestor" is trivial to achieve by using that ancestor in view-transition-group.

nt1m commented 1 month ago

My current thinking is that "last capture wins" is the simplest and more consistent with existing stuff, especially since "nearest common ancestor" is trivial to achieve by using that ancestor in view-transition-group.

Agreed

khushalsagar commented 1 month ago

My current thinking is that "last capture wins" is the simplest and more consistent with existing stuff

By "last capture wins" I think you mean we use the ancestor based on the new DOM? How do we deal with a case where the new element uses an ancestor which is not in the old element's ancestor chain? We have to figure out what the old transform should be in this case.

I'm not opposed to this idea but stuff like the above seems easier to reason about with "least common ancestor". Since you can always map the cached old transform to any ancestor. The fallback is more like the default flat mode.

noamr commented 1 month ago

My current thinking is that "last capture wins" is the simplest and more consistent with existing stuff

By "last capture wins" I think you mean we use the ancestor based on the new DOM? How do we deal with a case where the new element uses an ancestor which is not in the old element's ancestor chain? We have to figure out what the old transform should be in this case.

The old element will save the final transform, and we'll project to be relative to the final parent.

I'm not opposed to this idea but stuff like the above seems easier to reason about with "least common ancestor". Since you can always map the cached old transform to any ancestor. The fallback is more like the default flat mode.

Is there more stuff like the above?

khushalsagar commented 1 month ago

The old element will save the final transform, and we'll project to be relative to the final parent.

Ah, so figure out its transform relative to the root and map it into the chosen ancestor's space. Makes sense.

Is there more stuff like the above?

Everything else which is hierarchical (opacity, clip). If these effects are on the LCA then using that will look more correct. Though if they are on any node between the LCA and the new named element and we choose LCA then new would look wrong.

So I'm fine with both. As you said, if this doesn't work authors can always fix it by using the LCA as the parent explicitly.

noamr commented 1 month ago

The old element will save the final transform, and we'll project to be relative to the final parent.

Ah, so figure out its transform relative to the root and map it into the chosen ancestor's space. Makes sense.

Is there more stuff like the above?

Everything else which is hierarchical (opacity, clip). If these effects are on the LCA then using that will look more correct. Though if they are on any node between the LCA and the new named element and we choose LCA then new would look wrong.

Opacity & clip are different from transform in that sense. They would be applied on the parent element, which would clip/mask the whole layer underneath. In some cases it would look different from the original state, but there is no way around it (except for putting view-transition-group: nearest on everything.

So I'm fine with both. As you said, if this doesn't work authors can always fix it by using the LCA as the parent explicitly.

Cool