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.
This commit is contained in:
Matthew Olsson 2024-02-03 12:51:15 -07:00 committed by Andreas Kling
parent 1735f3d9aa
commit 1d98f812af
Notes: sideshowbarker 2024-07-16 22:54:10 +09:00
2 changed files with 61 additions and 0 deletions

View File

@ -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 keyframes 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 keyframes 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 {};
}

View File

@ -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&);