mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-16 05:51:55 +00:00
LibWeb: Allow calc()
values in steps()
easing functions
This commit is contained in:
parent
21b531598d
commit
c5a3eaaf45
Notes:
github-actions[bot]
2025-06-18 06:58:22 +00:00
Author: https://github.com/tcl3
Commit: c5a3eaaf45
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5123
Reviewed-by: https://github.com/gmta ✅
8 changed files with 177 additions and 27 deletions
|
@ -75,7 +75,7 @@ EffectTiming AnimationEffect::get_timing() const
|
|||
.iterations = m_iteration_count,
|
||||
.duration = m_iteration_duration,
|
||||
.direction = m_playback_direction,
|
||||
.easing = m_timing_function.to_string(),
|
||||
.easing = m_timing_function.to_string(CSS::SerializationMode::Normal),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ ComputedEffectTiming AnimationEffect::get_computed_timing() const
|
|||
.iterations = m_iteration_count,
|
||||
.duration = duration,
|
||||
.direction = m_playback_direction,
|
||||
.easing = m_timing_function.to_string(),
|
||||
.easing = m_timing_function.to_string(CSS::SerializationMode::Normal),
|
||||
},
|
||||
|
||||
end_time(),
|
||||
|
|
|
@ -2651,11 +2651,10 @@ RefPtr<CSSStyleValue const> Parser::parse_easing_value(TokenStream<ComponentValu
|
|||
EasingStyleValue::Steps steps;
|
||||
|
||||
auto const& intervals_argument = comma_separated_arguments[0][0];
|
||||
if (!intervals_argument.is(Token::Type::Number))
|
||||
auto intervals_token = TokenStream<ComponentValue>::of_single_token(intervals_argument);
|
||||
auto intervals = parse_integer(intervals_token);
|
||||
if (!intervals.has_value())
|
||||
return nullptr;
|
||||
if (!intervals_argument.token().number().is_integer())
|
||||
return nullptr;
|
||||
auto intervals = intervals_argument.token().to_integer();
|
||||
|
||||
if (comma_separated_arguments.size() == 2) {
|
||||
TokenStream identifier_stream { comma_separated_arguments[1] };
|
||||
|
@ -2690,14 +2689,16 @@ RefPtr<CSSStyleValue const> Parser::parse_easing_value(TokenStream<ComponentValu
|
|||
// https://drafts.csswg.org/css-easing/#step-easing-functions
|
||||
// If the <step-position> is jump-none, the <integer> must be at least 2, or the function is invalid.
|
||||
// Otherwise, the <integer> must be at least 1, or the function is invalid.
|
||||
if (!intervals->is_calculated()) {
|
||||
if (steps.position == EasingStyleValue::Steps::Position::JumpNone) {
|
||||
if (intervals <= 1)
|
||||
if (intervals->value() <= 1)
|
||||
return nullptr;
|
||||
} else if (intervals <= 0) {
|
||||
} else if (intervals->value() <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
steps.number_of_intervals = intervals;
|
||||
steps.number_of_intervals = *intervals;
|
||||
transaction.commit();
|
||||
return EasingStyleValue::create(steps);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "EasingStyleValue.h"
|
||||
#include <AK/BinarySearch.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibWeb/CSS/StyleValues/IntegerStyleValue.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
|
@ -174,7 +175,7 @@ double EasingStyleValue::Linear::evaluate_at(double input_progress, bool before_
|
|||
}
|
||||
|
||||
// https://drafts.csswg.org/css-easing/#linear-easing-function-serializing
|
||||
String EasingStyleValue::Linear::to_string() const
|
||||
String EasingStyleValue::Linear::to_string(SerializationMode) const
|
||||
{
|
||||
// The linear keyword is serialized as itself.
|
||||
if (*this == identity())
|
||||
|
@ -316,7 +317,7 @@ double EasingStyleValue::CubicBezier::evaluate_at(double input_progress, bool) c
|
|||
}
|
||||
|
||||
// https://drafts.csswg.org/css-easing/#bezier-serialization
|
||||
String EasingStyleValue::CubicBezier::to_string() const
|
||||
String EasingStyleValue::CubicBezier::to_string(SerializationMode) const
|
||||
{
|
||||
StringBuilder builder;
|
||||
if (*this == CubicBezier::ease()) {
|
||||
|
@ -337,7 +338,10 @@ double EasingStyleValue::Steps::evaluate_at(double input_progress, bool before_f
|
|||
{
|
||||
// https://www.w3.org/TR/css-easing-1/#step-easing-algo
|
||||
// 1. Calculate the current step as floor(input progress value × steps).
|
||||
auto current_step = floor(input_progress * number_of_intervals);
|
||||
auto resolved_number_of_intervals = number_of_intervals.resolved({}).value_or(1);
|
||||
resolved_number_of_intervals = max(resolved_number_of_intervals, position == Steps::Position::JumpNone ? 2 : 1);
|
||||
|
||||
auto current_step = floor(input_progress * resolved_number_of_intervals);
|
||||
|
||||
// 2. If the step position property is one of:
|
||||
// - jump-start,
|
||||
|
@ -350,7 +354,7 @@ double EasingStyleValue::Steps::evaluate_at(double input_progress, bool before_f
|
|||
// - the before flag is set, and
|
||||
// - input progress value × steps mod 1 equals zero (that is, if input progress value × steps is integral), then
|
||||
// decrement current step by one.
|
||||
auto step_progress = input_progress * number_of_intervals;
|
||||
auto step_progress = input_progress * resolved_number_of_intervals;
|
||||
if (before_flag && trunc(step_progress) == step_progress)
|
||||
current_step -= 1;
|
||||
|
||||
|
@ -363,7 +367,7 @@ double EasingStyleValue::Steps::evaluate_at(double input_progress, bool before_f
|
|||
// jump-start or jump-end -> steps
|
||||
// jump-none -> steps - 1
|
||||
// jump-both -> steps + 1
|
||||
auto jumps = number_of_intervals;
|
||||
auto jumps = resolved_number_of_intervals;
|
||||
if (position == Steps::Position::JumpNone) {
|
||||
jumps--;
|
||||
} else if (position == Steps::Position::JumpBoth) {
|
||||
|
@ -379,7 +383,7 @@ double EasingStyleValue::Steps::evaluate_at(double input_progress, bool before_f
|
|||
}
|
||||
|
||||
// https://drafts.csswg.org/css-easing/#steps-serialization
|
||||
String EasingStyleValue::Steps::to_string() const
|
||||
String EasingStyleValue::Steps::to_string(SerializationMode mode) const
|
||||
{
|
||||
StringBuilder builder;
|
||||
// Unlike the other easing function keywords, step-start and step-end do not serialize as themselves.
|
||||
|
@ -403,10 +407,15 @@ String EasingStyleValue::Steps::to_string() const
|
|||
return {};
|
||||
}
|
||||
}();
|
||||
auto intervals = number_of_intervals;
|
||||
if (mode == SerializationMode::ResolvedValue) {
|
||||
auto resolved_value = number_of_intervals.resolved({}).value_or(1);
|
||||
intervals = max(resolved_value, this->position == Steps::Position::JumpNone ? 2 : 1);
|
||||
}
|
||||
if (position.has_value()) {
|
||||
builder.appendff("steps({}, {})", number_of_intervals, position.value());
|
||||
builder.appendff("steps({}, {})", intervals.to_string(), position.value());
|
||||
} else {
|
||||
builder.appendff("steps({})", number_of_intervals);
|
||||
builder.appendff("steps({})", intervals.to_string());
|
||||
}
|
||||
}
|
||||
return MUST(builder.to_string());
|
||||
|
@ -420,11 +429,11 @@ double EasingStyleValue::Function::evaluate_at(double input_progress, bool befor
|
|||
});
|
||||
}
|
||||
|
||||
String EasingStyleValue::Function::to_string() const
|
||||
String EasingStyleValue::Function::to_string(SerializationMode mode) const
|
||||
{
|
||||
return visit(
|
||||
[&](auto const& curve) {
|
||||
return curve.to_string();
|
||||
return curve.to_string(mode);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibWeb/CSS/CSSStyleValue.h>
|
||||
#include <LibWeb/CSS/CalculatedOr.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
|
@ -35,7 +36,7 @@ public:
|
|||
bool operator==(Linear const&) const = default;
|
||||
|
||||
double evaluate_at(double input_progress, bool before_flag) const;
|
||||
String to_string() const;
|
||||
String to_string(SerializationMode) const;
|
||||
|
||||
Linear(Vector<Stop> stops);
|
||||
};
|
||||
|
@ -62,7 +63,7 @@ public:
|
|||
bool operator==(CubicBezier const&) const;
|
||||
|
||||
double evaluate_at(double input_progress, bool before_flag) const;
|
||||
String to_string() const;
|
||||
String to_string(SerializationMode) const;
|
||||
};
|
||||
|
||||
struct Steps {
|
||||
|
@ -78,20 +79,20 @@ public:
|
|||
static Steps step_start();
|
||||
static Steps step_end();
|
||||
|
||||
unsigned int number_of_intervals;
|
||||
IntegerOrCalculated number_of_intervals { 0 };
|
||||
Position position { Position::End };
|
||||
|
||||
bool operator==(Steps const&) const = default;
|
||||
|
||||
double evaluate_at(double input_progress, bool before_flag) const;
|
||||
String to_string() const;
|
||||
String to_string(SerializationMode) const;
|
||||
};
|
||||
|
||||
struct Function : public Variant<Linear, CubicBezier, Steps> {
|
||||
using Variant::Variant;
|
||||
|
||||
double evaluate_at(double input_progress, bool before_flag) const;
|
||||
String to_string() const;
|
||||
String to_string(SerializationMode) const;
|
||||
};
|
||||
|
||||
static ValueComparingNonnullRefPtr<EasingStyleValue const> create(Function const& function)
|
||||
|
@ -102,7 +103,7 @@ public:
|
|||
|
||||
Function const& function() const { return m_function; }
|
||||
|
||||
virtual String to_string(SerializationMode) const override { return m_function.to_string(); }
|
||||
virtual String to_string(SerializationMode mode) const override { return m_function.to_string(mode); }
|
||||
|
||||
bool properties_equal(EasingStyleValue const& other) const { return m_function == other.m_function; }
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 21 tests
|
||||
|
||||
18 Pass
|
||||
3 Fail
|
||||
Pass Property animation-timing-function value 'linear'
|
||||
Pass Property animation-timing-function value 'ease'
|
||||
Pass Property animation-timing-function value 'ease-in'
|
||||
Pass Property animation-timing-function value 'ease-out'
|
||||
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 'steps(4, start)'
|
||||
Pass Property animation-timing-function value 'steps(2, end)'
|
||||
Pass Property animation-timing-function value 'steps( 2, end )'
|
||||
Pass Property animation-timing-function value 'steps(2, jump-start)'
|
||||
Pass Property animation-timing-function value 'steps(2, jump-end)'
|
||||
Pass Property animation-timing-function value 'steps(2, jump-both)'
|
||||
Pass Property animation-timing-function value 'steps(2, jump-none)'
|
||||
Pass Property animation-timing-function value 'steps(calc(-10), start)'
|
||||
Pass Property animation-timing-function value 'steps(calc(5 / 2), start)'
|
||||
Pass Property animation-timing-function value 'steps(calc(1), jump-none)'
|
||||
Fail Property animation-timing-function value 'linear, ease, linear'
|
||||
Fail Property animation-timing-function value 'steps(calc(2 + sign(100em - 1px)), end)'
|
|
@ -0,0 +1,28 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 22 tests
|
||||
|
||||
19 Pass
|
||||
3 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
|
||||
Pass e.style['animation-timing-function'] = "ease-out" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "ease-in-out" should set the property value
|
||||
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
|
||||
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
|
||||
Pass e.style['animation-timing-function'] = "steps( 2, end )" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "steps(2, jump-start)" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "steps(2, jump-end)" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "steps(2, jump-both)" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "steps(2, jump-none)" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "steps(calc(-10), start)" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "steps(calc(5 / 2), start)" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "steps(calc(1), jump-none)" should set the property value
|
||||
Fail e.style['animation-timing-function'] = "linear, ease, linear" should set the property value
|
||||
Pass e.style['animation-timing-function'] = "steps(calc(2 + sign(100em - 1px)))" should set the property value
|
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Easing: getComputedStyle().animationTimingFunction</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-easing/#timing-functions">
|
||||
<meta name="assert" content="animation-timing-function computed value is a computed <easing-function> list.">
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
<script src="../../css/support/computed-testcommon.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
<script>
|
||||
test_computed_value("animation-timing-function", "linear");
|
||||
|
||||
test_computed_value("animation-timing-function", "ease");
|
||||
test_computed_value("animation-timing-function", "ease-in");
|
||||
test_computed_value("animation-timing-function", "ease-out");
|
||||
test_computed_value("animation-timing-function", "ease-in-out");
|
||||
test_computed_value("animation-timing-function", "cubic-bezier(0.1, 0.2, 0.8, 0.9)");
|
||||
test_computed_value("animation-timing-function", "cubic-bezier(0, -2, 1, 3)");
|
||||
test_computed_value("animation-timing-function", "cubic-bezier(0, 0.7, 1, 1.3)");
|
||||
test_computed_value("animation-timing-function", "cubic-bezier(calc(-2), calc(0.7 / 2), calc(1.5), calc(0))", "cubic-bezier(0, 0.35, 1, 0)",);
|
||||
|
||||
test_computed_value("animation-timing-function", "steps(4, start)");
|
||||
test_computed_value("animation-timing-function", "steps(2, end)", "steps(2)");
|
||||
test_computed_value("animation-timing-function", "steps( 2, end )", "steps(2)");
|
||||
test_computed_value("animation-timing-function", "steps(2, jump-start)");
|
||||
test_computed_value("animation-timing-function", "steps(2, jump-end)", "steps(2)");
|
||||
test_computed_value("animation-timing-function", "steps(2, jump-both)");
|
||||
test_computed_value("animation-timing-function", "steps(2, jump-none)");
|
||||
test_computed_value("animation-timing-function", "steps(calc(-10), start)", "steps(1, start)");
|
||||
test_computed_value("animation-timing-function", "steps(calc(5 / 2), start)", "steps(3, start)");
|
||||
test_computed_value("animation-timing-function", "steps(calc(1), jump-none)", "steps(2, jump-none)");
|
||||
|
||||
test_computed_value("animation-timing-function", "linear, ease, linear");
|
||||
|
||||
test_computed_value("animation-timing-function", "steps(calc(2 + sign(100em - 1px)), end)", "steps(3)");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Easing: parsing animation-timing-function with valid values</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-easing/#timing-functions">
|
||||
<meta name="assert" content="animation-timing-function supports the full grammar '<timing-function> #'.">
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
<script src="../../css/support/parsing-testcommon.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
test_valid_value("animation-timing-function", "linear");
|
||||
|
||||
test_valid_value("animation-timing-function", "ease");
|
||||
test_valid_value("animation-timing-function", "ease-in");
|
||||
test_valid_value("animation-timing-function", "ease-out");
|
||||
test_valid_value("animation-timing-function", "ease-in-out");
|
||||
test_valid_value("animation-timing-function", "cubic-bezier(0.1, 0.2, 0.8, 0.9)");
|
||||
test_valid_value("animation-timing-function", "cubic-bezier(0, -2, 1, 3)");
|
||||
test_valid_value("animation-timing-function", "cubic-bezier(0, 0.7, 1, 1.3)");
|
||||
test_valid_value("animation-timing-function", "cubic-bezier(calc(-2), calc(0.7 / 2), calc(1.5), calc(0))", "cubic-bezier(calc(-2), calc(0.35), calc(1.5), calc(0))");
|
||||
test_valid_value("animation-timing-function", "cubic-bezier(0, sibling-index(), 1, sign(2em - 20px))");
|
||||
|
||||
test_valid_value("animation-timing-function", "steps(4, start)");
|
||||
test_valid_value("animation-timing-function", "steps(2, end)", "steps(2)");
|
||||
test_valid_value("animation-timing-function", "steps( 2, end )", "steps(2)");
|
||||
test_valid_value("animation-timing-function", "steps(2, jump-start)");
|
||||
test_valid_value("animation-timing-function", "steps(2, jump-end)", "steps(2)");
|
||||
test_valid_value("animation-timing-function", "steps(2, jump-both)");
|
||||
test_valid_value("animation-timing-function", "steps(2, jump-none)");
|
||||
test_valid_value("animation-timing-function", "steps(calc(-10), start)");
|
||||
test_valid_value("animation-timing-function", "steps(calc(5 / 2), start)", "steps(calc(2.5), start)");
|
||||
test_valid_value("animation-timing-function", "steps(calc(1), jump-none)");
|
||||
|
||||
test_valid_value("animation-timing-function", "linear, ease, linear");
|
||||
|
||||
test_valid_value("animation-timing-function", "steps(calc(2 + sign(100em - 1px)))");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue