LibWeb: Fix glitchy CSS transitions

`start_needed_transitions()` decides which animations need to be started
based on previous and current style property values. Before this change,
we were using the style value without animations applied as the
"current" value. This caused issues such as starting a new transition
from the animation’s end value when an ongoing animation was
interrupted.
This commit is contained in:
Aliaksandr Kalenik 2025-05-28 15:37:53 +02:00 committed by Tim Ledbetter
commit 36a255eedd
Notes: github-actions[bot] 2025-05-28 16:37:07 +00:00
3 changed files with 4 additions and 15 deletions

View file

@ -150,14 +150,4 @@ double CSSTransition::timing_function_output_at_time(double t) const
return m_keyframe_effect->timing_function().evaluate_at(progress, before_flag);
}
NonnullRefPtr<CSSStyleValue const> CSSTransition::value_at_time(double t, AllowDiscrete allow_discrete) const
{
// https://drafts.csswg.org/css-transitions/#application
auto progress = timing_function_output_at_time(t);
auto result = interpolate_property(*m_keyframe_effect->target(), m_transition_property, m_start_value, m_end_value, progress, allow_discrete);
if (result)
return result.release_nonnull();
return m_start_value;
}
}

View file

@ -38,7 +38,6 @@ public:
double reversing_shortening_factor() const { return m_reversing_shortening_factor; }
double timing_function_output_at_time(double t) const;
NonnullRefPtr<CSSStyleValue const> value_at_time(double t, AllowDiscrete allow_discrete) const;
// This is designed to be created from AnimationEffect::Phase.
enum class Phase : u8 {

View file

@ -1475,7 +1475,7 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_
for (auto i = to_underlying(CSS::first_longhand_property_id); i <= to_underlying(CSS::last_longhand_property_id); ++i) {
auto property_id = static_cast<CSS::PropertyID>(i);
auto matching_transition_properties = element.property_transition_attributes(property_id);
auto const& before_change_value = previous_style.property(property_id, ComputedProperties::WithAnimationsApplied::No);
auto const& before_change_value = previous_style.property(property_id, ComputedProperties::WithAnimationsApplied::Yes);
auto const& after_change_value = new_style.property(property_id, ComputedProperties::WithAnimationsApplied::No);
auto existing_transition = element.property_transition(property_id);
@ -1488,7 +1488,7 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_
auto transition = CSSTransition::start_a_transition(element, property_id, document().transition_generation(),
start_time, end_time, start_value, end_value, reversing_adjusted_start_value, reversing_shortening_factor);
// Immediately set the property's value to the transition's current value, to prevent single-frame jumps.
new_style.set_animated_property(property_id, transition->value_at_time(style_change_event_time, matching_transition_properties->transition_behavior == TransitionBehavior::AllowDiscrete ? AllowDiscrete::Yes : AllowDiscrete::No));
collect_animation_into(element, {}, as<Animations::KeyframeEffect>(*transition->effect()), new_style, AnimationRefresh::No);
};
// 1. If all of the following are true:
@ -1560,8 +1560,8 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_
// 1. If the current value of the property in the running transition is equal to the value of the property in the after-change style,
// or if these two values are not transitionable,
// then implementations must cancel the running transition.
auto current_value = existing_transition->value_at_time(style_change_event_time, matching_transition_properties->transition_behavior == TransitionBehavior::AllowDiscrete ? AllowDiscrete::Yes : AllowDiscrete::No);
if (current_value->equals(after_change_value) || !property_values_are_transitionable(property_id, current_value, after_change_value, element, matching_transition_properties->transition_behavior)) {
auto& current_value = new_style.property(property_id, ComputedProperties::WithAnimationsApplied::Yes);
if (current_value.equals(after_change_value) || !property_values_are_transitionable(property_id, current_value, after_change_value, element, matching_transition_properties->transition_behavior)) {
dbgln_if(CSS_TRANSITIONS_DEBUG, "Transition step 4.1");
existing_transition->cancel();
}