소스 검색

LibWeb: Add the AnimationEffect IDL object

Matthew Olsson 1 년 전
부모
커밋
0df06ce273

+ 5 - 1
Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp

@@ -27,6 +27,8 @@ static bool is_platform_object(Type const& type)
     // might simply need to add another type here.
     static constexpr Array types = {
         "AbortSignal"sv,
+        "AnimationEffect"sv,
+        "AnimationTimeline"sv,
         "Attr"sv,
         "AudioTrack"sv,
         "Blob"sv,
@@ -3499,7 +3501,9 @@ void generate_constructor_implementation(IDL::Interface const& interface, String
 #include <LibWeb/Bindings/@prototype_class@.h>
 #include <LibWeb/Bindings/ExceptionOrUtils.h>
 #include <LibWeb/Bindings/Intrinsics.h>
-#if __has_include(<LibWeb/Crypto/@name@.h>)
+#if __has_include(<LibWeb/Animations/@name@.h>)
+#    include <LibWeb/Animations/@name@.h>
+#elif __has_include(<LibWeb/Crypto/@name@.h>)
 #    include <LibWeb/Crypto/@name@.h>
 #elif __has_include(<LibWeb/CSS/@name@.h>)
 #    include <LibWeb/CSS/@name@.h>

+ 165 - 0
Userland/Libraries/LibWeb/Animations/AnimationEffect.cpp

@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Runtime/VM.h>
+#include <LibWeb/Animations/AnimationEffect.h>
+#include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/WebIDL/ExceptionOr.h>
+
+namespace Web::Animations {
+
+JS::NonnullGCPtr<AnimationEffect> AnimationEffect::create(JS::Realm& realm)
+{
+    return realm.heap().allocate<AnimationEffect>(realm, realm);
+}
+
+OptionalEffectTiming EffectTiming::to_optional_effect_timing() const
+{
+    return {
+        .delay = delay,
+        .end_delay = end_delay,
+        .fill = fill,
+        .iteration_start = iteration_start,
+        .iterations = iterations,
+        .duration = duration,
+        .direction = direction,
+        .easing = easing,
+    };
+}
+
+// https://www.w3.org/TR/web-animations-1/#dom-animationeffect-gettiming
+EffectTiming AnimationEffect::get_timing() const
+{
+    // 1. Returns the specified timing properties for this animation effect.
+    return {
+        .delay = m_start_delay,
+        .end_delay = m_end_delay,
+        .fill = m_fill_mode,
+        .iteration_start = m_iteration_start,
+        .iterations = m_iteration_count,
+        .duration = m_iteration_duration,
+        .direction = m_playback_direction,
+        .easing = m_easing_function,
+    };
+}
+
+// https://www.w3.org/TR/web-animations-1/#dom-animationeffect-getcomputedtiming
+ComputedEffectTiming AnimationEffect::get_computed_timing() const
+{
+    // 1. Returns the calculated timing properties for this animation effect.
+
+    // Note: Although some of the attributes of the object returned by getTiming() and getComputedTiming() are common,
+    //       their values may differ in the following ways:
+
+    //     - duration: while getTiming() may return the string auto, getComputedTiming() must return a number
+    //       corresponding to the calculated value of the iteration duration as defined in the description of the
+    //       duration member of the EffectTiming interface.
+    //
+    //       In this level of the specification, that simply means that an auto value is replaced by zero.
+    auto duration = m_iteration_duration.has<String>() ? 0.0 : m_iteration_duration.get<double>();
+
+    //     - fill: likewise, while getTiming() may return the string auto, getComputedTiming() must return the specific
+    //       FillMode used for timing calculations as defined in the description of the fill member of the EffectTiming
+    //       interface.
+    //
+    //       In this level of the specification, that simply means that an auto value is replaced by the none FillMode.
+    auto fill = m_fill_mode == Bindings::FillMode::Auto ? Bindings::FillMode::None : m_fill_mode;
+
+    return {
+        {
+            .delay = m_start_delay,
+            .end_delay = m_end_delay,
+            .fill = fill,
+            .iteration_start = m_iteration_start,
+            .iterations = m_iteration_count,
+            .duration = duration,
+            .direction = m_playback_direction,
+            .easing = m_easing_function,
+        },
+
+        // FIXME:
+        0.0,
+        0.0,
+        0.0,
+        0.0,
+        0.0,
+    };
+}
+
+// https://www.w3.org/TR/web-animations-1/#dom-animationeffect-updatetiming
+// https://www.w3.org/TR/web-animations-1/#update-the-timing-properties-of-an-animation-effect
+WebIDL::ExceptionOr<void> AnimationEffect::update_timing(OptionalEffectTiming timing)
+{
+    // 1. If the iterationStart member of input exists and is less than zero, throw a TypeError and abort this
+    //    procedure.
+    if (timing.iteration_start.has_value() && timing.iteration_start.value() < 0.0)
+        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid iteration start value"sv };
+
+    // 2. If the iterations member of input exists, and is less than zero or is the value NaN, throw a TypeError and
+    //    abort this procedure.
+    if (timing.iterations.has_value() && (timing.iterations.value() < 0.0 || isnan(timing.iterations.value())))
+        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid iteration count value"sv };
+
+    // 3. If the duration member of input exists, and is less than zero or is the value NaN, throw a TypeError and
+    //    abort this procedure.
+    // Note: "auto", the only valid string value, is treated as 0.
+    auto& duration = timing.duration;
+    if (duration.has_value() && duration->has<double>() && (duration->get<double>() < 0.0 || isnan(duration->get<double>())))
+        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid duration value"sv };
+
+    // FIXME:
+    // 4. If the easing member of input exists but cannot be parsed using the <easing-function> production
+    //    [CSS-EASING-1], throw a TypeError and abort this procedure.
+
+    // 5. Assign each member that exists in input to the corresponding timing property of effect as follows:
+
+    //    - delay → start delay
+    if (timing.delay.has_value())
+        m_start_delay = timing.delay.value();
+
+    //    - endDelay → end delay
+    if (timing.end_delay.has_value())
+        m_end_delay = timing.end_delay.value();
+
+    //    - fill → fill mode
+    if (timing.fill.has_value())
+        m_fill_mode = timing.fill.value();
+
+    //    - iterationStart → iteration start
+    if (timing.iteration_start.has_value())
+        m_iteration_start = timing.iteration_start.value();
+
+    //    - iterations → iteration count
+    if (timing.iterations.has_value())
+        m_iteration_count = timing.iterations.value();
+
+    //    - duration → iteration duration
+    if (timing.duration.has_value())
+        m_iteration_duration = timing.duration.value();
+
+    //    - direction → playback direction
+    if (timing.direction.has_value())
+        m_playback_direction = timing.direction.value();
+
+    //    - easing → timing function
+    if (timing.easing.has_value())
+        m_easing_function = timing.easing.value();
+
+    return {};
+}
+
+AnimationEffect::AnimationEffect(JS::Realm& realm)
+    : Bindings::PlatformObject(realm)
+{
+}
+
+void AnimationEffect::initialize(JS::Realm& realm)
+{
+    Base::initialize(realm);
+    set_prototype(&Bindings::ensure_web_prototype<Bindings::AnimationEffectPrototype>(realm, "AnimationEffect"));
+}
+
+}

+ 117 - 0
Userland/Libraries/LibWeb/Animations/AnimationEffect.h

@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Optional.h>
+#include <AK/String.h>
+#include <AK/Variant.h>
+#include <LibWeb/Bindings/AnimationEffectPrototype.h>
+#include <LibWeb/Bindings/PlatformObject.h>
+
+namespace Web::Animations {
+
+// // https://www.w3.org/TR/web-animations-1/#the-effecttiming-dictionaries
+struct OptionalEffectTiming {
+    Optional<double> delay {};
+    Optional<double> end_delay {};
+    Optional<Bindings::FillMode> fill {};
+    Optional<double> iteration_start {};
+    Optional<double> iterations {};
+    Optional<Variant<double, String>> duration;
+    Optional<Bindings::PlaybackDirection> direction {};
+    Optional<String> easing {};
+};
+
+// // https://www.w3.org/TR/web-animations-1/#the-effecttiming-dictionaries
+struct EffectTiming {
+    double delay { 0 };
+    double end_delay { 0 };
+    Bindings::FillMode fill { Bindings::FillMode::Auto };
+    double iteration_start { 0.0 };
+    double iterations { 1.0 };
+    Variant<double, String> duration { "auto"_string };
+    Bindings::PlaybackDirection direction { Bindings::PlaybackDirection::Normal };
+    String easing { "linear"_string };
+
+    OptionalEffectTiming to_optional_effect_timing() const;
+};
+
+// https://www.w3.org/TR/web-animations-1/#the-computedeffecttiming-dictionary
+struct ComputedEffectTiming : public EffectTiming {
+    double end_time;
+    double active_duration;
+    Optional<double> local_time;
+    Optional<double> progress;
+    Optional<double> current_iteration;
+};
+
+// https://www.w3.org/TR/web-animations-1/#the-animationeffect-interface
+class AnimationEffect : public Bindings::PlatformObject {
+    WEB_PLATFORM_OBJECT(AnimationEffect, Bindings::PlatformObject);
+
+public:
+    static JS::NonnullGCPtr<AnimationEffect> create(JS::Realm&);
+
+    EffectTiming get_timing() const;
+    ComputedEffectTiming get_computed_timing() const;
+    WebIDL::ExceptionOr<void> update_timing(OptionalEffectTiming timing = {});
+
+    double start_delay() const { return m_start_delay; }
+    void set_start_delay(double start_delay) { m_start_delay = start_delay; }
+
+    double end_delay() const { return m_end_delay; }
+    void set_end_delay(double end_delay) { m_end_delay = end_delay; }
+
+    Bindings::FillMode fill_mode() const { return m_fill_mode; }
+    void set_fill_mode(Bindings::FillMode fill_mode) { m_fill_mode = fill_mode; }
+
+    double iteration_start() const { return m_iteration_start; }
+    void set_iteration_start(double iteration_start) { m_iteration_start = iteration_start; }
+
+    double iteration_count() const { return m_iteration_count; }
+    void set_iteration_count(double iteration_count) { m_iteration_count = iteration_count; }
+
+    Variant<double, String> const& iteration_duration() const { return m_iteration_duration; }
+    void set_iteration_duration(Variant<double, String> iteration_duration) { m_iteration_duration = move(iteration_duration); }
+
+    Bindings::PlaybackDirection playback_direction() const { return m_playback_direction; }
+    void set_playback_direction(Bindings::PlaybackDirection playback_direction) { m_playback_direction = playback_direction; }
+
+    String const& easing_function() const { return m_easing_function; }
+    void set_easing_function(String easing_function) { m_easing_function = move(easing_function); }
+
+protected:
+    AnimationEffect(JS::Realm&);
+
+    virtual void initialize(JS::Realm&) override;
+
+    // https://www.w3.org/TR/web-animations-1/#start-delay
+    double m_start_delay { 0.0 };
+
+    // https://www.w3.org/TR/web-animations-1/#end-delay
+    double m_end_delay { 0.0 };
+
+    // https://www.w3.org/TR/web-animations-1/#fill-mode
+    Bindings::FillMode m_fill_mode { Bindings::FillMode::Auto };
+
+    // https://www.w3.org/TR/web-animations-1/#iteration-start
+    double m_iteration_start { 0.0 };
+
+    // https://www.w3.org/TR/web-animations-1/#iteration-count
+    double m_iteration_count { 1.0 };
+
+    // https://www.w3.org/TR/web-animations-1/#iteration-duration
+    Variant<double, String> m_iteration_duration { 0.0 };
+
+    // https://www.w3.org/TR/web-animations-1/#playback-direction
+    Bindings::PlaybackDirection m_playback_direction { Bindings::PlaybackDirection::Normal };
+
+    // https://www.w3.org/TR/css-easing-1/#easing-function
+    String m_easing_function { "linear"_string };
+};
+
+}

+ 46 - 0
Userland/Libraries/LibWeb/Animations/AnimationEffect.idl

@@ -0,0 +1,46 @@
+// https://www.w3.org/TR/web-animations-1/#the-effecttiming-dictionaries
+dictionary EffectTiming {
+    double                             delay = 0;
+    double                             endDelay = 0;
+    FillMode                           fill = "auto";
+    double                             iterationStart = 0.0;
+    unrestricted double                iterations = 1.0;
+    (unrestricted double or DOMString) duration = "auto";
+    PlaybackDirection                  direction = "normal";
+    DOMString                          easing = "linear";
+};
+
+// https://www.w3.org/TR/web-animations-1/#dictdef-optionaleffecttiming
+dictionary OptionalEffectTiming {
+    double                             delay;
+    double                             endDelay;
+    FillMode                           fill;
+    double                             iterationStart;
+    unrestricted double                iterations;
+    (unrestricted double or DOMString) duration;
+    PlaybackDirection                  direction;
+    DOMString                          easing;
+};
+
+// https://www.w3.org/TR/web-animations-1/#the-fillmode-enumeration
+enum FillMode { "none", "forwards", "backwards", "both", "auto" };
+
+// https://www.w3.org/TR/web-animations-1/#the-playbackdirection-enumeration
+enum PlaybackDirection { "normal", "reverse", "alternate", "alternate-reverse" };
+
+// https://www.w3.org/TR/web-animations-1/#the-computedeffecttiming-dictionary
+dictionary ComputedEffectTiming : EffectTiming {
+    unrestricted double  endTime;
+    unrestricted double  activeDuration;
+    double?              localTime;
+    double?              progress;
+    unrestricted double? currentIteration;
+};
+
+// https://www.w3.org/TR/web-animations-1/#the-animationeffect-interface
+[Exposed=Window]
+interface AnimationEffect {
+    EffectTiming         getTiming();
+    ComputedEffectTiming getComputedTiming();
+    undefined            updateTiming(optional OptionalEffectTiming timing = {});
+};

+ 1 - 0
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -2,6 +2,7 @@ include(libweb_generators)
 include(accelerated_graphics)
 
 set(SOURCES
+    Animations/AnimationEffect.cpp
     Animations/AnimationTimeline.cpp
     Animations/DocumentTimeline.cpp
     ARIA/AriaData.cpp

+ 1 - 0
Userland/Libraries/LibWeb/Forward.h

@@ -27,6 +27,7 @@ class RecordingPainter;
 }
 
 namespace Web::Animations {
+class AnimationEffect;
 class AnimationTimeline;
 class DocumentTimeline;
 }

+ 1 - 0
Userland/Libraries/LibWeb/idl_files.cmake

@@ -1,6 +1,7 @@
 # This file is included from "Meta/CMake/libweb_data.cmake"
 # It is defined here so that there is no need to go to the Meta directory when adding new idl files
 
+libweb_js_bindings(Animations/AnimationEffect)
 libweb_js_bindings(Animations/AnimationTimeline)
 libweb_js_bindings(Animations/DocumentTimeline)
 libweb_js_bindings(Crypto/Crypto)