mirror of
https://github.com/servo/servo.git
synced 2025-06-23 08:34:42 +01:00
Add support for transitionrun events
These events are triggered as soon as a transition is added to the list of running transitions. This will allow better test coverage while reworking the transitions and animations processing model.
This commit is contained in:
parent
0540c4a284
commit
d386d6d1f1
11 changed files with 105 additions and 93 deletions
|
@ -15,54 +15,62 @@ use script_traits::UntrustedNodeAddress;
|
|||
use script_traits::{
|
||||
AnimationState, ConstellationControlMsg, LayoutMsg as ConstellationMsg, TransitionEventType,
|
||||
};
|
||||
use style::animation::{update_style_for_animation, Animation, ElementAnimationState};
|
||||
use style::animation::{
|
||||
update_style_for_animation, Animation, ElementAnimationState, PropertyAnimation,
|
||||
};
|
||||
use style::dom::TElement;
|
||||
use style::font_metrics::ServoMetricsProvider;
|
||||
use style::selector_parser::RestyleDamage;
|
||||
use style::timer::Timer;
|
||||
|
||||
/// Collect newly transitioning nodes, which is used by the script process during
|
||||
/// forced, synchronous reflows to root DOM nodes for the duration of their transitions.
|
||||
pub fn collect_newly_transitioning_nodes(
|
||||
animation_states: &FxHashMap<OpaqueNode, ElementAnimationState>,
|
||||
mut out: Option<&mut Vec<UntrustedNodeAddress>>,
|
||||
) {
|
||||
// This extends the output vector with an iterator that contains a copy of the node
|
||||
// address for every new animation. This is a bit goofy, but the script thread
|
||||
// currently stores a rooted node for every property that is transitioning.
|
||||
if let Some(ref mut out) = out {
|
||||
out.extend(animation_states.iter().flat_map(|(node, state)| {
|
||||
let num_transitions = state
|
||||
.new_animations
|
||||
.iter()
|
||||
.filter(|animation| animation.is_transition())
|
||||
.count();
|
||||
std::iter::repeat(node.to_untrusted_node_address()).take(num_transitions)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes any new animations that were discovered after style recalculation. Also
|
||||
/// finish any animations that have completed, inserting them into `finished_animations`.
|
||||
pub fn update_animation_states<E>(
|
||||
pub fn update_animation_states(
|
||||
constellation_chan: &IpcSender<ConstellationMsg>,
|
||||
script_chan: &IpcSender<ConstellationControlMsg>,
|
||||
animation_states: &mut FxHashMap<OpaqueNode, ElementAnimationState>,
|
||||
invalid_nodes: FxHashSet<OpaqueNode>,
|
||||
mut newly_transitioning_nodes: Option<&mut Vec<UntrustedNodeAddress>>,
|
||||
pipeline_id: PipelineId,
|
||||
timer: &Timer,
|
||||
) where
|
||||
E: TElement,
|
||||
{
|
||||
) {
|
||||
let had_running_animations = animation_states
|
||||
.values()
|
||||
.any(|state| !state.running_animations.is_empty());
|
||||
|
||||
// Cancel all animations on any invalid nodes. These entries will later
|
||||
// be removed from the list of states, because their states will become
|
||||
// empty.
|
||||
for node in &invalid_nodes {
|
||||
if let Some(mut state) = animation_states.remove(node) {
|
||||
state.cancel_all_animations();
|
||||
send_events_for_cancelled_animations(script_chan, &mut state, pipeline_id);
|
||||
}
|
||||
}
|
||||
|
||||
let now = timer.seconds();
|
||||
let mut have_running_animations = false;
|
||||
for (node, animation_state) in animation_states.iter_mut() {
|
||||
// TODO(mrobinson): This should really be triggering transitionrun messages
|
||||
// on the script thread.
|
||||
if let Some(ref mut newly_transitioning_nodes) = newly_transitioning_nodes {
|
||||
let number_of_new_transitions = animation_state
|
||||
.new_animations
|
||||
.iter()
|
||||
.filter(|animation| animation.is_transition())
|
||||
.count();
|
||||
for _ in 0..number_of_new_transitions {
|
||||
newly_transitioning_nodes.push(node.to_untrusted_node_address());
|
||||
}
|
||||
}
|
||||
|
||||
update_animation_state::<E>(script_chan, animation_state, pipeline_id, now);
|
||||
|
||||
update_animation_state(script_chan, animation_state, pipeline_id, now, *node);
|
||||
have_running_animations =
|
||||
have_running_animations || !animation_state.running_animations.is_empty();
|
||||
}
|
||||
|
@ -85,16 +93,37 @@ pub fn update_animation_states<E>(
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn update_animation_state<E>(
|
||||
script_chan: &IpcSender<ConstellationControlMsg>,
|
||||
pub fn update_animation_state(
|
||||
script_channel: &IpcSender<ConstellationControlMsg>,
|
||||
animation_state: &mut ElementAnimationState,
|
||||
pipeline_id: PipelineId,
|
||||
now: f64,
|
||||
) where
|
||||
E: TElement,
|
||||
{
|
||||
send_events_for_cancelled_animations(script_chan, animation_state, pipeline_id);
|
||||
node: OpaqueNode,
|
||||
) {
|
||||
let send_transition_event = |property_animation: &PropertyAnimation, event_type| {
|
||||
script_channel
|
||||
.send(ConstellationControlMsg::TransitionEvent {
|
||||
pipeline_id,
|
||||
event_type,
|
||||
node: node.to_untrusted_node_address(),
|
||||
property_name: property_animation.property_name().into(),
|
||||
elapsed_time: property_animation.duration,
|
||||
})
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
handle_cancelled_animations(animation_state, send_transition_event);
|
||||
handle_running_animations(animation_state, now, send_transition_event);
|
||||
handle_new_animations(animation_state, send_transition_event);
|
||||
}
|
||||
|
||||
/// Walk through the list of running animations and remove all of the ones that
|
||||
/// have ended.
|
||||
pub fn handle_running_animations(
|
||||
animation_state: &mut ElementAnimationState,
|
||||
now: f64,
|
||||
mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType),
|
||||
) {
|
||||
let mut running_animations =
|
||||
std::mem::replace(&mut animation_state.running_animations, Vec::new());
|
||||
for mut running_animation in running_animations.drain(..) {
|
||||
|
@ -115,46 +144,25 @@ pub fn update_animation_state<E>(
|
|||
animation_state.running_animations.push(running_animation);
|
||||
} else {
|
||||
debug!("Finishing transition: {:?}", running_animation);
|
||||
if let Animation::Transition(node, _, ref property_animation) = running_animation {
|
||||
script_chan
|
||||
.send(ConstellationControlMsg::TransitionEvent {
|
||||
pipeline_id,
|
||||
event_type: TransitionEventType::TransitionEnd,
|
||||
node: node.to_untrusted_node_address(),
|
||||
property_name: property_animation.property_name().into(),
|
||||
elapsed_time: property_animation.duration,
|
||||
})
|
||||
.unwrap();
|
||||
if let Animation::Transition(_, _, ref property_animation) = running_animation {
|
||||
send_transition_event(property_animation, TransitionEventType::TransitionEnd);
|
||||
}
|
||||
animation_state.finished_animations.push(running_animation);
|
||||
}
|
||||
}
|
||||
|
||||
animation_state
|
||||
.running_animations
|
||||
.append(&mut animation_state.new_animations);
|
||||
}
|
||||
|
||||
/// Send events for cancelled animations. Currently this only handles cancelled
|
||||
/// transitions, but eventually this should handle cancelled CSS animations as
|
||||
/// well.
|
||||
pub fn send_events_for_cancelled_animations(
|
||||
script_channel: &IpcSender<ConstellationControlMsg>,
|
||||
pub fn handle_cancelled_animations(
|
||||
animation_state: &mut ElementAnimationState,
|
||||
pipeline_id: PipelineId,
|
||||
mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType),
|
||||
) {
|
||||
for animation in animation_state.cancelled_animations.drain(..) {
|
||||
match animation {
|
||||
Animation::Transition(node, _, ref property_animation) => {
|
||||
script_channel
|
||||
.send(ConstellationControlMsg::TransitionEvent {
|
||||
pipeline_id,
|
||||
event_type: TransitionEventType::TransitionCancel,
|
||||
node: node.to_untrusted_node_address(),
|
||||
property_name: property_animation.property_name().into(),
|
||||
elapsed_time: property_animation.duration,
|
||||
})
|
||||
.unwrap();
|
||||
Animation::Transition(_, _, ref property_animation) => {
|
||||
send_transition_event(property_animation, TransitionEventType::TransitionCancel)
|
||||
},
|
||||
Animation::Keyframes(..) => {
|
||||
warn!("Got unexpected animation in finished transitions list.")
|
||||
|
@ -163,6 +171,21 @@ pub fn send_events_for_cancelled_animations(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn handle_new_animations(
|
||||
animation_state: &mut ElementAnimationState,
|
||||
mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType),
|
||||
) {
|
||||
for animation in animation_state.new_animations.drain(..) {
|
||||
match animation {
|
||||
Animation::Transition(_, _, ref property_animation) => {
|
||||
send_transition_event(property_animation, TransitionEventType::TransitionRun)
|
||||
},
|
||||
Animation::Keyframes(..) => {},
|
||||
}
|
||||
animation_state.running_animations.push(animation);
|
||||
}
|
||||
}
|
||||
|
||||
/// Recalculates style for a set of animations. This does *not* run with the DOM
|
||||
/// lock held. Returns a set of nodes associated with animations that are no longer
|
||||
/// valid.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue