mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-19 15:32:31 +00:00
LibWeb: Allow calc()
values in cubic-bezier()
easing functions
This commit is contained in:
parent
c5a3eaaf45
commit
fa1e02e5d7
Notes:
github-actions[bot]
2025-06-18 06:58:15 +00:00
Author: https://github.com/tcl3
Commit: fa1e02e5d7
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5123
Reviewed-by: https://github.com/gmta ✅
5 changed files with 58 additions and 31 deletions
|
@ -2621,19 +2621,30 @@ RefPtr<CSSStyleValue const> Parser::parse_easing_value(TokenStream<ComponentValu
|
|||
for (auto const& argument : comma_separated_arguments) {
|
||||
if (argument.size() != 1)
|
||||
return nullptr;
|
||||
if (!argument[0].is(Token::Type::Number))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EasingStyleValue::CubicBezier bezier {
|
||||
comma_separated_arguments[0][0].token().number_value(),
|
||||
comma_separated_arguments[1][0].token().number_value(),
|
||||
comma_separated_arguments[2][0].token().number_value(),
|
||||
comma_separated_arguments[3][0].token().number_value(),
|
||||
auto parse_argument = [this, &comma_separated_arguments](auto index) {
|
||||
TokenStream<ComponentValue> argument_tokens { comma_separated_arguments[index] };
|
||||
return parse_number(argument_tokens);
|
||||
};
|
||||
|
||||
if (bezier.x1 < 0.0 || bezier.x1 > 1.0 || bezier.x2 < 0.0 || bezier.x2 > 1.0)
|
||||
auto x1 = parse_argument(0);
|
||||
auto y1 = parse_argument(1);
|
||||
auto x2 = parse_argument(2);
|
||||
auto y2 = parse_argument(3);
|
||||
if (!x1.has_value() || !y1.has_value() || !x2.has_value() || !y2.has_value())
|
||||
return nullptr;
|
||||
if (!x1->is_calculated() && (x1->value() < 0.0 || x1->value() > 1.0))
|
||||
return nullptr;
|
||||
if (!x2->is_calculated() && (x2->value() < 0.0 || x2->value() > 1.0))
|
||||
return nullptr;
|
||||
|
||||
EasingStyleValue::CubicBezier bezier {
|
||||
x1.release_value(),
|
||||
y1.release_value(),
|
||||
x2.release_value(),
|
||||
y2.release_value(),
|
||||
};
|
||||
|
||||
transaction.commit();
|
||||
return EasingStyleValue::create(bezier);
|
||||
|
|
|
@ -230,6 +230,11 @@ double EasingStyleValue::CubicBezier::evaluate_at(double input_progress, bool) c
|
|||
};
|
||||
|
||||
// https://www.w3.org/TR/css-easing-1/#cubic-bezier-algo
|
||||
auto resolved_x1 = clamp(x1.resolved({}).value_or(0.0), 0.0, 1.0);
|
||||
auto resolved_y1 = y1.resolved({}).value_or(0.0);
|
||||
auto resolved_x2 = clamp(x2.resolved({}).value_or(0.0), 0.0, 1.0);
|
||||
auto resolved_y2 = y2.resolved({}).value_or(0.0);
|
||||
|
||||
// For input progress values outside the range [0, 1], the curve is extended infinitely using tangent of the curve
|
||||
// at the closest endpoint as follows:
|
||||
|
||||
|
@ -237,13 +242,13 @@ double EasingStyleValue::CubicBezier::evaluate_at(double input_progress, bool) c
|
|||
if (input_progress < 0.0) {
|
||||
// 1. If the x value of P1 is greater than zero, use a straight line that passes through P1 and P0 as the
|
||||
// tangent.
|
||||
if (x1 > 0.0)
|
||||
return y1 / x1 * input_progress;
|
||||
if (resolved_x1 > 0.0)
|
||||
return resolved_y1 / resolved_x1 * input_progress;
|
||||
|
||||
// 2. Otherwise, if the x value of P2 is greater than zero, use a straight line that passes through P2 and P0 as
|
||||
// the tangent.
|
||||
if (x2 > 0.0)
|
||||
return y2 / x2 * input_progress;
|
||||
if (resolved_x2 > 0.0)
|
||||
return resolved_y2 / resolved_x2 * input_progress;
|
||||
|
||||
// 3. Otherwise, let the output progress value be zero for all input progress values in the range [-∞, 0).
|
||||
return 0.0;
|
||||
|
@ -252,13 +257,13 @@ double EasingStyleValue::CubicBezier::evaluate_at(double input_progress, bool) c
|
|||
// - For input progress values greater than one,
|
||||
if (input_progress > 1.0) {
|
||||
// 1. If the x value of P2 is less than one, use a straight line that passes through P2 and P3 as the tangent.
|
||||
if (x2 < 1.0)
|
||||
return (1.0 - y2) / (1.0 - x2) * (input_progress - 1.0) + 1.0;
|
||||
if (resolved_x2 < 1.0)
|
||||
return (1.0 - resolved_y2) / (1.0 - resolved_x2) * (input_progress - 1.0) + 1.0;
|
||||
|
||||
// 2. Otherwise, if the x value of P1 is less than one, use a straight line that passes through P1 and P3 as the
|
||||
// tangent.
|
||||
if (x1 < 1.0)
|
||||
return (1.0 - y1) / (1.0 - x1) * (input_progress - 1.0) + 1.0;
|
||||
if (resolved_x1 < 1.0)
|
||||
return (1.0 - resolved_y1) / (1.0 - resolved_x1) * (input_progress - 1.0) + 1.0;
|
||||
|
||||
// 3. Otherwise, let the output progress value be one for all input progress values in the range (1, ∞].
|
||||
return 1.0;
|
||||
|
@ -270,8 +275,8 @@ double EasingStyleValue::CubicBezier::evaluate_at(double input_progress, bool) c
|
|||
auto x = input_progress;
|
||||
|
||||
auto solve = [&](auto t) {
|
||||
auto x = cubic_bezier_at(x1, x2, t);
|
||||
auto y = cubic_bezier_at(y1, y2, t);
|
||||
auto x = cubic_bezier_at(resolved_x1, resolved_x2, t);
|
||||
auto y = cubic_bezier_at(resolved_y1, resolved_y2, t);
|
||||
return CubicBezier::CachedSample { x, y, t };
|
||||
};
|
||||
|
||||
|
@ -317,7 +322,7 @@ double EasingStyleValue::CubicBezier::evaluate_at(double input_progress, bool) c
|
|||
}
|
||||
|
||||
// https://drafts.csswg.org/css-easing/#bezier-serialization
|
||||
String EasingStyleValue::CubicBezier::to_string(SerializationMode) const
|
||||
String EasingStyleValue::CubicBezier::to_string(SerializationMode mode) const
|
||||
{
|
||||
StringBuilder builder;
|
||||
if (*this == CubicBezier::ease()) {
|
||||
|
@ -329,7 +334,18 @@ String EasingStyleValue::CubicBezier::to_string(SerializationMode) const
|
|||
} else if (*this == CubicBezier::ease_in_out()) {
|
||||
builder.append("ease-in-out"sv);
|
||||
} else {
|
||||
builder.appendff("cubic-bezier({}, {}, {}, {})", x1, y1, x2, y2);
|
||||
auto x1_value = x1;
|
||||
auto y1_value = y1;
|
||||
auto x2_value = x2;
|
||||
auto y2_value = y2;
|
||||
if (mode == SerializationMode::ResolvedValue) {
|
||||
x1_value = clamp(x1_value.resolved({}).value_or(0.0), 0.0, 1.0);
|
||||
x2_value = clamp(x2_value.resolved({}).value_or(0.0), 0.0, 1.0);
|
||||
y1_value = y1_value.resolved({}).value_or(0.0);
|
||||
y2_value = y2_value.resolved({}).value_or(0.0);
|
||||
}
|
||||
builder.appendff("cubic-bezier({}, {}, {}, {})",
|
||||
x1_value.to_string(), y1_value.to_string(), x2_value.to_string(), y2_value.to_string());
|
||||
}
|
||||
return MUST(builder.to_string());
|
||||
}
|
||||
|
|
|
@ -47,10 +47,10 @@ public:
|
|||
static CubicBezier ease_out();
|
||||
static CubicBezier ease_in_out();
|
||||
|
||||
double x1;
|
||||
double y1;
|
||||
double x2;
|
||||
double y2;
|
||||
NumberOrCalculated x1 { 0 };
|
||||
NumberOrCalculated y1 { 0 };
|
||||
NumberOrCalculated x2 { 0 };
|
||||
NumberOrCalculated y2 { 0 };
|
||||
|
||||
struct CachedSample {
|
||||
double x;
|
||||
|
@ -79,7 +79,7 @@ public:
|
|||
static Steps step_start();
|
||||
static Steps step_end();
|
||||
|
||||
IntegerOrCalculated number_of_intervals { 0 };
|
||||
IntegerOrCalculated number_of_intervals { 1 };
|
||||
Position position { Position::End };
|
||||
|
||||
bool operator==(Steps const&) const = default;
|
||||
|
|
|
@ -2,8 +2,8 @@ Harness status: OK
|
|||
|
||||
Found 21 tests
|
||||
|
||||
18 Pass
|
||||
3 Fail
|
||||
19 Pass
|
||||
2 Fail
|
||||
Pass Property animation-timing-function value 'linear'
|
||||
Pass Property animation-timing-function value 'ease'
|
||||
Pass Property animation-timing-function value 'ease-in'
|
||||
|
@ -12,7 +12,7 @@ Pass Property animation-timing-function value 'ease-in-out'
|
|||
Pass Property animation-timing-function value 'cubic-bezier(0.1, 0.2, 0.8, 0.9)'
|
||||
Pass Property animation-timing-function value 'cubic-bezier(0, -2, 1, 3)'
|
||||
Pass Property animation-timing-function value 'cubic-bezier(0, 0.7, 1, 1.3)'
|
||||
Fail Property animation-timing-function value 'cubic-bezier(calc(-2), calc(0.7 / 2), calc(1.5), calc(0))'
|
||||
Pass Property animation-timing-function value 'cubic-bezier(calc(-2), calc(0.7 / 2), calc(1.5), calc(0))'
|
||||
Pass Property animation-timing-function value 'steps(4, start)'
|
||||
Pass Property animation-timing-function value 'steps(2, end)'
|
||||
Pass Property animation-timing-function value 'steps( 2, end )'
|
||||
|
|
|
@ -2,8 +2,8 @@ Harness status: OK
|
|||
|
||||
Found 22 tests
|
||||
|
||||
19 Pass
|
||||
3 Fail
|
||||
20 Pass
|
||||
2 Fail
|
||||
Pass e.style['animation-timing-function'] = "linear" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "ease" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "ease-in" should set the property value
|
||||
|
@ -12,7 +12,7 @@ Pass e.style['animation-timing-function'] = "ease-in-out" should set the propert
|
|||
Pass e.style['animation-timing-function'] = "cubic-bezier(0.1, 0.2, 0.8, 0.9)" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "cubic-bezier(0, -2, 1, 3)" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "cubic-bezier(0, 0.7, 1, 1.3)" should set the property value
|
||||
Fail e.style['animation-timing-function'] = "cubic-bezier(calc(-2), calc(0.7 / 2), calc(1.5), calc(0))" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "cubic-bezier(calc(-2), calc(0.7 / 2), calc(1.5), calc(0))" should set the property value
|
||||
Fail e.style['animation-timing-function'] = "cubic-bezier(0, sibling-index(), 1, sign(2em - 20px))" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "steps(4, start)" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "steps(2, end)" should set the property value
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue