-
Notifications
You must be signed in to change notification settings - Fork 711
[css-animations][css-transitions] Isolate animation side-effects? #6398
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
Comments
I've certainly come across a few cases where having an API to get the un-animated style would be very useful so introducing this concept at a spec level might be a useful step in that direction.
I'm not sure about this. It's been a while since I've touched this code but from memory the following clause from the CSS Transitions spec is quite important:
Without that, animations end up triggering transitions.
I think Firefox should be correct regarding the before/after change styles part including inheritance--the only area where I'm aware it's wrong is with regards to context-sensitive properties like |
Thanks for having a look @birtles.
Note that I'm redefining the before-change style to be the before-change base style, and the same for the after-change style. The base style does not include any animation/transition effects of any kind, hence the current time of existing animations is not relevant for the purposes of triggering transitions.
I don't doubt that the fundamentals are correct in Firefox. For example I see that all levels are correctly transitioning at the same time in this example. (Not the case in Chrome). But more interesting are the parts that would be affected by this change, e.g. "complex" inherited interaction between ongoing animations and transitions, as in this example, where no browser gets it right. With this proposal, there would be no transition on Not sure what the full implications of this change would be, and if any losses would be worth it if we gain container queries. |
Oh, ok, that makes sense.
I think Firefox's CSS handling here is correct but there is a bug due to running background-color animations on the compositor. If you disable the (Quite a while ago I wrote a few tests covering some of these cases but never finished reworking the CSS transitions WPT tests enough that I could add them there: https://bug1192592.bmoattachments.org/attachment.cgi?id=8843824) |
Ah, I should have chosen the property to animate more carefully. Yes, that looks correct indeed, nice. |
The starting of transitions will be sort of how it works today based on step 4 of starting transitions in css-transitions-1. I.e. if the end value (which is equivalent to your suggested before-change style) is the same, no new transition is started. One notable difference with the proposal is that I think setting a property equal to its current animation value normally would have no effect but with your proposed change would start a transition. We could probably carve out an exception for this given that we need to compute the current value to start from anyways. To make sure I understand the current effect value, if you interrupt a transition like the following: e.style.transition = 'all 1s linear';
e.style.left = '100px';
setTimeout(() => {
// getComputedStyle(e).left == '50px'
e.style.left = '200px';
}, 500); We would still compute the current effect value from the replaced transition (e.g. left: 50px)?
Does this mean the entire descendant style which matches the container query would be "animated style" rather than base style? E.g. the following wouldn't start an animation on #b? <style>
@keyframes grow {
0% { width: 100px;
100% { width: 1000px;
}
#a {
animation: grow 5s;
contain: size;
}
@container (min-width: 500px){
#b {
animation: grow 5s;
}
}
<div id="a">
<div id="b">
</div>
</div> |
Not sure if by "current animation value" you mean only effects from CSS/Web-Animations, only effects from transitions, or effects from all of them. For transitions happening on the same element, it looks like the transition would be canceled in both the current and proposed behavior. For CSS/Web animations happening on the same element, I think you're right. And yes I suppose we could check the current value for any animations and special-case on that, if necessary. For transitions/css/web-animations effects inherited from ancestors, I don't think we can do tricks like that. It would just start/update transitions as if no transitions/animations take place in the document.
Yes.
That's right. Animations/transitions are only interpreted on the before/after (base) styles, so if I was originally going to also say: However, it probably makes more sense to specify that the computed values of |
Actually, I think it makes sense to do that for all properties which are "not animatable". Otherwise animations can affect |
It's worth considering the complexity in knowing the style without animations means that we would need to perform two layouts. One layout without animations to know which container queries should apply to the base style, and the second for the container queries which end up applying to the animated style. |
When there's no "style change event" (i.e. only animations progressing), we also don't need recalculate the base style/layout, since it didn't change. But yes, otherwise that's true. However, the currently specified definition of "before-change style" already requires us to do a separate style pass (which we currently ignore in Blink) for each style change event. Then container queries comes along and makes it worse by mixing style and layout, so now we also must potentially also do layout twice, using the old style information but with animation effects updated to current time. |
Overall this makes sense. I think this is pretty consistent with how you cannot affect animations on descendants using variables due to the animation-tainted rules, though we should revisit those since I think instead of tainting the variables they should use the after-style values. I remain somewhat concerned about the potential performance cost of multiple layouts, and how easy it will be to understand when some container query animations do not run because the container query was triggered as a result of animation. The latter is already somewhat of an issue with animation-tainted variables and should be addressable with good tooling.
Ah yes, it seems the starting of transitions is already attempting to do something like the proposal here where we look at the non-animated values. I checked and it looks like the reverse-adjusting duration behavior should also work correctly.
Normally, transitions would not see style changes on elements with active animations because the animation has a higher composite order than the underlying style. I think we'll just need to have a special case here to not start transitions on an element that is currently animating.
I agree, the getComputedStyle of "not animateable" properties should be their after-change style. |
@andruud and I were just chatting about this proposal, and I wanted to write a few notes about one issue that we discussed. It seems like one of the undesirable effects of this proposal is what happens when there are animations or transitions triggered by a container query change. To make this somewhat concrete, suppose we have a component that has container queries that give it substantially different styles at (width < 300px) and (width >= 300px). Then let's consider the 4 cases that result from crossing the following pairs of scenarios (which are intentionally using simple cases of transitions and animations rather than what might perhaps be more realistic ones):
@keyframes shrink {
from { width: 500px }
to { width: 200px }
}
#heading { font-size: 12px; transition: font-size linear 3s; }
@container component (inline-size >= 300px) {
#heading { font-size: 18px; }
}
@container component (inline-size < 300px) {
#logo { animation: roll-in 3s linear; }
} With the current proposal above, I think the results are as follows (and I think these results are undesirable):
I think the expectation is probably that container-query-dependent animations and transitions inside the container should start when the container query starts to apply to layout and to non-animation styles. It's possible this proposal could be modified to produce that result; part of such a modification would include having a style change event inside of the container when the container query evaluation changes, though other changes would be needed as well. |
I've implemented a prototype of container-size-change-is-style-change-event, and so far it appears to be a win-win: it gives much more desirable web-facing behavior, and it's also much easier and less expensive to implement (avoids having to know "two layouts" as @flackr was worried about). Taking this into account, how about this revised proposal:
At first I was worried that this would be a problem for container units, since they act as mini-container queries that dynamically respond to any change in container size. With this proposal, if you define a transition on something that uses container units, and also animate the size of that container, then it would trigger a transition on each frame. However, I think this is expected and non-fatal:
So to me the container units situation looks acceptable. |
Overall this sounds great! I'm glad that we can behavior that makes sense to developers How are container units defined? I would have thought that they would be similar to percentages, viewport units or calc where the computed value is a calc that does not itself change when the input variables change. Unfortunately, I can see that this is not the case at least for vw from the demo I put together to test this. It seems there is plenty of precedent for it starting transitions. |
I think it sounds good to me as well, except that:
probably needs to be specified differently, since I think in the revised proposal it does include some animation effects (those resulting from container queries that change in animations, or maybe all container queries), and something will need to define exactly which animation effects are included and which aren't. |
The only thing that "survives" to used-value time, is %. Everything else, including viewport units and container units are resolved to px at computed-value time, or sooner. But yes, specifying that container units are resolved used-value time would probably be the way to "fix" the problem, if we were to define this as a problem.
Yeah, agree. Should be more specific than "animation effects". Perhaps:
And then "animation effects" can still sneak its way into the base, since an animating container can affect the author/user origin of descendants. |
This CL implements a new approach [1][2] for dealing with animated container queries. The approach can be summarized as: 1) Delaying application of the CSSAnimationUpdate until after layout, and 2) Remembering the "original" old-styles for relevant elements. Both of these things were also required for the previous solution (currently behind the runtime flag CSSIsolatedAnimationUpdates), so they are already mostly in place. Apart from these two things, however, the new solution actually has more in common with how things already work on stable than with CSSIsolatedAnimationUpdates, so this this CL effectively reverts that code. The new approach in this CL works like this: - During StyleResolver::ResolveStyle, elements that have a non- empty animation update gets added to a set on DocumentAnimations. - A stack-allocated object CSSAnimationUpdateScope is placed on the sites where we do style and layout, and it ensures that any collected pending updates are applied before we exit that scope. - Old-styles are temporarily added for each element we resolve style for, with a lifetime that's also tied to CSSAnimationUpdateScope. Regarding InApplyAnimationUpdateScope: it turns out that applying an animation update can cause calls to Element:: SetNeedsAnimationStyleRecalc. On main, these calls are ignored, because we always apply the animation update during style recalc, and we early-out from SetNeedsAnimationStyleRecalc if we're currently in style recalc. With the changes in this CL, we apply the same update *after* style recalc, so we need another way to early-out from SetNeedsAnimationStyleRecalc, and this is what the InApplyAnimationUpdateScope is for. Note: It probably makes sense to store the elements-with-pending- updates set and the old-styles on the CSSAnimationUpdateScope itself, but I'll try that in a separate CL. Note: We still store old-styles for way too many elements. An improvement for that is coming soon. [1] https://docs.google.com/document/d/1Ef9V-Ra_6I3R9DY0o10fZ7HTriPm9oHYw1xUN0bRcHw [2] w3c/csswg-drafts#6398 (comment) Change-Id: I735b9113c12ef57696b82470b2256c639884ef48 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3129367 Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org> Reviewed-by: Rune Lillesveen <futhark@chromium.org> Reviewed-by: Kevin Ellis <kevers@chromium.org> Reviewed-by: Xianzhu Wang <wangxianzhu@chromium.org> Cr-Commit-Position: refs/heads/main@{#919696}
Currently we set a pending update for any element that has an active animation running (see CSSAnimationUpdate::IsEmpty), even if there are no actual *updates* (as in CSSAnimationUpdate::HasUpdates) to apply. This is because of the unfortunate PreviousActive- InterpolationsForAnimations map that we need to maintain. With CSSDelayedAnimationUpdates enabled, setting pending updates is more expensive than before, since elements with updates enter a HashSet (CSSAnimationUpdateScope::elements_with_pending_updates_) that is iterated though later. On some benchmarks, this shows up as smoothness/frame-time regressions since a large number of elements are added to the set on every frame. Ideally we would only set pending updates when there actually *are* updates, but this is currently[*] not possible because we have to maintain the aforementioned PreviousActiveInterpolationsForAnimations- map. However, we can still take one step in the right direction. Since we only care about the *keys* in the previous-map, we can avoid updating it if we know that the keys didn't change. This means that for "stable" animations that don't constantly change which properties are affected, we can avoid setting the pending update on each frame. [*] We may be able to in the future, depending on which details we land on here: w3c/csswg-drafts#6398 Bug: 1262215, 1180159 Change-Id: I2a0789b36ee41b123a97b4804311e45a243ae8b8 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3241146 Reviewed-by: Kevin Ellis <kevers@chromium.org> Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org> Cr-Commit-Position: refs/heads/main@{#935512}
In order to advance spec changes for css-transitions that are necessary for container queries [1], we need to know how often a transition is blocked from starting because of interference from an animation on the same property. [1] w3c/csswg-drafts#6398 Bug: 1180159 Change-Id: I1b59231499e63760a272d2add7cf14d5a1f4c1d1 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3250820 Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org> Reviewed-by: David Baron <dbaron@chromium.org> Cr-Commit-Position: refs/heads/main@{#936072}
This reverts commit 83eb3e4. Reason for revert: Performance and memory regressions, see bugs. Fixed: 1264284, 1264285 Original change's description: > Reduce number of pending CSSAnimationUpdates > > Currently we set a pending update for any element that has an > active animation running (see CSSAnimationUpdate::IsEmpty), even > if there are no actual *updates* (as in CSSAnimationUpdate::HasUpdates) > to apply. This is because of the unfortunate PreviousActive- > InterpolationsForAnimations map that we need to maintain. > > With CSSDelayedAnimationUpdates enabled, setting pending updates is > more expensive than before, since elements with updates enter > a HashSet (CSSAnimationUpdateScope::elements_with_pending_updates_) > that is iterated though later. On some benchmarks, this shows up > as smoothness/frame-time regressions since a large number of > elements are added to the set on every frame. > > Ideally we would only set pending updates when there actually *are* > updates, but this is currently[*] not possible because we have to > maintain the aforementioned PreviousActiveInterpolationsForAnimations- > map. > > However, we can still take one step in the right direction. Since we > only care about the *keys* in the previous-map, we can avoid updating > it if we know that the keys didn't change. This means that for "stable" > animations that don't constantly change which properties are affected, > we can avoid setting the pending update on each frame. > > [*] We may be able to in the future, depending on which details we > land on here: w3c/csswg-drafts#6398 > > Bug: 1262215, 1180159 > Change-Id: I2a0789b36ee41b123a97b4804311e45a243ae8b8 > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3241146 > Reviewed-by: Kevin Ellis <kevers@chromium.org> > Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org> > Cr-Commit-Position: refs/heads/main@{#935512} Bug: 1262215, 1180159 Change-Id: Ifd709a26158f86d23d335bffd3f9f32315c69081 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3251067 Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> Reviewed-by: Kevin Ellis <kevers@chromium.org> Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org> Cr-Commit-Position: refs/heads/main@{#936377}
- Introduce "base style", which is the computed style as it would be without animations/transitions. - Define before/after-change style in terms of that base style (at two different points in time). - There should be no need for the "completed transition" set anymore, since the before/after-change styles on a child are blind to any effects from transitions/animations happening on the parent. - Maintain the behavior that transitions can not start for properties affected by animations. Removing this behavior was considered, but was deemed impossible from use-counter data showing that ~5% of sites may rely on this behavior. This change stems from Container Queries, which blurred the line between the style and layout steps of the frame. The spec currently defines the before-change style as the style of the previous style change event, but with any animation effects updated to current time. This is difficult, because it persisting the before-change world in some form that is responsive to what the (newly updated) animations actually do. (For example, we'd need to re-resolve var(), font-relative units, be able to handle revert/revert-layer, etc.) With Container Queries, this problem becomes substantially worse, because now we'd also need to maintain the "before-change layout tree", which in practice is gone at the time we need it. w3c#6398
- Introduce "base style", which is the computed style as it would be without animations/transitions. - Define before/after-change style in terms of that base style (at two different points in time). - There should be no need for the "completed transition" set anymore, since the before/after-change styles on a child are blind to any effects from transitions/animations happening on the parent. - Maintain the behavior that transitions can not start for properties affected by animations. Removing this behavior was considered, but was deemed impossible from use-counter data showing that ~5% of sites may rely on this behavior. This change stems from Container Queries, which blurred the line between the style and layout steps of the frame. The spec currently defines the before-change style as the style of the previous style change event, but with any animation effects updated to current time. This is difficult, because it persisting the before-change world in some form that is responsive to what the (newly updated) animations actually do. (For example, we'd need to re-resolve var(), font-relative units, be able to handle revert/revert-layer, etc.) With Container Queries, this problem becomes substantially worse, because now we'd also need to maintain the "before-change layout tree", which in practice is gone at the time we need it. w3c#6398
(Using "animations" loosely to refer to any of CSS/Web Animations/Transitions/):
There seems to be an underlying idea in the specifications that effects caused by animations should themselves not trigger new animations. We solve this by making things "not animatable", "animation-tainted", etc. With container queries this problem becomes a bit worse, since the animated size of some element can cause the size of a sibling container (for example) to re-evaluate its container query, hence applying a totally different style to its descendants, including new
animation-
properties. Probably we need some adjustment to the specs to define what should happen when such animation properties appear (or change) because of "animated re-evaluation" of container queries.The css-transitions spec describes some alternate-reality-styles (before-change style and after-change style) which is used to determine from/to values. This does give a behavior for container queries, but the before/after-change styles now implicitly become before/after-change layouts. The before-change style is particularly painful, since we would need to do layout in a world where existing animations are updated to the current time, producing different animation effects, potentially causing a change in container query evaluation results, arbitrarily modifying the "previous" style and layout. This sounds pretty complicated to me, and I thought we should explore alternatives. And even if we want to keep css-transitions as is, we probably do need some update for css-animations.
What if we isolate all side-effects that happen because of stuff that's animating, such that it can't trigger new animations:
animation-
andtransition-
properties from the after-change-style.The. (EDIT: Take the computed values from after-change style instead, see previous bullet point).animation-
/transition-
properties are ignored on the animated style, hence if they appear/disappear/update because of container queries re-evaluating, then nothing happens. ("Global animation-tainting")Not sure how web-compatible this is, or if this loses us any crucial aspect of the current specified behavior. On the other hand, the interop situation seems pretty poor in this area, and no browser seems to implement the current before/after change style correctly, so there might be wiggle-room.
At the moment I don't have any other great ideas for how to deal with animations and container queries.
The text was updated successfully, but these errors were encountered: