diff --git a/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp index 03a3a13cfc..9639cf8f1f 100644 --- a/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp +++ b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp @@ -543,6 +543,45 @@ static WebIDL::ExceptionOr> 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 keyframe_set, HashTable 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::create(JS::Realm& realm) { return realm.heap().allocate(realm, realm); @@ -737,6 +776,25 @@ WebIDL::ExceptionOr KeyframeEffect::set_keyframes(Optional animated_properties; + + for (auto& keyframe : m_keyframes) { + Animations::KeyframeEffect::KeyFrameSet::ResolvedKeyFrame resolved_keyframe; + + auto key = static_cast(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 {}; } diff --git a/Userland/Libraries/LibWeb/Animations/KeyframeEffect.h b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.h index 191eb4c798..038a9194dd 100644 --- a/Userland/Libraries/LibWeb/Animations/KeyframeEffect.h +++ b/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 { struct UseInitial { }; struct ResolvedKeyFrame { @@ -65,6 +67,7 @@ public: }; RedBlackTree keyframes_by_key; }; + static void generate_initial_and_final_frames(RefPtr, HashTable const& animated_properties); static JS::NonnullGCPtr create(JS::Realm&);