diff --git a/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp index 69bfe0f205..435842cfe9 100644 --- a/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp +++ b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp @@ -195,6 +195,69 @@ static WebIDL::ExceptionOr> process_a_keyframe_like_object(JS:: return keyframe_output; } +// https://www.w3.org/TR/web-animations-1/#compute-missing-keyframe-offsets +[[maybe_unused]] static void compute_missing_keyframe_offsets(Vector& keyframes) +{ + // 1. For each keyframe, in keyframes, let the computed keyframe offset of the keyframe be equal to its keyframe + // offset value. + for (auto& keyframe : keyframes) + keyframe.computed_offset = keyframe.offset; + + // 2. If keyframes contains more than one keyframe and the computed keyframe offset of the first keyframe in + // keyframes is null, set the computed keyframe offset of the first keyframe to 0. + if (keyframes.size() > 1 && !keyframes[0].computed_offset.has_value()) + keyframes[0].computed_offset = 0.0; + + // 3. If the computed keyframe offset of the last keyframe in keyframes is null, set its computed keyframe offset + // to 1. + if (!keyframes.is_empty() && !keyframes.last().computed_offset.has_value()) + keyframes.last().computed_offset = 1.0; + + // 4. For each pair of keyframes A and B where: + // - A appears before B in keyframes, and + // - A and B have a computed keyframe offset that is not null, and + // - all keyframes between A and B have a null computed keyframe offset, + auto find_next_index_of_keyframe_with_computed_offset = [&](size_t starting_index) -> Optional { + for (size_t index = starting_index; index < keyframes.size(); index++) { + if (keyframes[index].computed_offset.has_value()) + return index; + } + + return {}; + }; + + auto maybe_index_a = find_next_index_of_keyframe_with_computed_offset(0); + if (!maybe_index_a.has_value()) + return; + + auto index_a = maybe_index_a.value(); + auto maybe_index_b = find_next_index_of_keyframe_with_computed_offset(index_a + 1); + + while (maybe_index_b.has_value()) { + auto index_b = maybe_index_b.value(); + + // calculate the computed keyframe offset of each keyframe between A and B as follows: + for (size_t keyframe_index = index_a + 1; keyframe_index < index_b; keyframe_index++) { + // 1. Let offsetk be the computed keyframe offset of a keyframe k. + auto offset_a = keyframes[index_a].computed_offset.value(); + auto offset_b = keyframes[index_b].computed_offset.value(); + + // 2. Let n be the number of keyframes between and including A and B minus 1. + auto n = static_cast(index_b - index_a); + + // 3. Let index refer to the position of keyframe in the sequence of keyframes between A and B such that the + // first keyframe after A has an index of 1. + auto index = static_cast(keyframe_index - index_a); + + // 4. Set the computed keyframe offset of keyframe to offsetA + (offsetB − offsetA) × index / n. + keyframes[keyframe_index].computed_offset = (offset_a + (offset_b - offset_a)) * index / n; + } + + index_a = index_b; + maybe_index_b = find_next_index_of_keyframe_with_computed_offset(index_b + 1); + } +} + JS::NonnullGCPtr KeyframeEffect::create(JS::Realm& realm) { return realm.heap().allocate(realm, realm);