mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:17:34 +00:00
LibWeb: Run pending play tasks when the timeline time changes
Note that the timeline time changes every animation frame when the Document sends out animation events
This commit is contained in:
parent
daaaaec2d0
commit
727a9a6472
6 changed files with 101 additions and 13 deletions
|
@ -11,6 +11,7 @@
|
||||||
#include <LibWeb/Animations/DocumentTimeline.h>
|
#include <LibWeb/Animations/DocumentTimeline.h>
|
||||||
#include <LibWeb/Bindings/Intrinsics.h>
|
#include <LibWeb/Bindings/Intrinsics.h>
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
|
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||||
#include <LibWeb/HTML/Window.h>
|
#include <LibWeb/HTML/Window.h>
|
||||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||||
#include <LibWeb/WebIDL/Promise.h>
|
#include <LibWeb/WebIDL/Promise.h>
|
||||||
|
@ -489,6 +490,23 @@ JS::GCPtr<DOM::Document> Animation::document_for_timing() const
|
||||||
return m_timeline->associated_document();
|
return m_timeline->associated_document();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Animation::notify_timeline_time_did_change()
|
||||||
|
{
|
||||||
|
update_finished_state(DidSeek::No, SynchronouslyNotify::Yes);
|
||||||
|
|
||||||
|
// Act on the pending play or pause task
|
||||||
|
if (m_pending_pause_task == TaskState::Scheduled) {
|
||||||
|
m_pending_pause_task = TaskState::None;
|
||||||
|
// FIXME:
|
||||||
|
// run_pending_pause_task();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pending_play_task == TaskState::Scheduled) {
|
||||||
|
m_pending_play_task = TaskState::None;
|
||||||
|
run_pending_play_task();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/web-animations-1/#associated-effect-end
|
// https://www.w3.org/TR/web-animations-1/#associated-effect-end
|
||||||
double Animation::associated_effect_end() const
|
double Animation::associated_effect_end() const
|
||||||
{
|
{
|
||||||
|
@ -725,6 +743,76 @@ void Animation::update_finished_state(DidSeek did_seek, SynchronouslyNotify sync
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Step 12 of https://www.w3.org/TR/web-animations-1/#playing-an-animation-section
|
||||||
|
void Animation::run_pending_play_task()
|
||||||
|
{
|
||||||
|
// 1. Assert that at least one of animation’s start time or hold time is resolved.
|
||||||
|
VERIFY(m_start_time.has_value() || m_hold_time.has_value());
|
||||||
|
|
||||||
|
// 2. Let ready time be the time value of the timeline associated with animation at the moment when animation became
|
||||||
|
// ready.
|
||||||
|
// FIXME: Ideally we would save the time before the update_finished_state call in play_an_animation() and use that
|
||||||
|
// as the ready time, as step 2 indicates, however at that point the timeline will not have had it's current
|
||||||
|
// time updated by the Document, so the time we would save would be incorrect if there are no other
|
||||||
|
// animations running.
|
||||||
|
auto ready_time = m_timeline->current_time();
|
||||||
|
|
||||||
|
// Note: The timeline being active is a precondition for this method to be called
|
||||||
|
VERIFY(ready_time.has_value());
|
||||||
|
|
||||||
|
// 3. Perform the steps corresponding to the first matching condition below, if any:
|
||||||
|
|
||||||
|
// -> If animation’s hold time is resolved,
|
||||||
|
if (m_hold_time.has_value()) {
|
||||||
|
// 1. Apply any pending playback rate on animation.
|
||||||
|
apply_any_pending_playback_rate();
|
||||||
|
|
||||||
|
// 2. Let new start time be the result of evaluating ready time - hold time / playback rate for animation. If
|
||||||
|
// the playback rate is zero, let new start time be simply ready time.
|
||||||
|
auto new_start_time = m_playback_rate != 0.0 ? ready_time.value() - (m_hold_time.value() / m_playback_rate) : ready_time;
|
||||||
|
|
||||||
|
// 3. Set the start time of animation to new start time.
|
||||||
|
m_start_time = new_start_time;
|
||||||
|
|
||||||
|
// 4. If animation’s playback rate is not 0, make animation’s hold time unresolved.
|
||||||
|
if (m_playback_rate != 0.0)
|
||||||
|
m_hold_time = {};
|
||||||
|
}
|
||||||
|
// -> If animation’s start time is resolved and animation has a pending playback rate,
|
||||||
|
else if (m_start_time.has_value() && m_pending_playback_rate.has_value()) {
|
||||||
|
// 1. Let current time to match be the result of evaluating (ready time - start time) × playback rate for
|
||||||
|
// animation.
|
||||||
|
auto current_time_to_match = (ready_time.value() - m_start_time.value()) * m_playback_rate;
|
||||||
|
|
||||||
|
// 2. Apply any pending playback rate on animation.
|
||||||
|
apply_any_pending_playback_rate();
|
||||||
|
|
||||||
|
// 3. If animation’s playback rate is zero, let animation’s hold time be current time to match.
|
||||||
|
if (m_playback_rate == 0.0)
|
||||||
|
m_hold_time = current_time_to_match;
|
||||||
|
|
||||||
|
// 4. Let new start time be the result of evaluating ready time - current time to match / playback rate for
|
||||||
|
// animation. If the playback rate is zero, let new start time be simply ready time.
|
||||||
|
auto new_start_time = m_playback_rate != 0.0 ? ready_time.value() - (current_time_to_match / m_playback_rate) : ready_time;
|
||||||
|
|
||||||
|
// 5. Set the start time of animation to new start time.
|
||||||
|
m_start_time = new_start_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Resolve animation’s current ready promise with animation.
|
||||||
|
HTML::TemporaryExecutionContext execution_context { Bindings::host_defined_environment_settings_object(realm()) };
|
||||||
|
WebIDL::resolve_promise(realm(), current_ready_promise(), this);
|
||||||
|
|
||||||
|
// 5. Run the procedure to update an animation’s finished state for animation with the did seek flag set to false,
|
||||||
|
// and the synchronously notify flag set to false.
|
||||||
|
update_finished_state(DidSeek::No, SynchronouslyNotify::No);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Animation::run_pending_pause_task()
|
||||||
|
{
|
||||||
|
// FIXME: Implement
|
||||||
|
}
|
||||||
|
|
||||||
JS::NonnullGCPtr<WebIDL::Promise> Animation::current_ready_promise() const
|
JS::NonnullGCPtr<WebIDL::Promise> Animation::current_ready_promise() const
|
||||||
{
|
{
|
||||||
if (!m_current_ready_promise) {
|
if (!m_current_ready_promise) {
|
||||||
|
|
|
@ -63,6 +63,7 @@ public:
|
||||||
Optional<double> convert_a_timeline_time_to_an_origin_relative_time(Optional<double>) const;
|
Optional<double> convert_a_timeline_time_to_an_origin_relative_time(Optional<double>) const;
|
||||||
|
|
||||||
JS::GCPtr<DOM::Document> document_for_timing() const;
|
JS::GCPtr<DOM::Document> document_for_timing() const;
|
||||||
|
void notify_timeline_time_did_change();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Animation(JS::Realm&);
|
Animation(JS::Realm&);
|
||||||
|
@ -93,6 +94,9 @@ private:
|
||||||
WebIDL::ExceptionOr<void> silently_set_current_time(Optional<double>);
|
WebIDL::ExceptionOr<void> silently_set_current_time(Optional<double>);
|
||||||
void update_finished_state(DidSeek, SynchronouslyNotify);
|
void update_finished_state(DidSeek, SynchronouslyNotify);
|
||||||
|
|
||||||
|
void run_pending_play_task();
|
||||||
|
void run_pending_pause_task();
|
||||||
|
|
||||||
JS::NonnullGCPtr<WebIDL::Promise> current_ready_promise() const;
|
JS::NonnullGCPtr<WebIDL::Promise> current_ready_promise() const;
|
||||||
JS::NonnullGCPtr<WebIDL::Promise> current_finished_promise() const;
|
JS::NonnullGCPtr<WebIDL::Promise> current_finished_promise() const;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>.
|
* Copyright (c) 2023-2024, Matthew Olsson <mattco@serenityos.org>.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -12,10 +12,10 @@ namespace Web::Animations {
|
||||||
|
|
||||||
JS_DEFINE_ALLOCATOR(AnimationTimeline);
|
JS_DEFINE_ALLOCATOR(AnimationTimeline);
|
||||||
|
|
||||||
WebIDL::ExceptionOr<void> AnimationTimeline::set_current_time(Optional<double> value)
|
void AnimationTimeline::set_current_time(Optional<double> value)
|
||||||
{
|
{
|
||||||
if (value == m_current_time)
|
if (value == m_current_time)
|
||||||
return {};
|
return;
|
||||||
|
|
||||||
if (m_is_monotonically_increasing && m_current_time.has_value()) {
|
if (m_is_monotonically_increasing && m_current_time.has_value()) {
|
||||||
if (!value.has_value() || value.value() < m_current_time.value())
|
if (!value.has_value() || value.value() < m_current_time.value())
|
||||||
|
@ -25,9 +25,7 @@ WebIDL::ExceptionOr<void> AnimationTimeline::set_current_time(Optional<double> v
|
||||||
m_current_time = value;
|
m_current_time = value;
|
||||||
|
|
||||||
for (auto& animation : m_associated_animations)
|
for (auto& animation : m_associated_animations)
|
||||||
TRY(animation->set_current_time(value));
|
animation->notify_timeline_time_did_change();
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationTimeline::set_associated_document(JS::GCPtr<DOM::Document> document)
|
void AnimationTimeline::set_associated_document(JS::GCPtr<DOM::Document> document)
|
||||||
|
|
|
@ -17,7 +17,7 @@ class AnimationTimeline : public Bindings::PlatformObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Optional<double> current_time() const { return m_current_time; }
|
Optional<double> current_time() const { return m_current_time; }
|
||||||
virtual WebIDL::ExceptionOr<void> set_current_time(Optional<double>);
|
virtual void set_current_time(Optional<double>);
|
||||||
|
|
||||||
JS::GCPtr<DOM::Document> associated_document() const { return m_associated_document; }
|
JS::GCPtr<DOM::Document> associated_document() const { return m_associated_document; }
|
||||||
void set_associated_document(JS::GCPtr<DOM::Document>);
|
void set_associated_document(JS::GCPtr<DOM::Document>);
|
||||||
|
|
|
@ -41,21 +41,19 @@ Optional<double> DocumentTimeline::convert_a_timeline_time_to_an_origin_relative
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/web-animations-1/#origin-time
|
// https://www.w3.org/TR/web-animations-1/#origin-time
|
||||||
WebIDL::ExceptionOr<void> DocumentTimeline::set_current_time(Optional<double> current_time)
|
void DocumentTimeline::set_current_time(Optional<double> current_time)
|
||||||
{
|
{
|
||||||
// A document timeline is a type of timeline that is associated with a document and whose current time is calculated
|
// A document timeline is a type of timeline that is associated with a document and whose current time is calculated
|
||||||
// as a fixed offset from the now timestamp provided each time the update animations and send events procedure is
|
// as a fixed offset from the now timestamp provided each time the update animations and send events procedure is
|
||||||
// run. This fixed offset is referred to as the document timeline’s origin time.
|
// run. This fixed offset is referred to as the document timeline’s origin time.
|
||||||
if (!current_time.has_value())
|
if (!current_time.has_value())
|
||||||
TRY(Base::set_current_time({}));
|
Base::set_current_time({});
|
||||||
else
|
else
|
||||||
TRY(Base::set_current_time(current_time.value() - m_origin_time));
|
Base::set_current_time(current_time.value() - m_origin_time);
|
||||||
|
|
||||||
// After a document timeline becomes active, it is monotonically increasing.
|
// After a document timeline becomes active, it is monotonically increasing.
|
||||||
if (!is_inactive())
|
if (!is_inactive())
|
||||||
VERIFY(is_monotonically_increasing());
|
VERIFY(is_monotonically_increasing());
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/web-animations-1/#document-timelines
|
// https://www.w3.org/TR/web-animations-1/#document-timelines
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
static JS::NonnullGCPtr<DocumentTimeline> create(JS::Realm&, DOM::Document&, HighResolutionTime::DOMHighResTimeStamp origin_time);
|
static JS::NonnullGCPtr<DocumentTimeline> create(JS::Realm&, DOM::Document&, HighResolutionTime::DOMHighResTimeStamp origin_time);
|
||||||
static WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentTimeline>> construct_impl(JS::Realm&, DocumentTimelineOptions options = {});
|
static WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentTimeline>> construct_impl(JS::Realm&, DocumentTimelineOptions options = {});
|
||||||
|
|
||||||
virtual WebIDL::ExceptionOr<void> set_current_time(Optional<double> current_time) override;
|
virtual void set_current_time(Optional<double> current_time) override;
|
||||||
virtual bool is_inactive() const override;
|
virtual bool is_inactive() const override;
|
||||||
|
|
||||||
virtual Optional<double> convert_a_timeline_time_to_an_origin_relative_time(Optional<double>) override;
|
virtual Optional<double> convert_a_timeline_time_to_an_origin_relative_time(Optional<double>) override;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue