mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-08 09:09:43 +00:00
LibWeb: Implement CSS transitions support for pseudo elements
We already had all necessary things for pseudo elements support in place except ability to save transition properties in Animatable. This commit adds the missing part.
This commit is contained in:
parent
9d0c307341
commit
3178679f0b
Notes:
github-actions[bot]
2025-05-30 13:49:19 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: 3178679f0b
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4905
5 changed files with 136 additions and 93 deletions
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, Matthew Olsson <mattco@serenityos.org>
|
* Copyright (c) 2024, Matthew Olsson <mattco@serenityos.org>
|
||||||
* Copyright (c) 2024, Sam Atkins <sam@ladybird.org>
|
* Copyright (c) 2024, Sam Atkins <sam@ladybird.org>
|
||||||
|
* Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -19,6 +20,15 @@
|
||||||
|
|
||||||
namespace Web::Animations {
|
namespace Web::Animations {
|
||||||
|
|
||||||
|
struct Animatable::Transition {
|
||||||
|
HashMap<CSS::PropertyID, size_t> transition_attribute_indices;
|
||||||
|
Vector<TransitionAttributes> transition_attributes;
|
||||||
|
GC::Ptr<CSS::CSSStyleDeclaration const> cached_transition_property_source;
|
||||||
|
HashMap<CSS::PropertyID, GC::Ref<CSS::CSSTransition>> associated_transitions;
|
||||||
|
};
|
||||||
|
|
||||||
|
Animatable::Impl::~Impl() = default;
|
||||||
|
|
||||||
// https://www.w3.org/TR/web-animations-1/#dom-animatable-animate
|
// https://www.w3.org/TR/web-animations-1/#dom-animatable-animate
|
||||||
WebIDL::ExceptionOr<GC::Ref<Animation>> Animatable::animate(Optional<GC::Root<JS::Object>> keyframes, Variant<Empty, double, KeyframeAnimationOptions> options)
|
WebIDL::ExceptionOr<GC::Ref<Animation>> Animatable::animate(Optional<GC::Root<JS::Object>> keyframes, Variant<Empty, double, KeyframeAnimationOptions> options)
|
||||||
{
|
{
|
||||||
|
@ -128,86 +138,100 @@ void Animatable::disassociate_with_animation(GC::Ref<Animation> animation)
|
||||||
impl.associated_animations.remove_first_matching([&](auto element) { return animation == element; });
|
impl.associated_animations.remove_first_matching([&](auto element) { return animation == element; });
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animatable::add_transitioned_properties(Vector<Vector<CSS::PropertyID>> properties, CSS::StyleValueVector delays, CSS::StyleValueVector durations, CSS::StyleValueVector timing_functions, CSS::StyleValueVector transition_behaviors)
|
void Animatable::add_transitioned_properties(Optional<CSS::PseudoElement> pseudo_element, Vector<Vector<CSS::PropertyID>> properties, CSS::StyleValueVector delays, CSS::StyleValueVector durations, CSS::StyleValueVector timing_functions, CSS::StyleValueVector transition_behaviors)
|
||||||
{
|
{
|
||||||
auto& impl = ensure_impl();
|
|
||||||
|
|
||||||
VERIFY(properties.size() == delays.size());
|
VERIFY(properties.size() == delays.size());
|
||||||
VERIFY(properties.size() == durations.size());
|
VERIFY(properties.size() == durations.size());
|
||||||
VERIFY(properties.size() == timing_functions.size());
|
VERIFY(properties.size() == timing_functions.size());
|
||||||
VERIFY(properties.size() == transition_behaviors.size());
|
VERIFY(properties.size() == transition_behaviors.size());
|
||||||
|
|
||||||
|
auto* maybe_transition = ensure_transition(pseudo_element);
|
||||||
|
if (!maybe_transition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto& transition = *maybe_transition;
|
||||||
for (size_t i = 0; i < properties.size(); i++) {
|
for (size_t i = 0; i < properties.size(); i++) {
|
||||||
size_t index_of_this_transition = impl.transition_attributes.size();
|
size_t index_of_this_transition = transition.transition_attributes.size();
|
||||||
auto delay = delays[i]->is_time() ? delays[i]->as_time().time().to_milliseconds() : 0;
|
auto delay = delays[i]->is_time() ? delays[i]->as_time().time().to_milliseconds() : 0;
|
||||||
auto duration = durations[i]->is_time() ? durations[i]->as_time().time().to_milliseconds() : 0;
|
auto duration = durations[i]->is_time() ? durations[i]->as_time().time().to_milliseconds() : 0;
|
||||||
auto timing_function = timing_functions[i]->is_easing() ? timing_functions[i]->as_easing().function() : CSS::EasingStyleValue::CubicBezier::ease();
|
auto timing_function = timing_functions[i]->is_easing() ? timing_functions[i]->as_easing().function() : CSS::EasingStyleValue::CubicBezier::ease();
|
||||||
auto transition_behavior = CSS::keyword_to_transition_behavior(transition_behaviors[i]->to_keyword()).value_or(CSS::TransitionBehavior::Normal);
|
auto transition_behavior = CSS::keyword_to_transition_behavior(transition_behaviors[i]->to_keyword()).value_or(CSS::TransitionBehavior::Normal);
|
||||||
VERIFY(timing_functions[i]->is_easing());
|
VERIFY(timing_functions[i]->is_easing());
|
||||||
impl.transition_attributes.empend(delay, duration, timing_function, transition_behavior);
|
transition.transition_attributes.empend(delay, duration, timing_function, transition_behavior);
|
||||||
|
|
||||||
for (auto const& property : properties[i])
|
for (auto const& property : properties[i])
|
||||||
impl.transition_attribute_indices.set(property, index_of_this_transition);
|
transition.transition_attribute_indices.set(property, index_of_this_transition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<Animatable::TransitionAttributes const&> Animatable::property_transition_attributes(CSS::PropertyID property) const
|
Optional<Animatable::TransitionAttributes const&> Animatable::property_transition_attributes(Optional<CSS::PseudoElement> pseudo_element, CSS::PropertyID property) const
|
||||||
{
|
{
|
||||||
if (!m_impl)
|
auto* maybe_transition = ensure_transition(pseudo_element);
|
||||||
|
if (!maybe_transition)
|
||||||
return {};
|
return {};
|
||||||
|
auto& transition = *maybe_transition;
|
||||||
auto& impl = *m_impl;
|
if (auto maybe_attr_index = transition.transition_attribute_indices.get(property); maybe_attr_index.has_value())
|
||||||
if (auto maybe_index = impl.transition_attribute_indices.get(property); maybe_index.has_value())
|
return transition.transition_attributes[maybe_attr_index.value()];
|
||||||
return impl.transition_attributes[maybe_index.value()];
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
GC::Ptr<CSS::CSSTransition> Animatable::property_transition(CSS::PropertyID property) const
|
GC::Ptr<CSS::CSSTransition> Animatable::property_transition(Optional<CSS::PseudoElement> pseudo_element, CSS::PropertyID property) const
|
||||||
{
|
{
|
||||||
if (!m_impl)
|
auto* maybe_transition = ensure_transition(pseudo_element);
|
||||||
|
if (!maybe_transition)
|
||||||
return {};
|
return {};
|
||||||
auto& impl = *m_impl;
|
auto& transition = *maybe_transition;
|
||||||
if (auto maybe_animation = impl.associated_transitions.get(property); maybe_animation.has_value())
|
if (auto maybe_animation = transition.associated_transitions.get(property); maybe_animation.has_value())
|
||||||
return maybe_animation.value();
|
return maybe_animation.value();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animatable::set_transition(CSS::PropertyID property, GC::Ref<CSS::CSSTransition> animation)
|
void Animatable::set_transition(Optional<CSS::PseudoElement> pseudo_element, CSS::PropertyID property, GC::Ref<CSS::CSSTransition> animation)
|
||||||
{
|
{
|
||||||
auto& impl = ensure_impl();
|
auto maybe_transition = ensure_transition(pseudo_element);
|
||||||
VERIFY(!impl.associated_transitions.contains(property));
|
if (!maybe_transition)
|
||||||
impl.associated_transitions.set(property, animation);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Animatable::remove_transition(CSS::PropertyID property_id)
|
|
||||||
{
|
|
||||||
auto& impl = *m_impl;
|
|
||||||
VERIFY(impl.associated_transitions.contains(property_id));
|
|
||||||
impl.associated_transitions.remove(property_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Animatable::clear_transitions()
|
|
||||||
{
|
|
||||||
if (!m_impl)
|
|
||||||
return;
|
return;
|
||||||
auto& impl = *m_impl;
|
auto& transition = *maybe_transition;
|
||||||
impl.associated_transitions.clear();
|
VERIFY(!transition.associated_transitions.contains(property));
|
||||||
impl.transition_attribute_indices.clear();
|
transition.associated_transitions.set(property, animation);
|
||||||
impl.transition_attributes.clear();
|
}
|
||||||
|
|
||||||
|
void Animatable::remove_transition(Optional<CSS::PseudoElement> pseudo_element, CSS::PropertyID property_id)
|
||||||
|
{
|
||||||
|
auto maybe_transition = ensure_transition(pseudo_element);
|
||||||
|
if (!maybe_transition)
|
||||||
|
return;
|
||||||
|
auto& transition = *maybe_transition;
|
||||||
|
VERIFY(transition.associated_transitions.contains(property_id));
|
||||||
|
transition.associated_transitions.remove(property_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Animatable::clear_transitions(Optional<CSS::PseudoElement> pseudo_element)
|
||||||
|
{
|
||||||
|
auto maybe_transition = ensure_transition(pseudo_element);
|
||||||
|
if (!maybe_transition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto& transition = *maybe_transition;
|
||||||
|
transition.associated_transitions.clear();
|
||||||
|
transition.transition_attribute_indices.clear();
|
||||||
|
transition.transition_attributes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animatable::visit_edges(JS::Cell::Visitor& visitor)
|
void Animatable::visit_edges(JS::Cell::Visitor& visitor)
|
||||||
{
|
{
|
||||||
if (!m_impl)
|
auto& impl = ensure_impl();
|
||||||
return;
|
|
||||||
auto& impl = *m_impl;
|
|
||||||
visitor.visit(impl.associated_animations);
|
visitor.visit(impl.associated_animations);
|
||||||
for (auto const& cached_animation_source : impl.cached_animation_name_source)
|
for (auto const& cached_animation_source : impl.cached_animation_name_source)
|
||||||
visitor.visit(cached_animation_source);
|
visitor.visit(cached_animation_source);
|
||||||
for (auto const& cached_animation_name : impl.cached_animation_name_animation)
|
for (auto const& cached_animation_name : impl.cached_animation_name_animation)
|
||||||
visitor.visit(cached_animation_name);
|
visitor.visit(cached_animation_name);
|
||||||
visitor.visit(impl.cached_transition_property_source);
|
for (auto const& transition : impl.transitions) {
|
||||||
visitor.visit(impl.associated_transitions);
|
if (transition) {
|
||||||
|
visitor.visit(transition->cached_transition_property_source);
|
||||||
|
visitor.visit(transition->associated_transitions);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GC::Ptr<CSS::CSSStyleDeclaration const> Animatable::cached_animation_name_source(Optional<CSS::PseudoElement> pseudo_element) const
|
GC::Ptr<CSS::CSSStyleDeclaration const> Animatable::cached_animation_name_source(Optional<CSS::PseudoElement> pseudo_element) const
|
||||||
|
@ -267,24 +291,43 @@ void Animatable::set_cached_animation_name_animation(GC::Ptr<Animations::Animati
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GC::Ptr<CSS::CSSStyleDeclaration const> Animatable::cached_transition_property_source() const
|
GC::Ptr<CSS::CSSStyleDeclaration const> Animatable::cached_transition_property_source(Optional<CSS::PseudoElement> pseudo_element) const
|
||||||
{
|
{
|
||||||
if (!m_impl)
|
auto* maybe_transition = ensure_transition(pseudo_element);
|
||||||
|
if (!maybe_transition)
|
||||||
return {};
|
return {};
|
||||||
return m_impl->cached_transition_property_source;
|
return maybe_transition->cached_transition_property_source;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animatable::set_cached_transition_property_source(GC::Ptr<CSS::CSSStyleDeclaration const> value)
|
void Animatable::set_cached_transition_property_source(Optional<CSS::PseudoElement> pseudo_element, GC::Ptr<CSS::CSSStyleDeclaration const> value)
|
||||||
{
|
{
|
||||||
ensure_impl();
|
auto* maybe_transition = ensure_transition(pseudo_element);
|
||||||
m_impl->cached_transition_property_source = value;
|
if (!maybe_transition)
|
||||||
|
return;
|
||||||
|
maybe_transition->cached_transition_property_source = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Animatable::Impl& Animatable::ensure_impl()
|
Animatable::Impl& Animatable::ensure_impl() const
|
||||||
{
|
{
|
||||||
if (!m_impl)
|
if (!m_impl)
|
||||||
m_impl = make<Impl>();
|
m_impl = make<Impl>();
|
||||||
return *m_impl;
|
return *m_impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Animatable::Transition* Animatable::ensure_transition(Optional<CSS::PseudoElement> pseudo_element) const
|
||||||
|
{
|
||||||
|
auto& impl = ensure_impl();
|
||||||
|
|
||||||
|
size_t pseudo_element_index = 0;
|
||||||
|
if (pseudo_element.has_value()) {
|
||||||
|
if (!CSS::Selector::PseudoElementSelector::is_known_pseudo_element_type(pseudo_element.value()))
|
||||||
|
return nullptr;
|
||||||
|
pseudo_element_index = to_underlying(pseudo_element.value()) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!impl.transitions[pseudo_element_index])
|
||||||
|
impl.transitions[pseudo_element_index] = make<Transition>();
|
||||||
|
return impl.transitions[pseudo_element_index];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, Matthew Olsson <mattco@serenityos.org>
|
* Copyright (c) 2024, Matthew Olsson <mattco@serenityos.org>
|
||||||
* Copyright (c) 2024, Sam Atkins <sam@ladybird.org>
|
* Copyright (c) 2024, Sam Atkins <sam@ladybird.org>
|
||||||
|
* Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -56,20 +57,22 @@ public:
|
||||||
GC::Ptr<Animations::Animation> cached_animation_name_animation(Optional<CSS::PseudoElement>) const;
|
GC::Ptr<Animations::Animation> cached_animation_name_animation(Optional<CSS::PseudoElement>) const;
|
||||||
void set_cached_animation_name_animation(GC::Ptr<Animations::Animation> value, Optional<CSS::PseudoElement>);
|
void set_cached_animation_name_animation(GC::Ptr<Animations::Animation> value, Optional<CSS::PseudoElement>);
|
||||||
|
|
||||||
GC::Ptr<CSS::CSSStyleDeclaration const> cached_transition_property_source() const;
|
GC::Ptr<CSS::CSSStyleDeclaration const> cached_transition_property_source(Optional<CSS::PseudoElement>) const;
|
||||||
void set_cached_transition_property_source(GC::Ptr<CSS::CSSStyleDeclaration const> value);
|
void set_cached_transition_property_source(Optional<CSS::PseudoElement>, GC::Ptr<CSS::CSSStyleDeclaration const> value);
|
||||||
|
|
||||||
void add_transitioned_properties(Vector<Vector<CSS::PropertyID>> properties, CSS::StyleValueVector delays, CSS::StyleValueVector durations, CSS::StyleValueVector timing_functions, CSS::StyleValueVector transition_behaviors);
|
void add_transitioned_properties(Optional<CSS::PseudoElement>, Vector<Vector<CSS::PropertyID>> properties, CSS::StyleValueVector delays, CSS::StyleValueVector durations, CSS::StyleValueVector timing_functions, CSS::StyleValueVector transition_behaviors);
|
||||||
Optional<TransitionAttributes const&> property_transition_attributes(CSS::PropertyID) const;
|
Optional<TransitionAttributes const&> property_transition_attributes(Optional<CSS::PseudoElement>, CSS::PropertyID) const;
|
||||||
void set_transition(CSS::PropertyID, GC::Ref<CSS::CSSTransition>);
|
void set_transition(Optional<CSS::PseudoElement>, CSS::PropertyID, GC::Ref<CSS::CSSTransition>);
|
||||||
void remove_transition(CSS::PropertyID);
|
void remove_transition(Optional<CSS::PseudoElement>, CSS::PropertyID);
|
||||||
GC::Ptr<CSS::CSSTransition> property_transition(CSS::PropertyID) const;
|
GC::Ptr<CSS::CSSTransition> property_transition(Optional<CSS::PseudoElement>, CSS::PropertyID) const;
|
||||||
void clear_transitions();
|
void clear_transitions(Optional<CSS::PseudoElement>);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void visit_edges(JS::Cell::Visitor&);
|
void visit_edges(JS::Cell::Visitor&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct Transition;
|
||||||
|
|
||||||
struct Impl {
|
struct Impl {
|
||||||
Vector<GC::Ref<Animation>> associated_animations;
|
Vector<GC::Ref<Animation>> associated_animations;
|
||||||
bool is_sorted_by_composite_order { true };
|
bool is_sorted_by_composite_order { true };
|
||||||
|
@ -77,14 +80,14 @@ private:
|
||||||
Array<GC::Ptr<CSS::CSSStyleDeclaration const>, to_underlying(CSS::PseudoElement::KnownPseudoElementCount) + 1> cached_animation_name_source;
|
Array<GC::Ptr<CSS::CSSStyleDeclaration const>, to_underlying(CSS::PseudoElement::KnownPseudoElementCount) + 1> cached_animation_name_source;
|
||||||
Array<GC::Ptr<Animation>, to_underlying(CSS::PseudoElement::KnownPseudoElementCount) + 1> cached_animation_name_animation;
|
Array<GC::Ptr<Animation>, to_underlying(CSS::PseudoElement::KnownPseudoElementCount) + 1> cached_animation_name_animation;
|
||||||
|
|
||||||
HashMap<CSS::PropertyID, size_t> transition_attribute_indices;
|
mutable Array<OwnPtr<Transition>, to_underlying(CSS::PseudoElement::KnownPseudoElementCount) + 1> transitions;
|
||||||
Vector<TransitionAttributes> transition_attributes;
|
|
||||||
GC::Ptr<CSS::CSSStyleDeclaration const> cached_transition_property_source;
|
|
||||||
HashMap<CSS::PropertyID, GC::Ref<CSS::CSSTransition>> associated_transitions;
|
|
||||||
};
|
|
||||||
Impl& ensure_impl();
|
|
||||||
|
|
||||||
OwnPtr<Impl> m_impl;
|
~Impl();
|
||||||
|
};
|
||||||
|
Impl& ensure_impl() const;
|
||||||
|
Transition* ensure_transition(Optional<CSS::PseudoElement>) const;
|
||||||
|
|
||||||
|
mutable OwnPtr<Impl> m_impl;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,12 +19,12 @@ namespace Web::CSS {
|
||||||
|
|
||||||
GC_DEFINE_ALLOCATOR(CSSTransition);
|
GC_DEFINE_ALLOCATOR(CSSTransition);
|
||||||
|
|
||||||
GC::Ref<CSSTransition> CSSTransition::start_a_transition(DOM::Element& element, PropertyID property_id, size_t transition_generation,
|
GC::Ref<CSSTransition> CSSTransition::start_a_transition(DOM::Element& element, Optional<PseudoElement> pseudo_element, PropertyID property_id,
|
||||||
double start_time, double end_time, NonnullRefPtr<CSSStyleValue const> start_value, NonnullRefPtr<CSSStyleValue const> end_value,
|
size_t transition_generation, double start_time, double end_time, NonnullRefPtr<CSSStyleValue const> start_value,
|
||||||
NonnullRefPtr<CSSStyleValue const> reversing_adjusted_start_value, double reversing_shortening_factor)
|
NonnullRefPtr<CSSStyleValue const> end_value, NonnullRefPtr<CSSStyleValue const> reversing_adjusted_start_value, double reversing_shortening_factor)
|
||||||
{
|
{
|
||||||
auto& realm = element.realm();
|
auto& realm = element.realm();
|
||||||
return realm.create<CSSTransition>(realm, element, property_id, transition_generation, start_time, end_time, start_value, end_value, reversing_adjusted_start_value, reversing_shortening_factor);
|
return realm.create<CSSTransition>(realm, element, pseudo_element, property_id, transition_generation, start_time, end_time, start_value, end_value, reversing_adjusted_start_value, reversing_shortening_factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
Animations::AnimationClass CSSTransition::animation_class() const
|
Animations::AnimationClass CSSTransition::animation_class() const
|
||||||
|
@ -75,7 +75,7 @@ Optional<int> CSSTransition::class_specific_composite_order(GC::Ref<Animations::
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
CSSTransition::CSSTransition(JS::Realm& realm, DOM::Element& element, PropertyID property_id, size_t transition_generation,
|
CSSTransition::CSSTransition(JS::Realm& realm, DOM::Element& element, Optional<PseudoElement> pseudo_element, PropertyID property_id, size_t transition_generation,
|
||||||
double start_time, double end_time, NonnullRefPtr<CSSStyleValue const> start_value, NonnullRefPtr<CSSStyleValue const> end_value,
|
double start_time, double end_time, NonnullRefPtr<CSSStyleValue const> start_value, NonnullRefPtr<CSSStyleValue const> end_value,
|
||||||
NonnullRefPtr<CSSStyleValue const> reversing_adjusted_start_value, double reversing_shortening_factor)
|
NonnullRefPtr<CSSStyleValue const> reversing_adjusted_start_value, double reversing_shortening_factor)
|
||||||
: Animations::Animation(realm)
|
: Animations::Animation(realm)
|
||||||
|
@ -99,9 +99,11 @@ CSSTransition::CSSTransition(JS::Realm& realm, DOM::Element& element, PropertyID
|
||||||
|
|
||||||
// Construct a KeyframesEffect for our animation
|
// Construct a KeyframesEffect for our animation
|
||||||
m_keyframe_effect->set_target(&element);
|
m_keyframe_effect->set_target(&element);
|
||||||
|
if (pseudo_element.has_value())
|
||||||
|
m_keyframe_effect->set_pseudo_element(Selector::PseudoElementSelector { pseudo_element.value() });
|
||||||
m_keyframe_effect->set_start_delay(start_time);
|
m_keyframe_effect->set_start_delay(start_time);
|
||||||
m_keyframe_effect->set_iteration_duration(m_end_time - start_time);
|
m_keyframe_effect->set_iteration_duration(m_end_time - start_time);
|
||||||
m_keyframe_effect->set_timing_function(element.property_transition_attributes(property_id)->timing_function);
|
m_keyframe_effect->set_timing_function(element.property_transition_attributes(pseudo_element, property_id)->timing_function);
|
||||||
|
|
||||||
auto key_frame_set = adopt_ref(*new Animations::KeyframeEffect::KeyFrameSet);
|
auto key_frame_set = adopt_ref(*new Animations::KeyframeEffect::KeyFrameSet);
|
||||||
Animations::KeyframeEffect::KeyFrameSet::ResolvedKeyFrame initial_keyframe;
|
Animations::KeyframeEffect::KeyFrameSet::ResolvedKeyFrame initial_keyframe;
|
||||||
|
@ -118,7 +120,7 @@ CSSTransition::CSSTransition(JS::Realm& realm, DOM::Element& element, PropertyID
|
||||||
set_owning_element(element);
|
set_owning_element(element);
|
||||||
set_effect(m_keyframe_effect);
|
set_effect(m_keyframe_effect);
|
||||||
element.associate_with_animation(*this);
|
element.associate_with_animation(*this);
|
||||||
element.set_transition(m_transition_property, *this);
|
element.set_transition(pseudo_element, m_transition_property, *this);
|
||||||
|
|
||||||
HTML::TemporaryExecutionContext context(realm);
|
HTML::TemporaryExecutionContext context(realm);
|
||||||
play().release_value_but_fixme_should_propagate_errors();
|
play().release_value_but_fixme_should_propagate_errors();
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <LibWeb/CSS/CSSStyleValue.h>
|
#include <LibWeb/CSS/CSSStyleValue.h>
|
||||||
#include <LibWeb/CSS/Interpolation.h>
|
#include <LibWeb/CSS/Interpolation.h>
|
||||||
#include <LibWeb/CSS/PropertyID.h>
|
#include <LibWeb/CSS/PropertyID.h>
|
||||||
|
#include <LibWeb/CSS/PseudoElement.h>
|
||||||
#include <LibWeb/CSS/StyleValues/EasingStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/EasingStyleValue.h>
|
||||||
#include <LibWeb/CSS/Time.h>
|
#include <LibWeb/CSS/Time.h>
|
||||||
|
|
||||||
|
@ -21,9 +22,9 @@ class CSSTransition : public Animations::Animation {
|
||||||
GC_DECLARE_ALLOCATOR(CSSTransition);
|
GC_DECLARE_ALLOCATOR(CSSTransition);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static GC::Ref<CSSTransition> start_a_transition(DOM::Element&, PropertyID, size_t transition_generation,
|
static GC::Ref<CSSTransition> start_a_transition(DOM::Element&, Optional<PseudoElement>, PropertyID,
|
||||||
double start_time, double end_time, NonnullRefPtr<CSSStyleValue const> start_value, NonnullRefPtr<CSSStyleValue const> end_value,
|
size_t transition_generation, double start_time, double end_time, NonnullRefPtr<CSSStyleValue const> start_value,
|
||||||
NonnullRefPtr<CSSStyleValue const> reversing_adjusted_start_value, double reversing_shortening_factor);
|
NonnullRefPtr<CSSStyleValue const> end_value, NonnullRefPtr<CSSStyleValue const> reversing_adjusted_start_value, double reversing_shortening_factor);
|
||||||
|
|
||||||
StringView transition_property() const { return string_from_property_id(m_transition_property); }
|
StringView transition_property() const { return string_from_property_id(m_transition_property); }
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ public:
|
||||||
void set_previous_phase(Phase phase) { m_previous_phase = phase; }
|
void set_previous_phase(Phase phase) { m_previous_phase = phase; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CSSTransition(JS::Realm&, DOM::Element&, PropertyID, size_t transition_generation,
|
CSSTransition(JS::Realm&, DOM::Element&, Optional<PseudoElement>, PropertyID, size_t transition_generation,
|
||||||
double start_time, double end_time, NonnullRefPtr<CSSStyleValue const> start_value, NonnullRefPtr<CSSStyleValue const> end_value,
|
double start_time, double end_time, NonnullRefPtr<CSSStyleValue const> start_value, NonnullRefPtr<CSSStyleValue const> end_value,
|
||||||
NonnullRefPtr<CSSStyleValue const> reversing_adjusted_start_value, double reversing_shortening_factor);
|
NonnullRefPtr<CSSStyleValue const> reversing_adjusted_start_value, double reversing_shortening_factor);
|
||||||
|
|
||||||
|
|
|
@ -1366,19 +1366,16 @@ static void apply_dimension_attribute(CascadedProperties& cascaded_properties, D
|
||||||
|
|
||||||
static void compute_transitioned_properties(ComputedProperties const& style, DOM::Element& element, Optional<PseudoElement> pseudo_element)
|
static void compute_transitioned_properties(ComputedProperties const& style, DOM::Element& element, Optional<PseudoElement> pseudo_element)
|
||||||
{
|
{
|
||||||
// FIXME: Implement transitioning for pseudo-elements
|
|
||||||
(void)pseudo_element;
|
|
||||||
|
|
||||||
auto const source_declaration = style.transition_property_source();
|
auto const source_declaration = style.transition_property_source();
|
||||||
if (!source_declaration)
|
if (!source_declaration)
|
||||||
return;
|
return;
|
||||||
if (!element.computed_properties())
|
if (!element.computed_properties())
|
||||||
return;
|
return;
|
||||||
if (source_declaration == element.cached_transition_property_source())
|
if (source_declaration == element.cached_transition_property_source(pseudo_element))
|
||||||
return;
|
return;
|
||||||
// Reparse this transition property
|
// Reparse this transition property
|
||||||
element.clear_transitions();
|
element.clear_transitions(pseudo_element);
|
||||||
element.set_cached_transition_property_source(*source_declaration);
|
element.set_cached_transition_property_source(pseudo_element, *source_declaration);
|
||||||
|
|
||||||
auto const& transition_properties_value = style.property(PropertyID::TransitionProperty);
|
auto const& transition_properties_value = style.property(PropertyID::TransitionProperty);
|
||||||
auto transition_properties = transition_properties_value.is_value_list()
|
auto transition_properties = transition_properties_value.is_value_list()
|
||||||
|
@ -1453,15 +1450,12 @@ static void compute_transitioned_properties(ComputedProperties const& style, DOM
|
||||||
PropertyID::TransitionBehavior,
|
PropertyID::TransitionBehavior,
|
||||||
[] { return CSSKeywordValue::create(Keyword::None); });
|
[] { return CSSKeywordValue::create(Keyword::None); });
|
||||||
|
|
||||||
element.add_transitioned_properties(move(properties), move(delays), move(durations), move(timing_functions), move(transition_behaviors));
|
element.add_transitioned_properties(pseudo_element, move(properties), move(delays), move(durations), move(timing_functions), move(transition_behaviors));
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-transitions/#starting
|
// https://drafts.csswg.org/css-transitions/#starting
|
||||||
void StyleComputer::start_needed_transitions(ComputedProperties const& previous_style, ComputedProperties& new_style, DOM::Element& element, Optional<PseudoElement> pseudo_element) const
|
void StyleComputer::start_needed_transitions(ComputedProperties const& previous_style, ComputedProperties& new_style, DOM::Element& element, Optional<PseudoElement> pseudo_element) const
|
||||||
{
|
{
|
||||||
// FIXME: Implement transitions for pseudo-elements
|
|
||||||
if (pseudo_element.has_value())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-transitions/#transition-combined-duration
|
// https://drafts.csswg.org/css-transitions/#transition-combined-duration
|
||||||
auto combined_duration = [](Animations::Animatable::TransitionAttributes const& transition_attributes) {
|
auto combined_duration = [](Animations::Animatable::TransitionAttributes const& transition_attributes) {
|
||||||
|
@ -1474,19 +1468,19 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_
|
||||||
|
|
||||||
for (auto i = to_underlying(CSS::first_longhand_property_id); i <= to_underlying(CSS::last_longhand_property_id); ++i) {
|
for (auto i = to_underlying(CSS::first_longhand_property_id); i <= to_underlying(CSS::last_longhand_property_id); ++i) {
|
||||||
auto property_id = static_cast<CSS::PropertyID>(i);
|
auto property_id = static_cast<CSS::PropertyID>(i);
|
||||||
auto matching_transition_properties = element.property_transition_attributes(property_id);
|
auto matching_transition_properties = element.property_transition_attributes(pseudo_element, property_id);
|
||||||
auto const& before_change_value = previous_style.property(property_id, ComputedProperties::WithAnimationsApplied::Yes);
|
auto const& before_change_value = previous_style.property(property_id, ComputedProperties::WithAnimationsApplied::Yes);
|
||||||
auto const& after_change_value = new_style.property(property_id, ComputedProperties::WithAnimationsApplied::No);
|
auto const& after_change_value = new_style.property(property_id, ComputedProperties::WithAnimationsApplied::No);
|
||||||
|
|
||||||
auto existing_transition = element.property_transition(property_id);
|
auto existing_transition = element.property_transition(pseudo_element, property_id);
|
||||||
bool has_running_transition = existing_transition && !existing_transition->is_finished();
|
bool has_running_transition = existing_transition && !existing_transition->is_finished();
|
||||||
bool has_completed_transition = existing_transition && existing_transition->is_finished();
|
bool has_completed_transition = existing_transition && existing_transition->is_finished();
|
||||||
|
|
||||||
auto start_a_transition = [&](auto start_time, auto end_time, auto const& start_value, auto const& end_value, auto const& reversing_adjusted_start_value, auto reversing_shortening_factor) {
|
auto start_a_transition = [&](auto start_time, auto end_time, auto const& start_value, auto const& end_value, auto const& reversing_adjusted_start_value, auto reversing_shortening_factor) {
|
||||||
dbgln_if(CSS_TRANSITIONS_DEBUG, "Starting a transition of {} from {} to {}", string_from_property_id(property_id), start_value->to_string(), end_value->to_string());
|
dbgln_if(CSS_TRANSITIONS_DEBUG, "Starting a transition of {} from {} to {}", string_from_property_id(property_id), start_value->to_string(), end_value->to_string());
|
||||||
|
|
||||||
auto transition = CSSTransition::start_a_transition(element, property_id, document().transition_generation(),
|
auto transition = CSSTransition::start_a_transition(element, pseudo_element, property_id,
|
||||||
start_time, end_time, start_value, end_value, reversing_adjusted_start_value, reversing_shortening_factor);
|
document().transition_generation(), start_time, end_time, start_value, end_value, reversing_adjusted_start_value, reversing_shortening_factor);
|
||||||
// Immediately set the property's value to the transition's current value, to prevent single-frame jumps.
|
// Immediately set the property's value to the transition's current value, to prevent single-frame jumps.
|
||||||
collect_animation_into(element, {}, as<Animations::KeyframeEffect>(*transition->effect()), new_style, AnimationRefresh::No);
|
collect_animation_into(element, {}, as<Animations::KeyframeEffect>(*transition->effect()), new_style, AnimationRefresh::No);
|
||||||
};
|
};
|
||||||
|
@ -1509,7 +1503,7 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_
|
||||||
|
|
||||||
// then implementations must remove the completed transition (if present) from the set of completed transitions
|
// then implementations must remove the completed transition (if present) from the set of completed transitions
|
||||||
if (has_completed_transition)
|
if (has_completed_transition)
|
||||||
element.remove_transition(property_id);
|
element.remove_transition(pseudo_element, property_id);
|
||||||
// and start a transition whose:
|
// and start a transition whose:
|
||||||
|
|
||||||
// - start time is the time of the style change event plus the matching transition delay,
|
// - start time is the time of the style change event plus the matching transition delay,
|
||||||
|
@ -1538,7 +1532,7 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_
|
||||||
// then implementations must remove the completed transition from the set of completed transitions.
|
// then implementations must remove the completed transition from the set of completed transitions.
|
||||||
else if (has_completed_transition && !existing_transition->transition_end_value()->equals(after_change_value)) {
|
else if (has_completed_transition && !existing_transition->transition_end_value()->equals(after_change_value)) {
|
||||||
dbgln_if(CSS_TRANSITIONS_DEBUG, "Transition step 2.");
|
dbgln_if(CSS_TRANSITIONS_DEBUG, "Transition step 2.");
|
||||||
element.remove_transition(property_id);
|
element.remove_transition(pseudo_element, property_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. If the element has a running transition or completed transition for the property,
|
// 3. If the element has a running transition or completed transition for the property,
|
||||||
|
@ -1549,7 +1543,7 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_
|
||||||
if (has_running_transition)
|
if (has_running_transition)
|
||||||
existing_transition->cancel();
|
existing_transition->cancel();
|
||||||
else
|
else
|
||||||
element.remove_transition(property_id);
|
element.remove_transition(pseudo_element, property_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. If the element has a running transition for the property,
|
// 4. If the element has a running transition for the property,
|
||||||
|
@ -1583,7 +1577,7 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_
|
||||||
existing_transition->cancel();
|
existing_transition->cancel();
|
||||||
// AD-HOC: Remove the cancelled transition, otherwise it breaks the invariant that there is only one
|
// AD-HOC: Remove the cancelled transition, otherwise it breaks the invariant that there is only one
|
||||||
// running or completed transition for a property at once.
|
// running or completed transition for a property at once.
|
||||||
element.remove_transition(property_id);
|
element.remove_transition(pseudo_element, property_id);
|
||||||
|
|
||||||
// - reversing-adjusted start value is the end value of the running transition,
|
// - reversing-adjusted start value is the end value of the running transition,
|
||||||
auto reversing_adjusted_start_value = existing_transition->transition_end_value();
|
auto reversing_adjusted_start_value = existing_transition->transition_end_value();
|
||||||
|
@ -1623,7 +1617,7 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_
|
||||||
existing_transition->cancel();
|
existing_transition->cancel();
|
||||||
// AD-HOC: Remove the cancelled transition, otherwise it breaks the invariant that there is only one
|
// AD-HOC: Remove the cancelled transition, otherwise it breaks the invariant that there is only one
|
||||||
// running or completed transition for a property at once.
|
// running or completed transition for a property at once.
|
||||||
element.remove_transition(property_id);
|
element.remove_transition(pseudo_element, property_id);
|
||||||
|
|
||||||
// - start time is the time of the style change event plus the matching transition delay,
|
// - start time is the time of the style change event plus the matching transition delay,
|
||||||
auto start_time = style_change_event_time + matching_transition_properties->delay;
|
auto start_time = style_change_event_time + matching_transition_properties->delay;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue