1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 15:57:45 +00:00

LibWeb: Make sure finished animations are not restarted

This commit makes the StyleComputer avoid restarting finished animations
(e.g. animations with finite iteration counts that have run to
completion).
As a bonus, it also disables the animation timer when all animations
have finished running.
This commit is contained in:
Ali Mohammad Pur 2023-05-27 09:03:58 +03:30 committed by Andreas Kling
parent e90752cc21
commit 2e71263c5c
2 changed files with 21 additions and 4 deletions

View file

@ -1004,7 +1004,7 @@ StyleComputer::AnimationStepTransition StyleComputer::Animation::step(CSS::Time
auto changed_iteration = false; auto changed_iteration = false;
if (new_progress >= 1) { if (new_progress >= 1) {
if (iteration_count.has_value()) { if (iteration_count.has_value()) {
if (iteration_count.value() == 0) { if (iteration_count.value() <= 1) {
progress = CSS::Percentage(100); progress = CSS::Percentage(100);
return AnimationStepTransition::ActiveToAfter; return AnimationStepTransition::ActiveToAfter;
} }
@ -1221,6 +1221,12 @@ void StyleComputer::ensure_animation_timer() const
constexpr static auto timer_delay_ms = 1000 / 60; constexpr static auto timer_delay_ms = 1000 / 60;
if (!m_animation_driver_timer) { if (!m_animation_driver_timer) {
m_animation_driver_timer = Platform::Timer::create_repeating(timer_delay_ms, [this] { m_animation_driver_timer = Platform::Timer::create_repeating(timer_delay_ms, [this] {
// If we run out of animations, stop the timer - it'll turn back on the next time we have an active animation.
if (m_active_animations.is_empty()) {
m_animation_driver_timer->stop();
return;
}
HashTable<AnimationKey> animations_to_remove; HashTable<AnimationKey> animations_to_remove;
HashTable<DOM::Element*> owning_elements_to_invalidate; HashTable<DOM::Element*> owning_elements_to_invalidate;
@ -1242,24 +1248,29 @@ void StyleComputer::ensure_animation_timer() const
break; break;
case AnimationStepTransition::IdleOrBeforeToAfter: case AnimationStepTransition::IdleOrBeforeToAfter:
// FIXME: Dispatch `animationstart` then `animationend`. // FIXME: Dispatch `animationstart` then `animationend`.
m_finished_animations.set(it.key);
break; break;
case AnimationStepTransition::ActiveToBefore: case AnimationStepTransition::ActiveToBefore:
// FIXME: Dispatch `animationend`. // FIXME: Dispatch `animationend`.
m_finished_animations.set(it.key);
break; break;
case AnimationStepTransition::ActiveToActiveChangingTheIteration: case AnimationStepTransition::ActiveToActiveChangingTheIteration:
// FIXME: Dispatch `animationiteration`. // FIXME: Dispatch `animationiteration`.
break; break;
case AnimationStepTransition::ActiveToAfter: case AnimationStepTransition::ActiveToAfter:
// FIXME: Dispatch `animationend`. // FIXME: Dispatch `animationend`.
m_finished_animations.set(it.key);
break; break;
case AnimationStepTransition::AfterToActive: case AnimationStepTransition::AfterToActive:
// FIXME: Dispatch `animationstart`. // FIXME: Dispatch `animationstart`.
break; break;
case AnimationStepTransition::AfterToBefore: case AnimationStepTransition::AfterToBefore:
// FIXME: Dispatch `animationstart` then `animationend`. // FIXME: Dispatch `animationstart` then `animationend`.
m_finished_animations.set(it.key);
break; break;
case AnimationStepTransition::Cancelled: case AnimationStepTransition::Cancelled:
// FIXME: Dispatch `animationcancel`. // FIXME: Dispatch `animationcancel`.
m_finished_animations.set(it.key);
break; break;
} }
if (it.value->is_done()) if (it.value->is_done())
@ -1329,14 +1340,16 @@ ErrorOr<void> StyleComputer::compute_cascaded_values(StyleProperties& style, DOM
// Animation declarations [css-animations-2] // Animation declarations [css-animations-2]
if (auto animation_name = style.maybe_null_property(PropertyID::AnimationName)) { if (auto animation_name = style.maybe_null_property(PropertyID::AnimationName)) {
ensure_animation_timer();
if (auto source_declaration = style.property_source_declaration(PropertyID::AnimationName)) { if (auto source_declaration = style.property_source_declaration(PropertyID::AnimationName)) {
AnimationKey animation_key { AnimationKey animation_key {
.source_declaration = source_declaration, .source_declaration = source_declaration,
.element = &element, .element = &element,
}; };
if (auto name = TRY(animation_name->to_string()); !name.is_empty()) {
if (m_finished_animations.contains(animation_key)) {
// We've already finished going through this animation, so drop it from the active animations.
m_active_animations.remove(animation_key);
} else if (auto name = TRY(animation_name->to_string()); !name.is_empty()) {
auto active_animation = m_active_animations.get(animation_key); auto active_animation = m_active_animations.get(animation_key);
if (!active_animation.has_value()) { if (!active_animation.has_value()) {
// New animation! // New animation!
@ -1376,6 +1389,9 @@ ErrorOr<void> StyleComputer::compute_cascaded_values(StyleProperties& style, DOM
m_active_animations.remove(animation_key); m_active_animations.remove(animation_key);
} }
} }
if (!m_active_animations.is_empty())
ensure_animation_timer();
} }
// Important author declarations // Important author declarations

View file

@ -205,6 +205,7 @@ private:
}; };
mutable HashMap<AnimationKey, NonnullOwnPtr<Animation>> m_active_animations; mutable HashMap<AnimationKey, NonnullOwnPtr<Animation>> m_active_animations;
mutable HashTable<AnimationKey> m_finished_animations;
mutable RefPtr<Platform::Timer> m_animation_driver_timer; mutable RefPtr<Platform::Timer> m_animation_driver_timer;
}; };