mirror of
https://github.com/servo/servo.git
synced 2025-08-07 14:35:33 +01:00
Add ElementAnimationState and PossibleElementAnimationState
This refactor is preparation for implementing a specification compliant transitions and animations processing model. These data structures hold all the animation information about a single node. Since adding, updating, and modifying animations for a single node are all interdependent, it makes sense to start encapsulating animation data and functionality into a single data structure. This also opens up the possibility for easier concurrency in the future by more easily allowing per-node mutexes.
This commit is contained in:
parent
5504d9259d
commit
8f988be18a
9 changed files with 332 additions and 454 deletions
|
@ -19,7 +19,7 @@ doctest = false
|
|||
gecko = ["style_traits/gecko", "fallible/known_system_malloc", "bindgen", "regex", "toml"]
|
||||
servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever",
|
||||
"cssparser/serde", "encoding_rs", "malloc_size_of/servo", "servo_url",
|
||||
"string_cache", "crossbeam-channel", "to_shmem/servo",
|
||||
"string_cache", "to_shmem/servo",
|
||||
"servo_arc/servo"]
|
||||
servo-layout-2013 = []
|
||||
servo-layout-2020 = []
|
||||
|
@ -34,7 +34,6 @@ atomic_refcell = "0.1"
|
|||
bitflags = "1.0"
|
||||
byteorder = "1.0"
|
||||
cssparser = "0.27"
|
||||
crossbeam-channel = { version = "0.4", optional = true }
|
||||
derive_more = "0.99"
|
||||
new_debug_unreachable = "1.0"
|
||||
encoding_rs = {version = "0.8", optional = true}
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
//! CSS transitions and animations.
|
||||
|
||||
// NOTE(emilio): This code isn't really executed in Gecko, but we don't want to
|
||||
// compile it out so that people remember it exists, thus the cfg'd Sender
|
||||
// import.
|
||||
// compile it out so that people remember it exists.
|
||||
|
||||
use crate::bezier::Bezier;
|
||||
use crate::context::SharedStyleContext;
|
||||
|
@ -15,7 +14,6 @@ use crate::font_metrics::FontMetricsProvider;
|
|||
use crate::properties::animated_properties::AnimatedProperty;
|
||||
use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
|
||||
use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
|
||||
#[cfg(feature = "servo")]
|
||||
use crate::properties::LonghandIdSet;
|
||||
use crate::properties::{self, CascadeMode, ComputedValues, LonghandId};
|
||||
use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
|
||||
|
@ -26,12 +24,8 @@ use crate::values::computed::TimingFunction;
|
|||
use crate::values::generics::box_::AnimationIterationCount;
|
||||
use crate::values::generics::easing::{StepPosition, TimingFunction as GenericTimingFunction};
|
||||
use crate::Atom;
|
||||
#[cfg(feature = "servo")]
|
||||
use crossbeam_channel::Sender;
|
||||
use servo_arc::Arc;
|
||||
use std::fmt;
|
||||
#[cfg(feature = "gecko")]
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
/// This structure represents a keyframes animation current iteration state.
|
||||
///
|
||||
|
@ -369,70 +363,168 @@ impl PropertyAnimation {
|
|||
}
|
||||
}
|
||||
|
||||
/// Start any new transitions for this node and ensure that any existing transitions
|
||||
/// that are cancelled are marked as cancelled in the SharedStyleContext. This is
|
||||
/// at the end of calculating style for a single node.
|
||||
#[cfg(feature = "servo")]
|
||||
pub fn update_transitions(
|
||||
context: &SharedStyleContext,
|
||||
new_animations_sender: &Sender<Animation>,
|
||||
opaque_node: OpaqueNode,
|
||||
old_style: &ComputedValues,
|
||||
new_style: &mut Arc<ComputedValues>,
|
||||
expired_transitions: &[PropertyAnimation],
|
||||
) {
|
||||
let mut all_running_animations = context.running_animations.write();
|
||||
let previously_running_animations = all_running_animations
|
||||
.remove(&opaque_node)
|
||||
.unwrap_or_else(Vec::new);
|
||||
/// Holds the animation state for a particular element.
|
||||
#[derive(Default)]
|
||||
pub struct ElementAnimationState {
|
||||
/// The animations running for this element.
|
||||
pub running_animations: Vec<Animation>,
|
||||
|
||||
let properties_that_transition = start_transitions_if_applicable(
|
||||
context,
|
||||
new_animations_sender,
|
||||
opaque_node,
|
||||
old_style,
|
||||
new_style,
|
||||
expired_transitions,
|
||||
&previously_running_animations,
|
||||
);
|
||||
/// The animations that have finished for this element, but not canceled. These are cleared
|
||||
/// upon triggering a DOM event for each finished animation.
|
||||
pub finished_animations: Vec<Animation>,
|
||||
|
||||
let mut all_cancelled_animations = context.cancelled_animations.write();
|
||||
let mut cancelled_animations = all_cancelled_animations
|
||||
.remove(&opaque_node)
|
||||
.unwrap_or_else(Vec::new);
|
||||
let mut running_animations = vec![];
|
||||
/// The animations that have been cancelled for this element. These are cleared
|
||||
/// upon triggering a DOM event for each cancelled animation.
|
||||
pub cancelled_animations: Vec<Animation>,
|
||||
|
||||
// For every animation that was running before this style change, we cancel it
|
||||
// if the property no longer transitions.
|
||||
for running_animation in previously_running_animations.into_iter() {
|
||||
if let Animation::Transition(_, _, ref property_animation) = running_animation {
|
||||
if !properties_that_transition.contains(property_animation.property_id()) {
|
||||
cancelled_animations.push(running_animation);
|
||||
continue;
|
||||
/// New animations created for this element.
|
||||
pub new_animations: Vec<Animation>,
|
||||
}
|
||||
|
||||
impl ElementAnimationState {
|
||||
/// Cancel all animations in this `ElementAnimationState`. This is typically called
|
||||
/// when the element has been removed from the DOM.
|
||||
pub fn cancel_all_animations(&mut self) {
|
||||
self.cancelled_animations.extend(
|
||||
self.finished_animations
|
||||
.drain(..)
|
||||
.chain(self.running_animations.drain(..))
|
||||
.chain(self.new_animations.drain(..)),
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn cancel_transitions_with_nontransitioning_properties(
|
||||
&mut self,
|
||||
properties_that_transition: &LonghandIdSet,
|
||||
) {
|
||||
if self.running_animations.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let previously_running_transitions =
|
||||
std::mem::replace(&mut self.running_animations, Vec::new());
|
||||
for running_animation in previously_running_transitions {
|
||||
if let Animation::Transition(_, _, ref property_animation) = running_animation {
|
||||
if !properties_that_transition.contains(property_animation.property_id()) {
|
||||
self.cancelled_animations.push(running_animation);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
self.running_animations.push(running_animation);
|
||||
}
|
||||
}
|
||||
|
||||
fn has_transition_with_same_end_value(&self, property_animation: &PropertyAnimation) -> bool {
|
||||
if self
|
||||
.running_animations
|
||||
.iter()
|
||||
.any(|animation| animation.is_transition_with_same_end_value(&property_animation))
|
||||
{
|
||||
debug!(
|
||||
"Running transition found with the same end value for {:?}",
|
||||
property_animation,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if self
|
||||
.finished_animations
|
||||
.iter()
|
||||
.any(|animation| animation.is_transition_with_same_end_value(&property_animation))
|
||||
{
|
||||
debug!(
|
||||
"Expired transition found with the same end value for {:?}",
|
||||
property_animation,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Compute before-change-style given an existing ElementAnimationState,
|
||||
/// information from the StyleContext, and the values of the previous style
|
||||
/// computation.
|
||||
///
|
||||
/// TODO(mrobinson): This is not a correct computation of before-change-style.
|
||||
/// For starters it's unclear why we aren't using the running transitions to
|
||||
/// transform this style into before-change-style.
|
||||
pub(crate) fn compute_before_change_style<E>(
|
||||
&mut self,
|
||||
context: &SharedStyleContext,
|
||||
style: &mut Arc<ComputedValues>,
|
||||
font_metrics: &dyn crate::font_metrics::FontMetricsProvider,
|
||||
) where
|
||||
E: TElement,
|
||||
{
|
||||
for animation in self.finished_animations.iter() {
|
||||
debug!("Updating style for finished animation {:?}", animation);
|
||||
// TODO: support animation-fill-mode
|
||||
if let Animation::Transition(_, _, property_animation) = animation {
|
||||
property_animation.update(Arc::make_mut(style), 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
for running_animation in self.running_animations.iter_mut() {
|
||||
let update = match *running_animation {
|
||||
Animation::Transition(..) => continue,
|
||||
Animation::Keyframes(..) => {
|
||||
update_style_for_animation::<E>(context, running_animation, style, font_metrics)
|
||||
},
|
||||
};
|
||||
|
||||
match *running_animation {
|
||||
Animation::Transition(..) => unreachable!(),
|
||||
Animation::Keyframes(_, _, _, ref mut state) => match update {
|
||||
AnimationUpdate::Regular => {},
|
||||
AnimationUpdate::AnimationCanceled => {
|
||||
state.expired = true;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
running_animations.push(running_animation);
|
||||
}
|
||||
|
||||
if !cancelled_animations.is_empty() {
|
||||
all_cancelled_animations.insert(opaque_node, cancelled_animations);
|
||||
/// Whether this `ElementAnimationState` is empty, which means it doesn't
|
||||
/// hold any animations in any state.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.running_animations.is_empty() &&
|
||||
self.finished_animations.is_empty() &&
|
||||
self.cancelled_animations.is_empty() &&
|
||||
self.new_animations.is_empty()
|
||||
}
|
||||
if !running_animations.is_empty() {
|
||||
all_running_animations.insert(opaque_node, running_animations);
|
||||
|
||||
fn add_new_animation(&mut self, animation: Animation) {
|
||||
self.new_animations.push(animation);
|
||||
}
|
||||
|
||||
fn add_or_update_new_animation(&mut self, timer: &Timer, new_animation: Animation) {
|
||||
// If the animation was already present in the list for the node,
|
||||
// just update its state.
|
||||
if let Animation::Keyframes(_, _, ref new_name, ref new_state) = new_animation {
|
||||
for existing_animation in self.running_animations.iter_mut() {
|
||||
match existing_animation {
|
||||
Animation::Keyframes(_, _, ref name, ref mut state) if *name == *new_name => {
|
||||
state.update_from_other(&new_state, timer);
|
||||
return;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise just add the new running animation.
|
||||
self.add_new_animation(new_animation);
|
||||
}
|
||||
}
|
||||
|
||||
/// Kick off any new transitions for this node and return all of the properties that are
|
||||
/// transitioning. This is at the end of calculating style for a single node.
|
||||
#[cfg(feature = "servo")]
|
||||
pub fn start_transitions_if_applicable(
|
||||
context: &SharedStyleContext,
|
||||
new_animations_sender: &Sender<Animation>,
|
||||
opaque_node: OpaqueNode,
|
||||
old_style: &ComputedValues,
|
||||
new_style: &mut Arc<ComputedValues>,
|
||||
expired_transitions: &[PropertyAnimation],
|
||||
running_animations: &[Animation],
|
||||
animation_state: &mut ElementAnimationState,
|
||||
) -> LonghandIdSet {
|
||||
use crate::properties::animated_properties::TransitionPropertyIteration;
|
||||
|
||||
|
@ -473,30 +565,10 @@ pub fn start_transitions_if_applicable(
|
|||
property_animation.update(Arc::get_mut(new_style).unwrap(), 0.0);
|
||||
|
||||
// Per [1], don't trigger a new transition if the end state for that
|
||||
// transition is the same as that of a transition that's expired.
|
||||
// transition is the same as that of a transition that's running or
|
||||
// completed.
|
||||
// [1]: https://drafts.csswg.org/css-transitions/#starting
|
||||
debug!("checking {:?} for matching end value", expired_transitions);
|
||||
if expired_transitions
|
||||
.iter()
|
||||
.any(|animation| animation.has_the_same_end_value_as(&property_animation))
|
||||
{
|
||||
debug!(
|
||||
"Not initiating transition for {}, expired transition \
|
||||
found with the same end value",
|
||||
property_animation.property_name()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if running_animations
|
||||
.iter()
|
||||
.any(|animation| animation.is_transition_with_same_end_value(&property_animation))
|
||||
{
|
||||
debug!(
|
||||
"Not initiating transition for {}, running transition \
|
||||
found with the same end value",
|
||||
property_animation.property_name()
|
||||
);
|
||||
if animation_state.has_transition_with_same_end_value(&property_animation) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -505,13 +577,11 @@ pub fn start_transitions_if_applicable(
|
|||
let box_style = new_style.get_box();
|
||||
let now = context.timer.seconds();
|
||||
let start_time = now + (box_style.transition_delay_mod(transition.index).seconds() as f64);
|
||||
new_animations_sender
|
||||
.send(Animation::Transition(
|
||||
opaque_node,
|
||||
start_time,
|
||||
property_animation,
|
||||
))
|
||||
.unwrap();
|
||||
animation_state.add_new_animation(Animation::Transition(
|
||||
opaque_node,
|
||||
start_time,
|
||||
property_animation,
|
||||
));
|
||||
}
|
||||
|
||||
properties_that_transition
|
||||
|
@ -575,15 +645,12 @@ where
|
|||
pub fn maybe_start_animations<E>(
|
||||
element: E,
|
||||
context: &SharedStyleContext,
|
||||
new_animations_sender: &Sender<Animation>,
|
||||
node: OpaqueNode,
|
||||
new_style: &Arc<ComputedValues>,
|
||||
) -> bool
|
||||
where
|
||||
animation_state: &mut ElementAnimationState,
|
||||
) where
|
||||
E: TElement,
|
||||
{
|
||||
let mut had_animations = false;
|
||||
|
||||
let box_style = new_style.get_box();
|
||||
for (i, name) in box_style.animation_name_iter().enumerate() {
|
||||
let name = match name.as_atom() {
|
||||
|
@ -637,8 +704,9 @@ where
|
|||
AnimationPlayState::Running => KeyframesRunningState::Running,
|
||||
};
|
||||
|
||||
new_animations_sender
|
||||
.send(Animation::Keyframes(
|
||||
animation_state.add_or_update_new_animation(
|
||||
&context.timer,
|
||||
Animation::Keyframes(
|
||||
node,
|
||||
anim.clone(),
|
||||
name.clone(),
|
||||
|
@ -653,12 +721,9 @@ where
|
|||
expired: false,
|
||||
cascade_style: new_style.clone(),
|
||||
},
|
||||
))
|
||||
.unwrap();
|
||||
had_animations = true;
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
had_animations
|
||||
}
|
||||
|
||||
/// Returns the kind of animation update that happened.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//! The context within which style is calculated.
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
use crate::animation::Animation;
|
||||
use crate::animation::ElementAnimationState;
|
||||
use crate::bloom::StyleBloom;
|
||||
use crate::data::{EagerPseudoStyles, ElementData};
|
||||
#[cfg(feature = "servo")]
|
||||
|
@ -29,8 +29,6 @@ use crate::timer::Timer;
|
|||
use crate::traversal::DomTraversal;
|
||||
use crate::traversal_flags::TraversalFlags;
|
||||
use app_units::Au;
|
||||
#[cfg(feature = "servo")]
|
||||
use crossbeam_channel::Sender;
|
||||
use euclid::default::Size2D;
|
||||
use euclid::Scale;
|
||||
use fxhash::FxHashMap;
|
||||
|
@ -43,8 +41,6 @@ use servo_arc::Arc;
|
|||
use servo_atoms::Atom;
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
#[cfg(feature = "servo")]
|
||||
use std::sync::Mutex;
|
||||
use style_traits::CSSPixel;
|
||||
use style_traits::DevicePixel;
|
||||
#[cfg(feature = "servo")]
|
||||
|
@ -54,22 +50,6 @@ use uluru::{Entry, LRUCache};
|
|||
|
||||
pub use selectors::matching::QuirksMode;
|
||||
|
||||
/// This structure is used to create a local style context from a shared one.
|
||||
#[cfg(feature = "servo")]
|
||||
pub struct ThreadLocalStyleContextCreationInfo {
|
||||
new_animations_sender: Sender<Animation>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
impl ThreadLocalStyleContextCreationInfo {
|
||||
/// Trivially constructs a `ThreadLocalStyleContextCreationInfo`.
|
||||
pub fn new(animations_sender: Sender<Animation>) -> Self {
|
||||
ThreadLocalStyleContextCreationInfo {
|
||||
new_animations_sender: animations_sender,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A global options structure for the style system. We use this instead of
|
||||
/// opts to abstract across Gecko and Servo.
|
||||
#[derive(Clone)]
|
||||
|
@ -186,25 +166,13 @@ pub struct SharedStyleContext<'a> {
|
|||
/// A map with our snapshots in order to handle restyle hints.
|
||||
pub snapshot_map: &'a SnapshotMap,
|
||||
|
||||
/// The animations that are currently running.
|
||||
/// The state of all animations for our styled elements.
|
||||
#[cfg(feature = "servo")]
|
||||
pub running_animations: Arc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>,
|
||||
|
||||
/// The list of animations that have expired since the last style recalculation.
|
||||
#[cfg(feature = "servo")]
|
||||
pub expired_animations: Arc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>,
|
||||
|
||||
/// The list of animations that have expired since the last style recalculation.
|
||||
#[cfg(feature = "servo")]
|
||||
pub cancelled_animations: Arc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>,
|
||||
pub animation_states: Arc<RwLock<FxHashMap<OpaqueNode, ElementAnimationState>>>,
|
||||
|
||||
/// Paint worklets
|
||||
#[cfg(feature = "servo")]
|
||||
pub registered_speculative_painters: &'a dyn RegisteredSpeculativePainters,
|
||||
|
||||
/// Data needed to create the thread-local style context from the shared one.
|
||||
#[cfg(feature = "servo")]
|
||||
pub local_context_creation_data: Mutex<ThreadLocalStyleContextCreationInfo>,
|
||||
}
|
||||
|
||||
impl<'a> SharedStyleContext<'a> {
|
||||
|
@ -741,10 +709,6 @@ pub struct ThreadLocalStyleContext<E: TElement> {
|
|||
pub rule_cache: RuleCache,
|
||||
/// The bloom filter used to fast-reject selector-matching.
|
||||
pub bloom_filter: StyleBloom<E>,
|
||||
/// A channel on which new animations that have been triggered by style
|
||||
/// recalculation can be sent.
|
||||
#[cfg(feature = "servo")]
|
||||
pub new_animations_sender: Sender<Animation>,
|
||||
/// A set of tasks to be run (on the parent thread) in sequential mode after
|
||||
/// the rest of the styling is complete. This is useful for
|
||||
/// infrequently-needed non-threadsafe operations.
|
||||
|
@ -778,12 +742,6 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
|
|||
sharing_cache: StyleSharingCache::new(),
|
||||
rule_cache: RuleCache::new(),
|
||||
bloom_filter: StyleBloom::new(),
|
||||
new_animations_sender: shared
|
||||
.local_context_creation_data
|
||||
.lock()
|
||||
.unwrap()
|
||||
.new_animations_sender
|
||||
.clone(),
|
||||
tasks: SequentialTaskList(Vec::new()),
|
||||
selector_flags: SelectorFlagsMap::new(),
|
||||
statistics: PerThreadTraversalStatistics::default(),
|
||||
|
|
|
@ -32,8 +32,6 @@ extern crate atomic_refcell;
|
|||
extern crate bitflags;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate byteorder;
|
||||
#[cfg(feature = "servo")]
|
||||
extern crate crossbeam_channel;
|
||||
#[macro_use]
|
||||
extern crate cssparser;
|
||||
#[macro_use]
|
||||
|
|
|
@ -7,13 +7,15 @@
|
|||
#![allow(unsafe_code)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
use crate::animation;
|
||||
use crate::computed_value_flags::ComputedValueFlags;
|
||||
use crate::context::{ElementCascadeInputs, QuirksMode, SelectorFlagsMap};
|
||||
use crate::context::{SharedStyleContext, StyleContext};
|
||||
use crate::data::ElementData;
|
||||
use crate::dom::TElement;
|
||||
#[cfg(feature = "servo")]
|
||||
use crate::dom::{OpaqueNode, TNode};
|
||||
use crate::dom::TNode;
|
||||
use crate::invalidation::element::restyle_hints::RestyleHint;
|
||||
use crate::properties::longhands::display::computed_value::T as Display;
|
||||
use crate::properties::ComputedValues;
|
||||
|
@ -437,55 +439,48 @@ trait PrivateMatchMethods: TElement {
|
|||
_restyle_hint: RestyleHint,
|
||||
_important_rules_changed: bool,
|
||||
) {
|
||||
use crate::animation;
|
||||
|
||||
let this_opaque = self.as_node().opaque();
|
||||
let mut expired_transitions = vec![];
|
||||
let shared_context = context.shared;
|
||||
if let Some(ref mut old_values) = *old_values {
|
||||
// We apply the expired transitions and animations to the old style
|
||||
// here, because we later compare the old style to the new style in
|
||||
// `start_transitions_if_applicable`. If the styles differ then it will
|
||||
// cause the expired transition to restart.
|
||||
//
|
||||
// TODO(mrobinson): We should really be following spec behavior and calculate
|
||||
// after-change-style and before-change-style here.
|
||||
Self::collect_and_update_style_for_expired_transitions(
|
||||
shared_context,
|
||||
this_opaque,
|
||||
old_values,
|
||||
&mut expired_transitions,
|
||||
);
|
||||
let mut animation_states = shared_context.animation_states.write();
|
||||
let mut animation_state = animation_states.remove(&this_opaque).unwrap_or_default();
|
||||
|
||||
Self::update_style_for_animations(
|
||||
if let Some(ref mut old_values) = *old_values {
|
||||
animation_state.compute_before_change_style::<Self>(
|
||||
shared_context,
|
||||
this_opaque,
|
||||
old_values,
|
||||
&context.thread_local.font_metrics_provider,
|
||||
);
|
||||
}
|
||||
|
||||
let new_animations_sender = &context.thread_local.new_animations_sender;
|
||||
// Trigger any present animations if necessary.
|
||||
animation::maybe_start_animations(
|
||||
*self,
|
||||
&shared_context,
|
||||
new_animations_sender,
|
||||
this_opaque,
|
||||
&new_values,
|
||||
&mut animation_state,
|
||||
);
|
||||
|
||||
// Trigger transitions if necessary. This will set `new_values` to
|
||||
// the starting value of the transition if it did trigger a transition.
|
||||
if let Some(ref values) = old_values {
|
||||
animation::update_transitions(
|
||||
&shared_context,
|
||||
new_animations_sender,
|
||||
if let Some(ref old_values) = old_values {
|
||||
let transitioning_properties = animation::start_transitions_if_applicable(
|
||||
shared_context,
|
||||
this_opaque,
|
||||
&values,
|
||||
old_values,
|
||||
new_values,
|
||||
&expired_transitions,
|
||||
&mut animation_state,
|
||||
);
|
||||
animation_state
|
||||
.cancel_transitions_with_nontransitioning_properties(&transitioning_properties);
|
||||
}
|
||||
|
||||
animation_state.finished_animations.clear();
|
||||
|
||||
// If the ElementAnimationState is empty, don't push it to save
|
||||
// memory and to avoid extra processing later.
|
||||
if !animation_state.is_empty() {
|
||||
animation_states.insert(this_opaque, animation_state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -601,67 +596,6 @@ trait PrivateMatchMethods: TElement {
|
|||
// properties, we can stop the cascade.
|
||||
ChildCascadeRequirement::MustCascadeChildrenIfInheritResetStyle
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
fn collect_and_update_style_for_expired_transitions(
|
||||
context: &SharedStyleContext,
|
||||
node: OpaqueNode,
|
||||
style: &mut Arc<ComputedValues>,
|
||||
expired_transitions: &mut Vec<crate::animation::PropertyAnimation>,
|
||||
) {
|
||||
use crate::animation::Animation;
|
||||
|
||||
let mut all_expired_animations = context.expired_animations.write();
|
||||
if let Some(animations) = all_expired_animations.remove(&node) {
|
||||
debug!("removing expired animations for {:?}", node);
|
||||
for animation in animations {
|
||||
debug!("Updating expired animation {:?}", animation);
|
||||
// TODO: support animation-fill-mode
|
||||
if let Animation::Transition(_, _, property_animation) = animation {
|
||||
property_animation.update(Arc::make_mut(style), 1.0);
|
||||
expired_transitions.push(property_animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
fn update_style_for_animations(
|
||||
context: &SharedStyleContext,
|
||||
node: OpaqueNode,
|
||||
style: &mut Arc<ComputedValues>,
|
||||
font_metrics: &dyn crate::font_metrics::FontMetricsProvider,
|
||||
) {
|
||||
use crate::animation::{self, Animation, AnimationUpdate};
|
||||
|
||||
let mut all_running_animations = context.running_animations.write();
|
||||
let running_animations = match all_running_animations.get_mut(&node) {
|
||||
Some(running_animations) => running_animations,
|
||||
None => return,
|
||||
};
|
||||
|
||||
for running_animation in running_animations.iter_mut() {
|
||||
let update = match *running_animation {
|
||||
Animation::Transition(..) => continue,
|
||||
Animation::Keyframes(..) => animation::update_style_for_animation::<Self>(
|
||||
context,
|
||||
running_animation,
|
||||
style,
|
||||
font_metrics,
|
||||
),
|
||||
};
|
||||
|
||||
match *running_animation {
|
||||
Animation::Transition(..) => unreachable!(),
|
||||
Animation::Keyframes(_, _, _, ref mut state) => match update {
|
||||
AnimationUpdate::Regular => {},
|
||||
AnimationUpdate::AnimationCanceled => {
|
||||
state.expired = true;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: TElement> PrivateMatchMethods for E {}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue