Files
ladybird/Libraries/LibWeb/Animations/AnimationTimeline.cpp
Callum Law 34f0eea89a LibWeb: Update animations in AnimationTimeline::set_current_time
These steps need to be run whenever update_current_time is called and in
a future commit that will be more than just the one place currently.

This also removes the early return in `set_current_time` if the new
`current time` is the same as the old one, since we want to update
animations regardless (e.g. to run pending tasks)
2026-05-08 22:59:16 +02:00

103 lines
4.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (c) 2023-2024, Matthew Olsson <mattco@serenityos.org>.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Animations/Animation.h>
#include <LibWeb/Animations/AnimationTimeline.h>
#include <LibWeb/Bindings/AnimationTimeline.h>
#include <LibWeb/DOM/Document.h>
namespace Web::Animations {
GC_DEFINE_ALLOCATOR(AnimationTimeline);
// https://drafts.csswg.org/web-animations-1/#dom-animationtimeline-currenttime
Optional<TimeValue> AnimationTimeline::current_time() const
{
// Returns the current time for this timeline or null if this timeline is inactive.
if (is_inactive())
return {};
return m_current_time;
}
void AnimationTimeline::set_current_time(Optional<TimeValue> value)
{
if (m_is_monotonically_increasing && m_current_time.has_value() && (!value.has_value() || *value < *m_current_time)) {
dbgln("AnimationTimeline::set_current_time({}): monotonically increasing timeline can only move forward", value);
return;
}
m_current_time = value;
update_associated_animations_and_dispatch_events();
}
void AnimationTimeline::update_associated_animations_and_dispatch_events()
{
// https://drafts.csswg.org/web-animations-1/#animation-frame-loop
// Note: Due to the hierarchical nature of the timing model, updating the current time of a timeline also involves:
// - Updating the current time of any animations associated with the timeline.
// - Running the update an animation's finished state procedure for any animations whose current time has been
// updated.
// - Queueing animation events for any such animations.
for (auto& animation : m_associated_animations)
animation.update();
auto animations = GC::RootVector<GC::Ref<Animations::Animation>> { heap() };
for (auto& animation : m_associated_animations)
animations.append(animation);
for (auto& animation : animations)
m_associated_document->dispatch_events_for_animation_if_necessary(animation);
}
// https://drafts.csswg.org/web-animations-2/#timeline-duration
NullableCSSNumberish AnimationTimeline::duration_for_bindings() const
{
// The duration of a timeline gives the maximum value a timeline may generate for its current time. This value is
// used to calculate the intrinsic iteration duration for the target effect of an animation that is associated with
// the timeline when the effects iteration duration is "auto". The value is computed such that the effect fills the
// available time. For a monotonic timeline, there is no upper bound on current time, and timeline duration is
// unresolved. For a non-monotonic (e.g. scroll) timeline, the duration has a fixed upper bound. In this case, the
// timeline is a progress-based timeline, and its timeline duration is 100%.
return NullableCSSNumberish::from_optional_css_numberish_time(realm(), duration());
}
// https://drafts.csswg.org/web-animations-1/#timeline
bool AnimationTimeline::is_inactive() const
{
// A timeline is considered to be inactive when its time value is unresolved, and active otherwise.
return !m_current_time.has_value();
}
AnimationTimeline::AnimationTimeline(JS::Realm& realm, GC::Ref<DOM::Document> document)
: Bindings::PlatformObject(realm)
, m_associated_document(document)
{
}
void AnimationTimeline::finalize()
{
Base::finalize();
m_associated_document->disassociate_with_timeline(*this);
}
void AnimationTimeline::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(AnimationTimeline);
Base::initialize(realm);
m_associated_document->associate_with_timeline(*this);
}
void AnimationTimeline::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_associated_document);
// We intentionally don't visit m_associated_animations here to avoid keeping Animations alive solely because they
// are associated with a timeline. Animations are disassociated from timelines in Animation::finalize() so we don't
// need to worry about dangling references.
}
}