-
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-transitions-2] Exit animations (@exit-style
)
#9500
Comments
I helped @jessicajaniuk flesh out and write this proposal, so obviously strong +1 for this! For context, Jessica works on the Angular team at Google, and thus us intimately familiar with how much of a common pain point this is.
I would be very surprised if it's too late to rename |
I don't see how this would work. The thing about But with this, once the element stops being rendered, it immediately disappears, so no transition is possible. We would either need to delay the actual removal until the transition finishes, which seems to open a can of worms, or somehow detect that the element is going to be removed before it happens and start the transition then. |
This would be excellent. The ability to animate an element as it ultimately leaves the DOM has been janky forever. I could be mistaken, but I think you can do this already with View Transitions, something to the tune of:
Then in CSS if you want control...
This example does that for list items "on the way out" of the DOM.
I think the View Transitions thing avoids the "can of worms" because it still immediately removes the element from the DOM, and the animation takes place on a rasterized visual version of the element, right? Still, I think
|
Easy, we add the (worm-wriggling intensifies) |
Yes, it's more challenging than
We cannot make removal async for a number of reasons. Beyond the obvious, one nonobvious reason is also that the exit style and the removal code may be written by different entities, and it's important for this to work well. E.g. I'm using a library which removes elements with no animation, so I write some styles to animate them. We could however delay it (sync) for the instant it would take to apply exit styles. Then, the transition effect would be entirely visual and the element would be taken out of the DOM immediately. Probably with some safeguard against infinite animations that could keep its ghost around forever. An easy such safeguard could be to simply not support animations, or cut them off when all transitions finish regardless of where they are in their playback. It could even be defined in terms of view transitions under the hood, just that the UA would generate it for you. |
I haven't really been following view transitions, but yeah, using them under the hood sounds more reasonable I guess. |
|
But yes, actually removing an element from the DOM is different. It feels like a pretty significant layering violation to delay DOM operations from CSS. That said, maybe we shouldn't worry too much about that, but I'm inclined to think that if we want to improve the ergonomics of doing a transition that ends with the element being removed from the dom, we might want to think about an API on the DOM side to make that more ergonomic. |
I should probably stop replying late at night, but now that I've reread #9500 (comment) more carefully I see Lea's point about immediate DOM removal being important -- and given that I agree that something built on top of view transitions is probably best. But I could imagine building it on top of view transitions in interesting ways. (Sorry for the multiple comments.) |
I can also see the developer difficulty, but keeping track of a now-gone DOM element and where it used to be until the end of the animation is very difficult/maybe impossible. Even keeping it until the next render and a View Transition can start is hard and in general almost as hard. The alternative of stalling the rendering pipeline to get a screenshot of the pixels for a View Transition is possible, but that comes at a very high cost of forced layout, stalled pipeline, and memory impact. OTOH the developer code to do it async would be something like:
(setting it to Which is certainly not as easy for the developer to deal with, but also seems feasible. |
One question in this context: how useful to developers is it if the DOM removal is async, but still at the start time of the animation (rather than the end). |
First, it's not quite a one liner, it's more like: if (document.startViewTransition) {
await document.startViewTransition(() => { el.remove() }).finished;
}
else {
el.classList.add("removing");
await new Promise(r => el.addEventListener("transitionend", r, {once: true}));
el.remove();
} But that could be abstracted away. More importantly it's about coupling.
I think my comment above may reply to this too? If not, could you expand on what you mean a bit more, ideally with a code example? |
I agree a sugar sweet way to specify an animation to play for DOM mutations would be useful. I've been thoroughly enjoying view transitions for this, and am very familiar with the naming dances that need to happen for it all to work. I'm going to try and share where my mind is dwelling on when comparing exit-style and view transitions, and where each can shine. For dom mutations and providing visual feedback to users, here's some of the transitions that are common:
View Transitions has ways to handle all these scenarios, and i think with exit-style i'm most curious about configuration for when it shouldn't run. Like, if an element is moving position in a list, due to sort/filter/removal/etc. Would To me, seems like exit-style is sugar for "certain" node removal scenarios and not all of them. And to prevent it from running I'd need to intervene with JS classes to inform selectors? It's almost like I'd like a way to tell exit-style which DOM mutations to observe and intercept for the exit animation. Meanwhile, view transitions have this logic already figured out. Authors can use CSS, no JS required beyond the call maybe there's a handoff point between exit-style and view transitions? exit-style for when you know the elements are only going to be removed and you want to animate it; view transitions when they might morph or change position in the dom? there's also tldr; |
I think the biggest thing here to me is that this addresses a developer need. As @LeaVerou pointed out, writing the code to handle element removal, even with a view transition, is not just a single line, and that code ends up having to be used everywhere an element is removed. That includes the overhead of adding an event listener for Yes, frameworks and libraries can obfuscate this so users don't have to write it, but that means we're shipping that removal javascript along in everyone's bundle sizes, and we're still dealing with overhead of the event listener. We're constantly looking for ways to shave off as much as we can from bundle sizes as we can. So if we can eliminate the need to have code to handle the removal, that would be ideal for us in the framework space. And of course, the symmetry of |
Just wanted to clarify that the View Transitions approach does not require any new event listeners. The element can be removed in the callback passed to However, there will be a difference for the web developer when using |
I think the requirement stated in #9500 (comment) -- to have the ability to do DOM node removal animations, starting from standard DOM node removal APIs that continue to have their usual effects on the DOM (synchronous removal), without unacceptable pre-removal performance costs -- just changes too many fundamental assumptions built into the Web. CSS has always worked on top of the DOM; we don't have the ability to render elements that aren't in the DOM, and changing that in either specifications or browser engines (and making sure it's reliable across all element types, all features that it needs to interact with, etc.) would be a very large amount of work. (Without thinking about it very hard I'd guess that we'd be talking about engineer years of work for each browser engine, and I'd be hard-pressed to answer whether we're talking about a single-digit or a double-digit number of engineer-years.) There are also a bunch of very deep issues here that interact deeply with all of correctness, interoperability, and performance. Let me try to give a simple example. Suppose you have some sort of widget containing a list of items, each of which has .item { background: white; color: black; }
.item:nth-child(odd) { background: silver; } Now let's suppose a script comes along and removes the 5th, 6th, and 7th items in the container, in that order: container.children[4].remove();
container.children[4].remove();
container.children[4].remove(); During their exit animations, is the background of the items Our normal practices for defining such behavior would be that the background of all three items would be Could we avoid this without adding additional APIs (which would then not satisfy the original requirement that the developer can use the normal removal APIs)? Well, then, suppose we didn't flush style at all during the removals. That would expose when the previous style flush was -- something that is entirely allowed to vary between implementations. For example, if there was an earlier element inserted into the list prior to the removals, and implementations differed (which they can) on whether a style update happened since that insertion, then specifying that we not flush style would cause the background color to vary between implementations. Could we avoid that without adding other APIs? I don't think we can without doing something even more unreasonable (like flushing style before executing any script that comes after anything that might have been run asynchronously). |
I removed the Agenda+ tag for now because the debate here seems ongoing. |
Framer Motion has an exit animation API: https://www.framer.com/motion/animate-presence/##exit-animations |
Problem statement
Elements are often removed from the DOM, either via JS (
element.remove()
etc), or by applyingdisplay: none
to them. Some common examples: deleting items from lists, toasts, dismissing dialogs and popovers, etc.Current solutions:
animationend
ortransitionend
event, filtering by the right transition/animation, and then removing the elementdisplay: none
(using a transition for the latter wouldn't work)All of these solutions are too heavyweight, and most require additional JS and possibly the allocation of an event listener which is a perf overhead.
Proposed solution
In #8174 the group added
@starting-style
, to solve the reverse of this problem and make it easier to specify entrance transitions. Something analogous to that, such as@exit-style
(name TBB) would complement it nicely.Other potential names:
@ending-style
,@remove-style
,@exiting-style
,@removing-style
If renaming
@starting-style
would not cause compat issues (it just shipped in Blink and it has not shipped anywhere else), we could name them as a pair:@start-style
/@end-style
@entrance-style
/@exit-style
Additionally, if there is a way to specify both entrance and exit styles, this simplifies a lot of other interactions that are technically neither, e.g. moving elements around.
Other solutions
@starting-style
and would likely suffer from the same issues that led to it becoming an @-ruleThe text was updated successfully, but these errors were encountered: