aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout/animation.rs
blob: 258ac7d0a39046e40399bc32466a3b4110d3b07a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/* 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)
        }
    }
}