-
Notifications
You must be signed in to change notification settings - Fork 676
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] Ignore offscreen elements from participating in transitions #8282
Comments
There are memory and computational benefits to this feature, but I originally proposed this as more of a visual / developer experience feature. If two pages have a common heading, you may want that to be static in a transition, so you give it a However, if one of the pages is scrolled 8000px, the transition between the two will be bad, as the header will fly in from 8000px away. With this feature, developers will be able to create a special incoming/outgoing animation for the header in this case (using the |
Since it significantly changes the animation, I'm not sure we can, or should, do this automatically. Since we won't be doing it in the first release, and it will only happen on constrained devices, I think it'll lead to things appearing broken when this 'auto' behaviour kicks in. I'd rather say that transitions can be skipped if the device is constrained, since that's a more reliable fallback that developers will already be catering for. That said, I support the default value being |
It feels like this is one of these options that you should give to startViewTransition to optimize things because you know that you've marked way too much with the view-transition-name. Although I do see the appeal of the new css property since you can use that in MPA cases too, to me it doesn't really feel like the right abstraction |
I like it as a CSS property since the instruction can be per transition item. Eg, if you're reordering a list, you still want things to transition from outside the viewport. Whereas you might not want that behaviour for a header. Both might happen in the same transition. |
A third value which would be useful for a case like https://deploy-preview-32--infrequently.netlify.app/, the page has a massive element which will be captured in entirety while the animation doesn't need that. Syntax ideas, |
Isn't the clipping just supposed to happen automatically? https://drafts.csswg.org/css-view-transitions-1/#compute-the-interest-rectangle-algorithm |
That automatic clipping is very conservative, only if we must because of constraints like max texture size. Technically an implementation doesn't need to be constrained by it (you could create a tiled image), but the option gives UA flexibility. This property would be an explicit hint from the developer that only the onscreen content (or a skirt around it) of this DOM element will be animated during the transition. The UA can use this knowledge to aggressively optimize for memory by painting and snapshotting a subset of the DOM element. |
On a hackathon I coached at this was a dealbreaker for some, as they saw performance get tanked on non-highend devices. They were animating 1 element out of a list of 50, of which only 7 of them where visible in the viewport. Having a way to easily exclude these offscreen elements would surely be beneficial here. |
Are they happy with some items being :only-child, and animating as such, even though they existed in both states. I wonder if we need some other feature in that case, where the group still animates from old to new, but there's only a new in the pair, or the group is dropped if the final position of the group is still out of view. |
@jakearchibald we have a couple of other issues with the characteristics you mentioned:
We haven't dug into the exact API shape but given how related these 3 features are, I feel like we should tackle them together. Like a new CSS property to specify one of these modes? |
I like the direction this conversation was going. The main thing that justifies a new attribute for this is the idea that this could be a UX choice rather than a mere optimization (e.g. preventing a header from jumping). I think the semantics here should be similar to content-visibility and intersection observers, however because this is observable and not just an optimization, the definitions need to be exact and customizable via a margin (same as Another thing I think we should do is make this new attribute inherited, this way the author can decide that a container makes its entire set of descendants behave in a certain way (and this can be overridden further down the tree). Perhaps this can be
|
+1 to making the property inherited. I'm assuming the initial value will be For the |
A few additional comments:
|
Hmmm, I think there will be cases where disregarding the ink overflow would end up with glitchy behaviour. Like a widget whose shadow is in the viewport. Should we really ignore it? This has come up for IO in the past too: #8649. @szager-chromium on that. I was curious how content-visibility handles this. Looks like it relies on With VT we intentionally decided not to have paint containment, so that won't work for us.
Oh I just read what you said carefully and I tend to agree. We should have separate properties which decide whether the element participates in the transition. If it is participating, then a separate property has knobs for deciding how its captured. So omitting In that regard, maybe the use-case in #9354 should eventually be handled by |
Absolutely agree. |
Also agree. I think ink-overflow intersection with the viewport counts as visible in terms of a view transition, else you risk 'seeing double'. |
Yeah, content-visibility uses paint containment for this reason, so that we don't need to render the subtree to figure out the extent of the overflow, all of the needed information is on the box itself (border box + overflow-clip-margin) |
One of the open questions for this feature is defining when an element is considered visible. There's 2 aspects to it:
|
I'm pretty certain we don't need to care about occlusion. Just viewport intersection. |
In either case I think we should be consistent with IntersectionObserver. For viewport intersection IntersectionObserver doesn't take the ink overflow into account, so this shouldn't either. If IO exposed ink overflow in some way, eg for occlusion, we could consider doing the same. Note that with a big enough root margin, being accurate about the ink overflow becomes less important. |
hmm, I think the developer intent of this feature trumps consistency with intersection observer here. We can avoid using the word 'intersection' if that's where the problem is. I'll ask Shopify folks though. |
How is the developer intent here different from the developer intent in |
I think the underlying question here is how we would expect elements to behave that are right outside the edge of the viewport. So let's say you have an element that has a |
It is. I'm sad that the OP has been ignored given the number of developers asking for this. I guess we can continue to solve it with JavaScript. |
Given the following: @keyframes apply-view-transition-name-when-in-range {
from { view-transition-name: var(--view-transition-name); }
to { view-transition-name: var(--view-transition-name); }
}
header {
--view-transition-name: header;
animation-name: apply-view-transition-name-when-in-range;
animation-timeline: view();
} Can you be more specific about what behavior the OP requests that's different? Is it just the aesthetic aspect of using an animation? If It looked something like this (with the exact same behavior) would it make any difference? header {
@in-view {
view-transition-name: header;
}
} Note that using view timelines for this has the added value of being able to control a different behavior when slightly off screen and far away from screen: @keyframes apply-view-transition-name{
from {
view-transition-name: var(--view-transition-name);
view-transition-class: near;
}
10% { view-transition-class: inside; }
90% { view-transition-class: inside; }
to {
view-transition-class: near;
view-transition-name: var(--view-transition-name);
}
}
header {
--view-transition-name: header;
animation-name: apply-view-transition-name;
view-timeline-inset: 50vmax;
animation-timeline: view();
} I think the problem with this issue is that it defines a scroll-driven behavior for view-transitions and we are trying to do this as a shortcut, while we already have a scroll-driven primitive that can do this, and the main issue with it is that it's tied to "animations"?. |
Pretty much, yeah. I'm guessing it doesn't account for ink overflow either? Are Chrome's dev rels happy with documenting this feature as a hack with scroll timelines? |
Correct, you have to set
We'll discuss this internally to see if people are content with it. I have it on my list to document it (and another hack to override the "don't capture contents if it's far from the screen" behavior we resolved on here) as ways to change view-transition behavior based on scroll position of participating elements. |
@jakearchibald apologies if it came across that this feature request is being ignored. OP's use-case of customizing the UX when content is offscreen was explicitly discussed at the last CSS f2f, the detailed notes are here. One of the questions raised in that discussion was if we should be providing the offscreen state of the old/new DOM elements as a pseudo-class. Something like |
Created #10581 and #10582 to account for the two issues with using
I believe that these issues belong in scroll-driven animations and not in view transitions, as they define scroll-related behavior. For example, the same patterns can be used to change the behavior of a regular CSS transition if it's away from the viewport. |
…de snapshot containing block See w3c#8282 (comment)
Summary of internal discussion and #10587:
I currently tend to like (4) as it allows optimizing the important things without totally breaking the user-experience or creating a new mental load on authors. It also allows different tiers of optimizations based on device/memory/whatever as the results are not web-observable and the tradeoff between performance and visual degradation can be played with. |
|
True, https://github.com/WebKit/WebKit/blob/main/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h#L654 seems to do something like cloning a layer but it's been a while since I touched CA so will leave this to @nt1m and @mattwoodrow |
|
The CSS Working Group just discussed
The full IRC log of that discussion<fantasai> noamr: We were discussing optimization to render new live element in place of old element snapshot for when it's offscreen<fantasai> noamr: but there were some concerns from WebKit wrt having two live images <noamr> https://github.com//issues/8282#issuecomment-2245542731 <fantasai> noamr: Discussed using transparent image, but that would create a cross-fade that's not necessarily what's wanted <fantasai> s/cross-fade/fade-in/ <fantasai> noamr: could make it implementation defined, but we lose compatibility here <fantasai> noamr: another option is to figure out the implementation <fantasai> noamr: Option I like the best is keep spec as-is, but say that if an element is off-screen, the UA can capture it at low resolution <fantasai> noamr: so if you have elements cross-fading from far away, might appear blurry <fantasai> noamr: also, using view-timeline, authors that want to override can do so <fantasai> noamr: by giving a different view transition class and use a different animation <fantasai> noamr: other option that was raised was to make one capture at the beginning of the new element, and use that as the old element <khush> q+ <Rossen2> ack khush <fantasai> khush: I like idea of having UA decide the rasterization range <fantasai> khush: and browser can make trade-offs based on, is this a low-end device, etc. <TabAtkins> fantasai: I think the idea of doing [missed] and a capture of the new state is interesting. not sure what makes the msot sense, tryign to get Tim to sign on <fantasai> s/[missed]/low-res captures <fantasai> noamr: Concern with using new image as old image is the semantic difference <fantasai> noamr: Want to also not create junk <flackr> s/junk/jank <fantasai> fantasai: You mentioned could capture low-res or not at all <TabAtkins> fantasai: If you have something that isnt' captyured; you're saying an opt is a low-res or not capturing at all, if you realize partway thru that you need the old image and didn't capture it, what do you do <vmpstr> q+ <noamr> you'd see whatever it is you've captured <fantasai> flackr: you'd either capture or not up front, and then have transparency if didn't capture <Rossen2> ack vmpstr <fantasai> vmpstr: Another option is we don't capture anything in the old, but we also don't do the cross-fade by default <fantasai> vmpstr: only display the new pseudo-element <noamr> (rejoining call) <Rossen2> ack fantasai <TabAtkins> fantasai: for "only dispaly the new element", I think in that case you'd need to somehow allow the author to select those cases <TabAtkins> fantasai: so if *they're* doing a cross-fade they'll realize they can't do it <khush> q+ <TabAtkins> fantasai: if their animation depends on the old image existing, so they can adapt to it <TabAtkins> fantasai: So on the "this is an optimization" perspective, doing a low-res capture makes more sense to me <TabAtkins> fantasai: In cases where the UA didn't capture the old el and realized they need it, I think transparent might be problematic <TabAtkins> fantasai: So instead in that case, if you optimzie too hard, just take a cpature of the new dom and use that instead <fantasai> vmpstr: Low-res capture cross-fading to that might have artifacts that are unappealing <TabAtkins> fantasai: to be clear, not saying... you'd only use the new dom in place of the old if you'd *failed* to catpure anything at all. if you have a lowres capture you shoudl use that <fantasai> khush: On aspect of authors should be able to detect this" <fantasai> khush: for not capturing the old <fantasai> khush: would it be enough to give the author a pseudo-class? <fantasai> khush: UA would not do a cross-fade, and the author can then do a customization <vmpstr> +1 <Rossen2> ack khush <fantasai> khush: [something] is not trivial to do <fantasai> khush: implementation concerns were raised on WK side <noamr> q+ <fantasai> noamr: aside from being detectable, also want good default <fantasai> noamr: I like the idea of having one capture of new element in the rare case where we didn't capture anything <Rossen2> ack noamr <fantasai> ntim: I would prefer low-res capture of the old state <fantasai> ntim: I think that's the easiest thing to implement <noamr> q+ <fantasai> ntim: for authors [garbled] <fantasai> ntim: show nothing solution is more disruptive to the user <fantasai> noamr: I would suggest something <Rossen2> ack noamr <fantasai> noamr: we say that you can display low-resolution image of the element <fantasai> noamr: that way we discourage showing totally transparent images <fantasai> noamr: we don't have to resolve on what happens when we didn't capture anything right now <fantasai> noamr: we can capture a low-res image, and we discourage capturing fully transparent <fantasai> noamr: if there's an issue, we can resolve on it then; might not be necessary <khush> q+ <fantasai> ntim: low-res of old capture seems reasonable to me <Rossen2> ack khush <fantasai> khush: do we want authors to be able to detect this? <fantasai> khush: low-res cross-fade could be looking bad <fantasai> vmpstr: use case would be e.g. grid-reorder where image is coming in from far off the screen <fantasai> khush: right now you see the same content, but now you'd see cross-fade between low-res and high-res <fantasai> vmpstr: can detect if something came from offscreen using view-timeline <fantasai> vmpstr: e.g. in grid-reordering case <fantasai> vmpstr: maybe we can resolve on the degradation in a different resolution <khush> i'm ok with that <vmpstr> yep., that's fine <fantasai> ntim: agree with noamr, detecting degradation should be a separate issue so we can get the syntax right <fantasai> ntim: but right now you can detect animations from off-screen <fantasai> s/vmpstr/noamr/ <fantasai> s/vmpstr/noamr/ <fantasai> s/vmpstr/noamr/ <flackr> q+ <fantasai> noamr: proposed resolution, if old element is off-screen, UA can capture in low resolutoin <fantasai> flackr: should we change the UA animation at least to use the new image, since we know that's better? <fantasai> noamr: That defeats the purpose <fantasai> flackr: developer animation would still work using old image <fantasai> flackr: but the UA animation would not need cross-fade.. <Rossen2> ack flackr <fantasai> flackr: so this doesn't fix not needing to capture issue? <fantasai> noamr: it fixes a perf issue, a lot less memory etc. <fantasai> RESOLVED: UA is allowed to capture old elements in low resolution if they are off-screen |
…t-of-viewport. Closes w3c#8282
Currently if an element has a non-none computed value for
view-transition-name
, it participates in the transition irrespective of whether it is in the visible viewport. This means the element will be rendered, which has significant computational and memory overhead, even if it is never seen by the user. If the developer wants to avoid this overhead, they have to keep track of the visibility of each element and only addview-transition-name
to the onscreen ones.The proposal to make this case easier is as follows:
See prior discussion on this here.
The text was updated successfully, but these errors were encountered: