Kaynağa Gözat

LibWeb: Generate KeyframeSet in KeyframeEffect::set_keyframes

This is similar to the logic used in StyleComputer (except a bit closer
to the spec), and will eventually be shared between the two.
Matthew Olsson 1 yıl önce
ebeveyn
işleme
1d98f812af

+ 58 - 0
Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp

@@ -543,6 +543,45 @@ static WebIDL::ExceptionOr<Vector<BaseKeyframe>> process_a_keyframes_argument(JS
     return processed_keyframes;
 }
 
+// https://www.w3.org/TR/css-animations-2/#keyframe-processing
+void KeyframeEffect::generate_initial_and_final_frames(RefPtr<KeyFrameSet> keyframe_set, HashTable<CSS::PropertyID> const& animated_properties)
+{
+    // 1. Find or create the initial keyframe, a keyframe with a keyframe offset of 0%, default timing function
+    //    as its keyframe timing function, and default composite as its keyframe composite.
+    KeyFrameSet::ResolvedKeyFrame* initial_keyframe;
+    if (auto existing_keyframe = keyframe_set->keyframes_by_key.find(0)) {
+        initial_keyframe = existing_keyframe;
+    } else {
+        keyframe_set->keyframes_by_key.insert(0, {});
+        initial_keyframe = keyframe_set->keyframes_by_key.find(0);
+    }
+
+    // 2. For any property in animated properties that is not otherwise present in a keyframe with an offset of
+    //    0% or one that would be positioned earlier in the used keyframe order, add the computed value of that
+    //    property on element to initial keyframe’s keyframe values.
+    for (auto property : animated_properties) {
+        if (!initial_keyframe->resolved_properties.contains(property))
+            initial_keyframe->resolved_properties.set(property, KeyFrameSet::UseInitial {});
+    }
+
+    // 3. If initial keyframe’s keyframe values is not empty, prepend initial keyframe to keyframes.
+
+    // 4. Repeat for final keyframe, using an offset of 100%, considering keyframes positioned later in the used
+    //    keyframe order, and appending to keyframes.
+    KeyFrameSet::ResolvedKeyFrame* final_keyframe;
+    if (auto existing_keyframe = keyframe_set->keyframes_by_key.find(100 * AnimationKeyFrameKeyScaleFactor)) {
+        final_keyframe = existing_keyframe;
+    } else {
+        keyframe_set->keyframes_by_key.insert(100 * AnimationKeyFrameKeyScaleFactor, {});
+        final_keyframe = keyframe_set->keyframes_by_key.find(100 * AnimationKeyFrameKeyScaleFactor);
+    }
+
+    for (auto property : animated_properties) {
+        if (!final_keyframe->resolved_properties.contains(property))
+            final_keyframe->resolved_properties.set(property, KeyFrameSet::UseInitial {});
+    }
+}
+
 JS::NonnullGCPtr<KeyframeEffect> KeyframeEffect::create(JS::Realm& realm)
 {
     return realm.heap().allocate<KeyframeEffect>(realm, realm);
@@ -737,6 +776,25 @@ WebIDL::ExceptionOr<void> KeyframeEffect::set_keyframes(Optional<JS::Handle<JS::
     //        missing keyframe offsets
     compute_missing_keyframe_offsets(m_keyframes);
 
+    auto keyframe_set = adopt_ref(*new KeyFrameSet);
+    HashTable<CSS::PropertyID> animated_properties;
+
+    for (auto& keyframe : m_keyframes) {
+        Animations::KeyframeEffect::KeyFrameSet::ResolvedKeyFrame resolved_keyframe;
+
+        auto key = static_cast<u64>(keyframe.computed_offset.value() * 100 * AnimationKeyFrameKeyScaleFactor);
+
+        for (auto const& [property_id, property_value] : keyframe.parsed_properties()) {
+            animated_properties.set(property_id);
+            resolved_keyframe.resolved_properties.set(property_id, property_value);
+        }
+
+        keyframe_set->keyframes_by_key.insert(key, resolved_keyframe);
+    }
+
+    generate_initial_and_final_frames(keyframe_set, animated_properties);
+    m_key_frame_set = keyframe_set;
+
     return {};
 }
 

+ 3 - 0
Userland/Libraries/LibWeb/Animations/KeyframeEffect.h

@@ -58,6 +58,8 @@ class KeyframeEffect : public AnimationEffect {
     JS_DECLARE_ALLOCATOR(KeyframeEffect);
 
 public:
+    constexpr static double AnimationKeyFrameKeyScaleFactor = 1000.0; // 0..100000
+
     struct KeyFrameSet : public RefCounted<KeyFrameSet> {
         struct UseInitial { };
         struct ResolvedKeyFrame {
@@ -65,6 +67,7 @@ public:
         };
         RedBlackTree<u64, ResolvedKeyFrame> keyframes_by_key;
     };
+    static void generate_initial_and_final_frames(RefPtr<KeyFrameSet>, HashTable<CSS::PropertyID> const& animated_properties);
 
     static JS::NonnullGCPtr<KeyframeEffect> create(JS::Realm&);