2023-11-05 10:20:10 -07:00
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2024, Matthew Olsson <mattco@serenityos.org>.
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
2024-02-03 12:17:29 -07:00
|
|
|
|
#include <AK/QuickSort.h>
|
2023-11-05 10:20:10 -07:00
|
|
|
|
#include <LibWeb/Animations/Animatable.h>
|
2024-02-03 12:16:31 -07:00
|
|
|
|
#include <LibWeb/Animations/Animation.h>
|
|
|
|
|
#include <LibWeb/Animations/DocumentTimeline.h>
|
|
|
|
|
#include <LibWeb/DOM/Document.h>
|
|
|
|
|
#include <LibWeb/DOM/Element.h>
|
2023-11-05 10:20:10 -07:00
|
|
|
|
|
|
|
|
|
namespace Web::Animations {
|
|
|
|
|
|
|
|
|
|
// https://www.w3.org/TR/web-animations-1/#dom-animatable-animate
|
|
|
|
|
WebIDL::ExceptionOr<JS::NonnullGCPtr<Animation>> Animatable::animate(Optional<JS::Handle<JS::Object>> keyframes, Variant<Empty, double, KeyframeAnimationOptions> options)
|
|
|
|
|
{
|
2024-02-03 12:16:31 -07:00
|
|
|
|
// 1. Let target be the object on which this method was called.
|
|
|
|
|
JS::NonnullGCPtr target { *static_cast<DOM::Element*>(this) };
|
|
|
|
|
auto& realm = target->realm();
|
|
|
|
|
|
|
|
|
|
// 2. Construct a new KeyframeEffect object, effect, in the relevant Realm of target by using the same procedure as
|
|
|
|
|
// the KeyframeEffect(target, keyframes, options) constructor, passing target as the target argument, and the
|
|
|
|
|
// keyframes and options arguments as supplied.
|
|
|
|
|
//
|
|
|
|
|
// If the above procedure causes an exception to be thrown, propagate the exception and abort this procedure.
|
|
|
|
|
auto effect = TRY(options.visit(
|
|
|
|
|
[&](Empty) { return KeyframeEffect::construct_impl(realm, target, keyframes); },
|
|
|
|
|
[&](auto const& value) { return KeyframeEffect::construct_impl(realm, target, keyframes, value); }));
|
|
|
|
|
|
|
|
|
|
// 3. If options is a KeyframeAnimationOptions object, let timeline be the timeline member of options or, if
|
|
|
|
|
// timeline member of options is missing, be the default document timeline of the node document of the element
|
|
|
|
|
// on which this method was called.
|
2024-05-27 07:17:29 -07:00
|
|
|
|
Optional<JS::GCPtr<AnimationTimeline>> timeline;
|
2024-02-03 12:16:31 -07:00
|
|
|
|
if (options.has<KeyframeAnimationOptions>())
|
|
|
|
|
timeline = options.get<KeyframeAnimationOptions>().timeline;
|
2024-05-27 07:17:29 -07:00
|
|
|
|
if (!timeline.has_value())
|
2024-02-03 12:16:31 -07:00
|
|
|
|
timeline = target->document().timeline();
|
|
|
|
|
|
|
|
|
|
// 4. Construct a new Animation object, animation, in the relevant Realm of target by using the same procedure as
|
|
|
|
|
// the Animation() constructor, passing effect and timeline as arguments of the same name.
|
2024-05-27 07:17:29 -07:00
|
|
|
|
auto animation = TRY(Animation::construct_impl(realm, effect, move(timeline)));
|
2024-02-03 12:16:31 -07:00
|
|
|
|
|
|
|
|
|
// 5. If options is a KeyframeAnimationOptions object, assign the value of the id member of options to animation’s
|
|
|
|
|
// id attribute.
|
|
|
|
|
if (options.has<KeyframeAnimationOptions>())
|
|
|
|
|
animation->set_id(options.get<KeyframeAnimationOptions>().id);
|
|
|
|
|
|
|
|
|
|
// 6. Run the procedure to play an animation for animation with the auto-rewind flag set to true.
|
|
|
|
|
TRY(animation->play_an_animation(Animation::AutoRewind::Yes));
|
|
|
|
|
|
|
|
|
|
// 7. Return animation.
|
|
|
|
|
return animation;
|
2023-11-05 10:20:10 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://www.w3.org/TR/web-animations-1/#dom-animatable-getanimations
|
2024-05-28 04:19:50 -07:00
|
|
|
|
Vector<JS::NonnullGCPtr<Animation>> Animatable::get_animations(GetAnimationsOptions options)
|
2023-11-05 10:20:10 -07:00
|
|
|
|
{
|
2024-02-03 12:17:29 -07:00
|
|
|
|
// Returns the set of relevant animations for this object, or, if an options parameter is passed with subtree set to
|
|
|
|
|
// true, returns the set of relevant animations for a subtree for this object.
|
|
|
|
|
|
|
|
|
|
// The returned list is sorted using the composite order described for the associated animations of effects in
|
|
|
|
|
// §5.4.2 The effect stack.
|
|
|
|
|
if (!m_is_sorted_by_composite_order) {
|
2024-03-07 11:11:27 -07:00
|
|
|
|
quick_sort(m_associated_animations, [](JS::NonnullGCPtr<Animation>& a, JS::NonnullGCPtr<Animation>& b) {
|
|
|
|
|
auto& a_effect = verify_cast<KeyframeEffect>(*a->effect());
|
|
|
|
|
auto& b_effect = verify_cast<KeyframeEffect>(*b->effect());
|
2024-02-03 12:17:29 -07:00
|
|
|
|
return KeyframeEffect::composite_order(a_effect, b_effect) < 0;
|
|
|
|
|
});
|
|
|
|
|
m_is_sorted_by_composite_order = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector<JS::NonnullGCPtr<Animation>> relevant_animations;
|
2024-03-07 11:11:27 -07:00
|
|
|
|
for (auto const& animation : m_associated_animations) {
|
|
|
|
|
if (animation->is_relevant())
|
2024-02-03 12:17:29 -07:00
|
|
|
|
relevant_animations.append(*animation);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 04:19:50 -07:00
|
|
|
|
if (options.subtree) {
|
|
|
|
|
JS::NonnullGCPtr target { *static_cast<DOM::Element*>(this) };
|
|
|
|
|
target->for_each_child_of_type<DOM::Element>([&](auto& child) {
|
|
|
|
|
relevant_animations.extend(child.get_animations(options));
|
|
|
|
|
return IterationDecision::Continue;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-03 12:17:29 -07:00
|
|
|
|
return relevant_animations;
|
2023-11-05 10:20:10 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-07 11:11:27 -07:00
|
|
|
|
void Animatable::associate_with_animation(JS::NonnullGCPtr<Animation> animation)
|
2024-02-03 12:10:44 -07:00
|
|
|
|
{
|
2024-03-07 11:11:27 -07:00
|
|
|
|
m_associated_animations.append(animation);
|
2024-02-03 12:17:29 -07:00
|
|
|
|
m_is_sorted_by_composite_order = false;
|
2024-02-03 12:10:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-07 11:11:27 -07:00
|
|
|
|
void Animatable::disassociate_with_animation(JS::NonnullGCPtr<Animation> animation)
|
2024-02-03 12:10:44 -07:00
|
|
|
|
{
|
2024-03-07 11:11:27 -07:00
|
|
|
|
m_associated_animations.remove_first_matching([&](auto element) { return animation == element; });
|
2024-02-03 12:10:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-07 09:31:52 -07:00
|
|
|
|
void Animatable::visit_edges(JS::Cell::Visitor& visitor)
|
|
|
|
|
{
|
2024-04-15 13:58:21 +02:00
|
|
|
|
visitor.visit(m_associated_animations);
|
2024-03-07 09:31:52 -07:00
|
|
|
|
visitor.visit(m_cached_animation_name_source);
|
|
|
|
|
visitor.visit(m_cached_animation_name_animation);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-05 10:20:10 -07:00
|
|
|
|
}
|