style: Fix parsing and add generated keyframes

This commit is contained in:
Emilio Cobos Álvarez 2016-06-27 08:52:33 -07:00
parent 46eec45886
commit 2d566ef0ef
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
10 changed files with 189 additions and 94 deletions

View file

@ -4,12 +4,11 @@
//! CSS transitions and animations.
use app_units::Au;
use bezier::Bezier;
use context::SharedStyleContext;
use dom::{OpaqueNode, TRestyleDamage};
use euclid::point::Point2D;
use keyframes::KeyframesStep;
use keyframes::{KeyframesStep, KeyframesStepValue};
use properties::animated_properties::{AnimatedProperty, TransitionProperty};
use properties::longhands::animation_direction::computed_value::AnimationDirection;
use properties::longhands::animation_iteration_count::computed_value::AnimationIterationCount;
@ -54,7 +53,7 @@ pub enum KeyframesRunningState {
/// playing or paused).
// TODO: unify the use of f32/f64 in this file.
#[derive(Debug, Clone)]
pub struct KeyframesAnimationState {
pub struct KeyframesAnimationState<Impl: SelectorImplExt> {
/// The time this animation started at.
pub started_at: f64,
/// The duration of this animation.
@ -71,9 +70,12 @@ pub struct KeyframesAnimationState {
pub current_direction: AnimationDirection,
/// Werther this keyframe animation is outdated due to a restyle.
pub expired: bool,
/// The original cascade style, needed to compute the generated keyframes of
/// the animation.
pub cascade_style: Arc<Impl::ComputedValues>,
}
impl KeyframesAnimationState {
impl<Impl: SelectorImplExt> KeyframesAnimationState<Impl> {
/// Performs a tick in the animation state, i.e., increments the counter of
/// the current iteration count, updates times and then toggles the
/// direction if appropriate.
@ -121,7 +123,8 @@ impl KeyframesAnimationState {
///
/// There are some bits of state we can't just replace, over all taking in
/// account times, so here's that logic.
pub fn update_from_other(&mut self, other: &Self) {
pub fn update_from_other(&mut self, other: &Self)
where Self: Clone {
use self::KeyframesRunningState::*;
debug!("KeyframesAnimationState::update_from_other({:?}, {:?})", self, other);
@ -167,7 +170,7 @@ impl KeyframesAnimationState {
/// State relating to an animation.
#[derive(Clone, Debug)]
pub enum Animation {
pub enum Animation<Impl: SelectorImplExt> {
/// A transition is just a single frame triggered at a time, with a reflow.
///
/// the f64 field is the start time as returned by `time::precise_time_s()`.
@ -176,10 +179,10 @@ pub enum Animation {
Transition(OpaqueNode, f64, AnimationFrame, bool),
/// A keyframes animation is identified by a name, and can have a
/// node-dependent state (i.e. iteration count, etc.).
Keyframes(OpaqueNode, Atom, KeyframesAnimationState),
Keyframes(OpaqueNode, Atom, KeyframesAnimationState<Impl>),
}
impl Animation {
impl<Impl: SelectorImplExt> Animation<Impl> {
#[inline]
pub fn mark_as_expired(&mut self) {
debug_assert!(!self.is_expired());
@ -344,18 +347,20 @@ impl<T> GetMod for Vec<T> {
//
// TODO(emilio): Take rid of this mutex splitting SharedLayoutContex into a
// cloneable part and a non-cloneable part..
pub fn start_transitions_if_applicable<C: ComputedValues>(new_animations_sender: &Mutex<Sender<Animation>>,
node: OpaqueNode,
old_style: &C,
new_style: &mut C)
-> bool {
pub fn start_transitions_if_applicable<Impl: SelectorImplExt>(new_animations_sender: &Mutex<Sender<Animation<Impl>>>,
node: OpaqueNode,
old_style: &Impl::ComputedValues,
new_style: &mut Arc<Impl::ComputedValues>)
-> bool {
let mut had_animations = false;
for i in 0..new_style.get_box().transition_count() {
// Create any property animations, if applicable.
let property_animations = PropertyAnimation::from_transition(i, old_style, new_style);
let property_animations = PropertyAnimation::from_transition(i, old_style, Arc::make_mut(new_style));
for property_animation in property_animations {
// Set the property to the initial value.
property_animation.update(new_style, 0.0);
// NB: get_mut is guaranteed to succeed since we called make_mut()
// above.
property_animation.update(Arc::get_mut(new_style).unwrap(), 0.0);
// Kick off the animation.
let now = time::precise_time_s();
@ -378,24 +383,33 @@ pub fn start_transitions_if_applicable<C: ComputedValues>(new_animations_sender:
fn compute_style_for_animation_step<Impl: SelectorImplExt>(context: &SharedStyleContext<Impl>,
step: &KeyframesStep,
old_style: &Impl::ComputedValues) -> Impl::ComputedValues {
let declaration_block = DeclarationBlock {
declarations: step.declarations.clone(),
source_order: 0,
specificity: ::std::u32::MAX,
};
let (computed, _) = properties::cascade(context.viewport_size,
&[declaration_block],
false,
Some(old_style),
None,
context.error_reporter.clone());
computed
previous_style: &Impl::ComputedValues,
style_from_cascade: &Impl::ComputedValues)
-> Impl::ComputedValues {
match step.value {
// TODO: avoiding this spurious clone might involve having to create
// an Arc in the below (more common case).
KeyframesStepValue::ComputedValues => style_from_cascade.clone(),
KeyframesStepValue::Declarations(ref declarations) => {
let declaration_block = DeclarationBlock {
declarations: declarations.clone(),
source_order: 0,
specificity: ::std::u32::MAX,
};
let (computed, _) = properties::cascade(context.viewport_size,
&[declaration_block],
false,
Some(previous_style),
None,
context.error_reporter.clone());
computed
}
}
}
pub fn maybe_start_animations<Impl: SelectorImplExt>(context: &SharedStyleContext<Impl>,
node: OpaqueNode,
new_style: &Impl::ComputedValues) -> bool
new_style: &Arc<Impl::ComputedValues>) -> bool
{
let mut had_animations = false;
@ -407,8 +421,17 @@ pub fn maybe_start_animations<Impl: SelectorImplExt>(context: &SharedStyleContex
continue
}
if context.stylist.animations().get(&name).is_some() {
if let Some(ref anim) = context.stylist.animations().get(&name) {
debug!("maybe_start_animations: animation {} found", name);
// If this animation doesn't have any keyframe, we can just continue
// without submitting it to the compositor, since both the first and
// the second keyframes would be synthetised from the computed
// values.
if anim.steps.is_empty() {
continue;
}
let delay = box_style.animation_delay.0.get_mod(i).seconds();
let now = time::precise_time_s();
let animation_start = now + delay as f64;
@ -444,6 +467,7 @@ pub fn maybe_start_animations<Impl: SelectorImplExt>(context: &SharedStyleContex
direction: animation_direction,
current_direction: initial_direction,
expired: false,
cascade_style: new_style.clone(),
})).unwrap();
had_animations = true;
}
@ -474,7 +498,7 @@ pub fn update_style_for_animation_frame<C: ComputedValues>(mut new_style: &mut A
/// Updates a single animation and associated style based on the current time.
/// If `damage` is provided, inserts the appropriate restyle damage.
pub fn update_style_for_animation<Damage, Impl>(context: &SharedStyleContext<Impl>,
animation: &Animation,
animation: &Animation<Impl>,
style: &mut Arc<Damage::ConcreteComputedValues>,
damage: Option<&mut Damage>)
where Impl: SelectorImplExt,
@ -516,6 +540,8 @@ where Impl: SelectorImplExt,
Some(animation) => animation,
};
debug_assert!(!animation.steps.is_empty());
let maybe_index = style.as_servo()
.get_box().animation_name.0.iter()
.position(|animation_name| name == animation_name);
@ -579,8 +605,6 @@ where Impl: SelectorImplExt,
let target_keyframe = match target_keyframe_position {
Some(target) => &animation.steps[target],
None => {
// TODO: The 0. case falls here, maybe we should just resort
// to the first keyframe instead.
warn!("update_style_for_animation: No current keyframe found for animation \"{}\" at progress {}",
name, total_progress);
return;
@ -605,7 +629,8 @@ where Impl: SelectorImplExt,
// TODO: How could we optimise it? Is it such a big deal?
let from_style = compute_style_for_animation_step(context,
last_keyframe,
&**style);
&**style,
&state.cascade_style);
// NB: The spec says that the timing function can be overwritten
// from the keyframe style.
@ -616,7 +641,8 @@ where Impl: SelectorImplExt,
let target_style = compute_style_for_animation_step(context,
target_keyframe,
&from_style);
&from_style,
&state.cascade_style);
let mut new_style = (*style).clone();