opacity clamping and additive animation
Categories
(Core :: CSS Transitions and Animations, defect, P3)
Tracking
()
People
(Reporter: kvndy, Assigned: kvndy)
Details
Attachments
(4 files, 4 obsolete files)
Steps to reproduce:
Firefox Nightly 144.0a1 (2025-08-24)
Toggle manually-constructed additive Web-Animation of opacity from 1 to 0 on top of a newly-changed specified value of 0, run to completion. Item fades out.
Toggle manually-constructed additive Web-Animation of opacity from -1 to 0 on top of a newly-changed specified value of 1, run to completion. Item should fade in but doesn't. It instantly changes to 1, and an opacity animation runs from 0 to 0
Actual results:
Opacity values are clamped between 0 and 1
Expected results:
Opacity values should not be clamped between 0 and 1. There is no useful form of additive animation that disallows negative values.
As this related bug
https://bugzilla.mozilla.org/show_bug.cgi?id=662157#c4
points out, the SVG spec specifically defers clamping, here:
https://www.w3.org/TR/SVG11/implnote.html#RangeClamping
There is no such accommodation made for CSS. Firefox behavior has changed since this comment:
https://github.com/w3c/csswg-drafts/issues/3340#issuecomment-441488781
Chromium behavior is as expected, an additive opacity animation runs from -1 to 0.
This needs to be fixed at the spec level, but it would be wonderful if Firefox would exhibit the more useful behavior of deferred clamping until that time.
It also needs to be pointed out that negative width and height values are disallowed on Keyframe construction in both Chromium and Firefox. (This also needs to be changed at the spec level.) Firefox console.warns of this in KeyframeUtils.cpp MakePropertyValuePair, but the same does not happen for opacity which fails silently.
| Assignee | ||
Comment 1•7 months ago
|
||
Toggle on mousedown, twice.
| Assignee | ||
Comment 2•7 months ago
|
||
I’ve filed an issue with the W3C. Please support specifying deferred clamping for Keyframe values.
https://github.com/w3c/csswg-drafts/issues/12648
Comment 3•7 months ago
|
||
The Bugbug bot thinks this bug should belong to the 'Core::Layout' component, and is moving the bug to that component. Please correct in case you think the bot is wrong.
Comment 4•7 months ago
|
||
This is not specific to opacity in any way tho, right? All values clamp the same (e.g. all non-negative values clamp similarly to opacity)
| Assignee | ||
Comment 5•7 months ago
|
||
Don’t know the answer to your question but can investigate. I’ve narrowed it down some but still can’t figure it out. Maybe there’s an outer to_computed_value getting called which is clamping?
Logs:
glue.rs Servo_GetComputedKeyframeValues
animated_properties.mako.rs AnimationValue from_declaration
animated_properties.mako.rs AnimationValue from_declaration not boxed value pre:Opacity(Number { value: -1.0, calc_clamping_mode: None });
specified/mod.rs Number get result:-1.0;
specified/mod.rs to_computed_value for Number result:-1.0;
animated_properties.mako.rs AnimationValue from_declaration not boxed value post:0.0;
Code (animated_properties.mako.rs):
% if boxed:
let value = (**value).to_computed_value(context);
% else:
println!("animated_properties.mako.rs AnimationValue from_declaration not boxed value pre:{:?};",value);
let value = value.to_computed_value(context);
println!("animated_properties.mako.rs AnimationValue from_declaration not boxed value post:{:?};",value);
% endif
| Assignee | ||
Comment 6•7 months ago
|
||
I will continue to investigate but will have to pause for a bit. If the solution is obvious to you or anyone else by all means please land a patch.
| Assignee | ||
Comment 7•7 months ago
|
||
I don’t have breakpoints enabled but the first thing I will try is to get a stack trace.
| Assignee | ||
Comment 8•7 months ago
|
||
On first mousedown:
opacity from 1 to 0, converted to relative 1-0 is from 1 to 0, animates
lineHeight from 2.4 to 1.2, converted to relative 2.4-1.2 is from 1.2 to 0, animates
width & height from 200px to 100px, converted to relative 200-100 is from 100px to 0, animates
On second mousedown:
opacity from 0 to 1, converted to relative 0-1 is from -1 to 0, fails
lineHeight from 1.2 to 2.4, converted to relative 1.2-2.4 is from -1.2 to 0, fails
width & height from 100px to 200px, converted to relative 100-200 is from -100px to 0, fails
Console warns:
Keyframe property value “-100px” is invalid according to the syntax for “width”.
Keyframe property value “-100px” is invalid according to the syntax for “height”.
Keyframe property value “-1.2” is invalid according to the syntax for “line-height”.
No console warning for opacity. In Chromium opacity animates on both mousedown events, and it gives the same three console warnings.
| Assignee | ||
Comment 9•7 months ago
|
||
Finally got a stack trace. My worst fears have been realized. The only useful form of additive animation has been arbitrarily restricted by under-specification. I’ve tried to convince the W3C to allow these features for more than a dozen years. Someone else will have to acknowledge the usefulness and request negative values for keyframes. Code from specified/mod.rs:
impl ToComputedValue for Opacity {
type ComputedValue = CSSFloat;
#[inline]
fn to_computed_value(&self, context: &Context) -> CSSFloat {
let value = self.0.to_computed_value(context);
if context.for_smil_animation {
// SMIL expects to be able to interpolate between out-of-range
// opacity values.
value
} else {
value.min(1.0).max(0.0)
}
}
#[inline]
fn from_computed_value(computed: &CSSFloat) -> Self {
Opacity(Number::from_computed_value(computed))
}
}
| Assignee | ||
Comment 10•7 months ago
|
||
| Assignee | ||
Comment 11•7 months ago
|
||
Tab Atkins responded to https://github.com/w3c/csswg-drafts/issues/12648
I couldn’t help but bloviate, but his response may impact any decision on accepting this patch
| Assignee | ||
Comment 12•7 months ago
|
||
This is the corrected link to his second response:
https://github.com/w3c/csswg-drafts/issues/12648#issuecomment-3234345013
Updated•7 months ago
|
| Assignee | ||
Comment 13•7 months ago
|
||
No changes planned, despite erroneous tag
Updated•6 months ago
|
| Assignee | ||
Comment 14•6 months ago
|
||
Chromium behavior differs from Firefox 142 and Safari. I don’t understand when animation clamping is supposed to occur. I also don’t understand why opacity behavior differs in allowing out-of-bounds values for keyframes and element.style when other properties such as height and lineHeight do not. Closed as invalid until I do understand.
| Assignee | ||
Comment 15•6 months ago
|
||
Reopened to submit a second patch. I'm still unsure if opacity clamping is a choice or not, if this is a feature or a bug. I believe type <number> and <percentage> should be able to interpolate out-of-range values for opacity, regardless if it’s SMIL or not.
| Assignee | ||
Comment 16•6 months ago
|
||
| Assignee | ||
Comment 17•6 months ago
|
||
Updated•6 months ago
|
| Assignee | ||
Comment 18•6 months ago
|
||
| Assignee | ||
Comment 19•6 months ago
|
||
Sorry, I don't know how to add to an existing patch. I attempted git merge --squash which is obviously wrong
Comment 20•6 months ago
|
||
Just git commit --amend, then moz-phab again?
| Assignee | ||
Comment 21•6 months ago
|
||
(In reply to Emilio Cobos Álvarez [:emilio] from comment #20)
Just
git commit --amend, thenmoz-phabagain?
I tried that but I guess I was in the wrong branch, one that I made to test against the mochikit tests, so it failed. I can try again.
| Assignee | ||
Comment 22•5 months ago
|
||
| Assignee | ||
Comment 23•5 months ago
|
||
The reason why I keep submitting new patches is I used the command git commit --amend -m "Bug 1984925 - [animation] out-of-bounds opacity keyframes and wpt"
I didn't realize adding a commit message is what creates a new patch. Next time I will use the command git commit --amend only.
WIP because the test needs a spec reference.
Updated•5 months ago
|
Updated•5 months ago
|
| Assignee | ||
Comment 24•5 months ago
|
||
Attempting to download D266702 which is the tentatively accepted patch and continue from there
| Assignee | ||
Comment 25•5 months ago
|
||
git commit --amend without the additional -m "commit message" forces me to give a commit message anyway. I'm afraid moz-phab after that will create another new patch. Apologies in advance for my incompetence
Updated•5 months ago
|
| Assignee | ||
Comment 26•5 months ago
|
||
From CSS-Values-4 Section 16. Serializing “Opacity values outside the range [0,1] are preserved, without clamping, in the serialized specified value.”
From Web-Animations-1 5.3.2. Computing property values “resolve value according to the Computed Value line of the property’s definition table”.
From Web-Animations-1 5.3.3. Calculating computed keyframes “Before calculating the effect value of a keyframe effect, the property values on its keyframes are computed” and “The result of resolving these values is a set of computed keyframes.”
The later subsection states “Computed keyframes are produced using the following procedure.” and “2. For each property specified in keyframe: Compute a property value using the value specified on keyframe as the value”.
From Web-Animations-1 5.3.4. The effect value of a keyframe effect “5. Define the neutral value for composition as a value which, when combined with an underlying value using the add composite operation, produces the underlying value.” and “6. Let property-specific keyframes be the result of getting the set of computed keyframes for this keyframe effect.”
Later it states “If keyframe has a composite operation that is not replace” and “2. Let value to combine be the property value of target property specified on keyframe.”
I’m having difficulty finding a generous interpretation that would allow deferred opacity clamping. If the computed value is clamped, the set of computed keyframes is clamped.
Description
•