MDN https://developer.mozilla.org/en-US/docs/Web/API/Animation/commitStyles says:
"The commitStyles() method of the Web Animations API's Animation interface commits the end styling state of an animation to the element being animated, even after that animation has been removed. It will cause the end styling state to be written to the element being animated, in the form of properties inside a style attribute."
This is exactly what I want and I like the example in which commitStyles() is called right after the animation is created. The expected behavior is very clear since the end state of an animation is well-defined
To my surprise this is not the case in Chrome 84. Looking at the spec it seems MDN got it wrong:
https://drafts.csswg.org/web-animations/#dom-animation-commitstyles says:
"Writes the current effect values produced by this animation鈥檚 animation effects to their corresponding effect targets' inline style using the commit computed styles procedure."
So an animation of transform: translate(100px), duration: 1000 may record a translation of anywhere between 0 and 100px depending on when commitStyles() is called. Sure calling it after the animation finishes insures the end state is written but seems to also require setting fill: forwards. It just seems much easier and more intuitive to have it record a predictable state no matter when it's called
Could commitStyles() have an option to always record the end state preferably without involving the fill property? Even better could it become an animation property and I wouldn't have to use fill at all?
That's an interesting proposal. It certainly seems feasible to add a flag to commitStyles to produce the behavior you describe. Do you mind explaining the particular use case you have for it?
From my understanding, the proposal would be to effectively seek to the target effect end (but without firing events etc.), force the fill mode for all associated effects to both, and then apply the procedure to commit computed styles.
Understanding the use cases would give a better idea of whether this should be two separate flags or not (e.g. one to seek to the target end, one to force the fill mode). It would also give a better idea of the priority of this feature.
My application is a large state graph of vertices and edges. Each state transition animates the entire graph so the destination vertex (new current state) is centered in the view box. The new position of the graph needs to persist. There's a timeline slider to play the transitions from any point forward and backward.
It seems like what this case needs is something like:
function finishTransition(anim) {
// Make sure finish() seeks to the target effect end
anim.playbackRate = 1;
anim.finish();
anim.effect.updateTiming({ fill: 'both' }); // Or 'forwards'
anim.commitStyles();
anim.cancel();
}
I wonder how common this pattern is or if there are similar patterns?
My understanding was that commitStyles was created as an alternative to fill-forwards, with fewer gotchas. But in practice, finish happens too late to commit the styles, so you still need to use fill-forwards to persist them, then cancel the animation to avoid the gotchas of fill-forwards.
Adding a flag to commitStyles just to make its primary use-case work seems wrong, but maybe I'm wrong about the primary use-case.
The spec gives a bit of a summary of the motivation and alternatives here: https://drafts.csswg.org/web-animations-1/#fill-behavior
The interesting part with regard to fill modes is that most animation APIs (going back to SMIL upon which CoreAnimation etc. were built) use endpoint exclusive timing so at the exact point when an animation finishes, it has no effect--unless there is an appropriate fill mode set. That's useful for sequencing and repeating animations since it ensures they don't overlap but it's been causing problems for scroll-driven animations recently. (There a bit of explanation in the SMIL spec.)
Unfortunately, we very likely can't change this very fundamental part of the model (and other specs like SVG etc. depend on it if we ever want to rebase them on Web Animations).
However commitStyles() already has the special behavior that it applies removed animations so I suppose we could make it also have the special behavior of altering the timing model to sample a finished animation as if its (root) target effect had fill: both set. That would solve half of this issue and, I believe, the troubles @jakearchibald encountered. It's also possible this API is new enough that such a change would be Web compatible. It wouldn't change the behavior for animations which are already filling which is likely to be the majority case anyway.
Fwiw, my advice to developers is to avoid filling forwards, as those "stuck" animations tend to catch you out later on, so I see commitStyles as something that makes avoiding fill-forwards easier.
I think there are issues with immediately committing the end value when you have composite animations:
animA = element.animate(
{transform: ['translateX(0)', 'translateX(500px)']},
{duration: 5000, composite: 'add'});
animA.commitStyles(); // element.style.transform = 'translateX(500px)';
Now the effect is animating from 500px to 1000px?
This is why in #5475 I proposed we don't commit immediately but rather commit the style change when the animation finishes (on an infinitely precise timeline which is conceptually identical to filling forwards).
The proposal here is that for a finished animation you would apply the final value. It wouldn't affect animation's that are not finished. (More precisely, we'd simply opt out of the endpoint exclusive behaviour for the root target effect such that this would still apply to un-finished animations that are in their end delay.)
The part about jumping to the end is separate (hence why this proposal only addresses half of this issue).
Most helpful comment
Fwiw, my advice to developers is to avoid filling forwards, as those "stuck" animations tend to catch you out later on, so I see
commitStylesas something that makes avoiding fill-forwards easier.