LibWeb: Support number/percentage interpolation for scale

Previously we didn't support interpolating component values of `scale`
from a number to a percentage (or vice versa). This also caused
interpolation from `none` to a percentage value to fail since the
fallback value is number based.

This makes the transition run when hovering buttons on
https://chrede88.github.io/L1nkr/ rather than being discrete.
This commit is contained in:
Callum Law 2026-06-18 20:22:04 +12:00 committed by Jelle Raaijmakers
parent 2c6aa4d272
commit 64f866cae2
3 changed files with 33 additions and 15 deletions

View file

@ -100,7 +100,7 @@ static RefPtr<StyleValue const> interpolate_discrete(StyleValue const& from, Sty
return delta >= 0.5f ? to : from;
}
static RefPtr<StyleValue const> interpolate_scale(DOM::Element& element, CalculationContext const& calculation_context, StyleValue const& a_from, StyleValue const& a_to, float delta, AllowDiscrete allow_discrete)
static RefPtr<StyleValue const> interpolate_scale(StyleValue const& a_from, StyleValue const& a_to, float delta)
{
if (a_from.to_keyword() == Keyword::None && a_to.to_keyword() == Keyword::None)
return a_from;
@ -113,26 +113,20 @@ static RefPtr<StyleValue const> interpolate_scale(DOM::Element& element, Calcula
auto const& from_transform = from.as_transformation();
auto const& to_transform = to.as_transformation();
auto interpolated_x = interpolate_value(element, calculation_context, from_transform.values()[0], to_transform.values()[0], delta, allow_discrete);
if (!interpolated_x)
return {};
auto interpolated_y = interpolate_value(element, calculation_context, from_transform.values()[1], to_transform.values()[1], delta, allow_discrete);
if (!interpolated_y)
return {};
RefPtr<StyleValue const> interpolated_z;
auto interpolated_x = interpolate_raw(number_from_style_value(from_transform.values()[0], 1), number_from_style_value(to_transform.values()[0], 1), delta, infinite_range);
auto interpolated_y = interpolate_raw(number_from_style_value(from_transform.values()[1], 1), number_from_style_value(to_transform.values()[1], 1), delta, infinite_range);
Optional<double> interpolated_z;
if (from_transform.values().size() == 3 || to_transform.values().size() == 3) {
static auto const& one_value = NumberStyleValue::create(1).leak_ref();
auto from = from_transform.values().size() == 3 ? from_transform.values()[2] : ValueComparingNonnullRefPtr<StyleValue const> { one_value };
auto to = to_transform.values().size() == 3 ? to_transform.values()[2] : ValueComparingNonnullRefPtr<StyleValue const> { one_value };
interpolated_z = interpolate_value(element, calculation_context, from, to, delta, allow_discrete);
if (!interpolated_z)
return {};
interpolated_z = interpolate_raw(number_from_style_value(from, 1), number_from_style_value(to, 1), delta, infinite_range);
}
StyleValueVector new_values = { *interpolated_x, *interpolated_y };
if (interpolated_z)
new_values.append(*interpolated_z);
StyleValueVector new_values = { NumberStyleValue::create(interpolated_x), NumberStyleValue::create(interpolated_y) };
if (interpolated_z.has_value())
new_values.append(NumberStyleValue::create(interpolated_z.value()));
return TransformationStyleValue::create(
PropertyID::Scale,
@ -766,7 +760,7 @@ ValueComparingRefPtr<StyleValue const> interpolate_property(DOM::Element& elemen
}
if (property_id == PropertyID::Scale) {
if (auto result = interpolate_scale(element, calculation_context, from, to, delta, allow_discrete))
if (auto result = interpolate_scale(from, to, delta))
return result;
return interpolate_discrete(from, to, delta, allow_discrete);
}

View file

@ -0,0 +1,4 @@
100% -> 200%: 1.5
1 -> 200%: 1.5
100% -> 2: 1.5
none -> 200%: 1.5

View file

@ -0,0 +1,20 @@
<!doctype html>
<div id="target"></div>
<script src="../include.js"></script>
<script>
test(() => {
function sample(from, to) {
const animation = target.animate({ scale: [from, to] }, 1000);
animation.pause();
animation.currentTime = 500;
println(`${from} -> ${to}: ${getComputedStyle(target).scale}`);
animation.cancel();
}
// Each of the following pairs of values should interpolate to "1.5" at the halfway point
sample("100%", "200%");
sample("1", "200%");
sample("100%", "2");
sample("none", "200%");
});
</script>