Animatable.cpp 4.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. /*
  2. * Copyright (c) 2024, Matthew Olsson <mattco@serenityos.org>.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/QuickSort.h>
  7. #include <LibWeb/Animations/Animatable.h>
  8. #include <LibWeb/Animations/Animation.h>
  9. #include <LibWeb/Animations/DocumentTimeline.h>
  10. #include <LibWeb/DOM/Document.h>
  11. #include <LibWeb/DOM/Element.h>
  12. namespace Web::Animations {
  13. // https://www.w3.org/TR/web-animations-1/#dom-animatable-animate
  14. WebIDL::ExceptionOr<JS::NonnullGCPtr<Animation>> Animatable::animate(Optional<JS::Handle<JS::Object>> keyframes, Variant<Empty, double, KeyframeAnimationOptions> options)
  15. {
  16. // 1. Let target be the object on which this method was called.
  17. JS::NonnullGCPtr target { *static_cast<DOM::Element*>(this) };
  18. auto& realm = target->realm();
  19. // 2. Construct a new KeyframeEffect object, effect, in the relevant Realm of target by using the same procedure as
  20. // the KeyframeEffect(target, keyframes, options) constructor, passing target as the target argument, and the
  21. // keyframes and options arguments as supplied.
  22. //
  23. // If the above procedure causes an exception to be thrown, propagate the exception and abort this procedure.
  24. auto effect = TRY(options.visit(
  25. [&](Empty) { return KeyframeEffect::construct_impl(realm, target, keyframes); },
  26. [&](auto const& value) { return KeyframeEffect::construct_impl(realm, target, keyframes, value); }));
  27. // 3. If options is a KeyframeAnimationOptions object, let timeline be the timeline member of options or, if
  28. // timeline member of options is missing, be the default document timeline of the node document of the element
  29. // on which this method was called.
  30. JS::GCPtr<AnimationTimeline> timeline;
  31. if (options.has<KeyframeAnimationOptions>())
  32. timeline = options.get<KeyframeAnimationOptions>().timeline;
  33. if (!timeline)
  34. timeline = target->document().timeline();
  35. // 4. Construct a new Animation object, animation, in the relevant Realm of target by using the same procedure as
  36. // the Animation() constructor, passing effect and timeline as arguments of the same name.
  37. auto animation = TRY(Animation::construct_impl(realm, effect, timeline));
  38. // 5. If options is a KeyframeAnimationOptions object, assign the value of the id member of options to animation’s
  39. // id attribute.
  40. if (options.has<KeyframeAnimationOptions>())
  41. animation->set_id(options.get<KeyframeAnimationOptions>().id);
  42. // 6. Run the procedure to play an animation for animation with the auto-rewind flag set to true.
  43. TRY(animation->play_an_animation(Animation::AutoRewind::Yes));
  44. // 7. Return animation.
  45. return animation;
  46. }
  47. // https://www.w3.org/TR/web-animations-1/#dom-animatable-getanimations
  48. Vector<JS::NonnullGCPtr<Animation>> Animatable::get_animations(Web::Animations::GetAnimationsOptions options)
  49. {
  50. // Returns the set of relevant animations for this object, or, if an options parameter is passed with subtree set to
  51. // true, returns the set of relevant animations for a subtree for this object.
  52. // The returned list is sorted using the composite order described for the associated animations of effects in
  53. // §5.4.2 The effect stack.
  54. if (!m_is_sorted_by_composite_order) {
  55. quick_sort(m_associated_effects, [](JS::NonnullGCPtr<AnimationEffect>& a, JS::NonnullGCPtr<AnimationEffect>& b) {
  56. auto& a_effect = verify_cast<KeyframeEffect>(*a);
  57. auto& b_effect = verify_cast<KeyframeEffect>(*b);
  58. return KeyframeEffect::composite_order(a_effect, b_effect) < 0;
  59. });
  60. m_is_sorted_by_composite_order = true;
  61. }
  62. // FIXME: Support subtree
  63. (void)options;
  64. Vector<JS::NonnullGCPtr<Animation>> relevant_animations;
  65. for (auto& effect : m_associated_effects) {
  66. if (auto animation = effect->associated_animation(); animation && animation->is_relevant())
  67. relevant_animations.append(*animation);
  68. }
  69. return relevant_animations;
  70. }
  71. void Animatable::associate_with_effect(JS::NonnullGCPtr<AnimationEffect> effect)
  72. {
  73. m_associated_effects.append(effect);
  74. m_is_sorted_by_composite_order = false;
  75. }
  76. void Animatable::disassociate_with_effect(JS::NonnullGCPtr<AnimationEffect> effect)
  77. {
  78. m_associated_effects.remove_first_matching([&](auto element) { return effect == element; });
  79. }
  80. }