mirror of
https://github.com/servo/servo.git
synced 2025-06-12 10:24:43 +00:00
Transition events are not yet supported, and the only animatable properties are `top`, `right`, `bottom`, and `left`. However, all other features of transitions are supported. There are no automated tests at present because I'm not sure how best to test it, but three manual tests are included.
104 lines
4.1 KiB
Rust
104 lines
4.1 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
//! CSS transitions and animations.
|
|
|
|
use flow::{self, Flow};
|
|
use incremental::{self, RestyleDamage};
|
|
|
|
use clock_ticks;
|
|
use gfx::display_list::OpaqueNode;
|
|
use layout_task::{LayoutTask, LayoutTaskData};
|
|
use msg::constellation_msg::{Msg, PipelineId};
|
|
use script::layout_interface::Animation;
|
|
use std::mem;
|
|
use std::sync::mpsc::Sender;
|
|
use style::animation::{GetMod, PropertyAnimation};
|
|
use style::properties::ComputedValues;
|
|
|
|
/// Inserts transitions into the queue of running animations as applicable for the given style
|
|
/// difference. This is called from the layout worker threads.
|
|
pub fn start_transitions_if_applicable(new_animations_sender: &Sender<Animation>,
|
|
node: OpaqueNode,
|
|
old_style: &ComputedValues,
|
|
new_style: &mut ComputedValues) {
|
|
for i in range(0, new_style.get_animation().transition_property.0.len()) {
|
|
// Create any property animations, if applicable.
|
|
let property_animations = PropertyAnimation::from_transition(i, old_style, new_style);
|
|
for property_animation in property_animations.into_iter() {
|
|
// Set the property to the initial value.
|
|
property_animation.update(new_style, 0.0);
|
|
|
|
// Kick off the animation.
|
|
let now = clock_ticks::precise_time_s();
|
|
let animation_style = new_style.get_animation();
|
|
let start_time = now + animation_style.transition_delay.0.get_mod(i).seconds();
|
|
new_animations_sender.send(Animation {
|
|
node: node.id(),
|
|
property_animation: property_animation,
|
|
start_time: start_time,
|
|
end_time: start_time +
|
|
animation_style.transition_duration.0.get_mod(i).seconds(),
|
|
}).unwrap()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Processes any new animations that were discovered after style recalculation.
|
|
pub fn process_new_animations(rw_data: &mut LayoutTaskData, pipeline_id: PipelineId) {
|
|
while let Ok(animation) = rw_data.new_animations_receiver.try_recv() {
|
|
rw_data.running_animations.push(animation)
|
|
}
|
|
|
|
let animations_are_running = !rw_data.running_animations.is_empty();
|
|
rw_data.constellation_chan
|
|
.0
|
|
.send(Msg::ChangeRunningAnimationsState(pipeline_id, animations_are_running))
|
|
.unwrap();
|
|
}
|
|
|
|
/// Recalculates style for an animation. This does *not* run with the DOM lock held.
|
|
pub fn recalc_style_for_animation(flow: &mut Flow, animation: &Animation) {
|
|
let mut damage = RestyleDamage::empty();
|
|
flow.mutate_fragments(&mut |fragment| {
|
|
if fragment.node.id() != animation.node {
|
|
return
|
|
}
|
|
|
|
let now = clock_ticks::precise_time_s();
|
|
let mut progress = (now - animation.start_time) / animation.duration();
|
|
if progress > 1.0 {
|
|
progress = 1.0
|
|
}
|
|
if progress <= 0.0 {
|
|
return
|
|
}
|
|
|
|
let mut new_style = fragment.style.clone();
|
|
animation.property_animation.update(&mut *new_style.make_unique(), progress);
|
|
damage.insert(incremental::compute_damage(&Some(fragment.style.clone()), &new_style));
|
|
fragment.style = new_style
|
|
});
|
|
|
|
let base = flow::mut_base(flow);
|
|
base.restyle_damage.insert(damage);
|
|
for kid in base.children.iter_mut() {
|
|
recalc_style_for_animation(kid, animation)
|
|
}
|
|
}
|
|
|
|
/// Handles animation updates.
|
|
pub fn tick_all_animations(layout_task: &LayoutTask, rw_data: &mut LayoutTaskData) {
|
|
let running_animations = mem::replace(&mut rw_data.running_animations, Vec::new());
|
|
let now = clock_ticks::precise_time_s();
|
|
for running_animation in running_animations.into_iter() {
|
|
layout_task.tick_animation(running_animation, rw_data);
|
|
|
|
if now < running_animation.end_time {
|
|
// Keep running the animation if it hasn't expired.
|
|
rw_data.running_animations.push(running_animation)
|
|
}
|
|
}
|
|
}
|
|
|