mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
style: Actually animate something!
So this actually allows some more animations to be triggered. The bad part of this is that they're actually triggered always when the style is recalculated, so we're going to have at least some more state into the node, and the constellation, which would have to keep track of the animations states.
This commit is contained in:
parent
18f09289ce
commit
0077eb147c
4 changed files with 141 additions and 25 deletions
|
@ -8,6 +8,8 @@ use app_units::Au;
|
||||||
use bezier::Bezier;
|
use bezier::Bezier;
|
||||||
use euclid::point::Point2D;
|
use euclid::point::Point2D;
|
||||||
use dom::{OpaqueNode, TRestyleDamage};
|
use dom::{OpaqueNode, TRestyleDamage};
|
||||||
|
use keyframes::KeyframesAnimation;
|
||||||
|
use keyframes::KeyframesStep;
|
||||||
use properties::animated_properties::{AnimatedProperty, TransitionProperty};
|
use properties::animated_properties::{AnimatedProperty, TransitionProperty};
|
||||||
use properties::longhands::transition_timing_function::computed_value::StartEnd;
|
use properties::longhands::transition_timing_function::computed_value::StartEnd;
|
||||||
use properties::longhands::transition_timing_function::computed_value::TransitionTimingFunction;
|
use properties::longhands::transition_timing_function::computed_value::TransitionTimingFunction;
|
||||||
|
@ -17,10 +19,22 @@ use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use time;
|
use time;
|
||||||
use values::computed::Time;
|
use values::computed::Time;
|
||||||
|
use selector_impl::SelectorImplExt;
|
||||||
|
use context::SharedStyleContext;
|
||||||
|
use selectors::matching::DeclarationBlock;
|
||||||
|
use properties;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum AnimationKind {
|
||||||
|
Transition,
|
||||||
|
Keyframe,
|
||||||
|
}
|
||||||
|
|
||||||
/// State relating to an animation.
|
/// State relating to an animation.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Animation {
|
pub struct Animation {
|
||||||
|
/// The kind of animation, either a transition or a keyframe.
|
||||||
|
pub kind: AnimationKind,
|
||||||
/// An opaque reference to the DOM node participating in the animation.
|
/// An opaque reference to the DOM node participating in the animation.
|
||||||
pub node: OpaqueNode,
|
pub node: OpaqueNode,
|
||||||
/// A description of the property animation that is occurring.
|
/// A description of the property animation that is occurring.
|
||||||
|
@ -164,7 +178,7 @@ pub fn start_transitions_if_applicable<C: ComputedValues>(new_animations_sender:
|
||||||
let mut had_animations = false;
|
let mut had_animations = false;
|
||||||
for i in 0..new_style.get_box().transition_count() {
|
for i in 0..new_style.get_box().transition_count() {
|
||||||
// Create any property animations, if applicable.
|
// Create any property animations, if applicable.
|
||||||
let property_animations = PropertyAnimation::from_transition(i, old_style.as_servo(), new_style.as_servo_mut());
|
let property_animations = PropertyAnimation::from_transition(i, old_style, new_style);
|
||||||
for property_animation in property_animations {
|
for property_animation in property_animations {
|
||||||
// Set the property to the initial value.
|
// Set the property to the initial value.
|
||||||
property_animation.update(new_style, 0.0);
|
property_animation.update(new_style, 0.0);
|
||||||
|
@ -175,6 +189,7 @@ pub fn start_transitions_if_applicable<C: ComputedValues>(new_animations_sender:
|
||||||
let start_time =
|
let start_time =
|
||||||
now + (box_style.transition_delay.0.get_mod(i).seconds() as f64);
|
now + (box_style.transition_delay.0.get_mod(i).seconds() as f64);
|
||||||
new_animations_sender.lock().unwrap().send(Animation {
|
new_animations_sender.lock().unwrap().send(Animation {
|
||||||
|
kind: AnimationKind::Transition,
|
||||||
node: node,
|
node: node,
|
||||||
property_animation: property_animation,
|
property_animation: property_animation,
|
||||||
start_time: start_time,
|
start_time: start_time,
|
||||||
|
@ -189,6 +204,99 @@ pub fn start_transitions_if_applicable<C: ComputedValues>(new_animations_sender:
|
||||||
had_animations
|
had_animations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn maybe_start_animations<Impl: SelectorImplExt>(context: &SharedStyleContext<Impl>,
|
||||||
|
node: OpaqueNode,
|
||||||
|
new_style: &Impl::ComputedValues) -> bool
|
||||||
|
{
|
||||||
|
let mut had_animations = false;
|
||||||
|
|
||||||
|
for (i, name) in new_style.as_servo().get_box().animation_name.0.iter().enumerate() {
|
||||||
|
debug!("maybe_start_animations: name={}", name);
|
||||||
|
let total_duration = new_style.as_servo().get_box().animation_duration.0.get_mod(i).seconds();
|
||||||
|
if total_duration == 0. {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This should be factored out, too much indentation.
|
||||||
|
if let Some(ref animation) = context.stylist.animations().get(&**name) {
|
||||||
|
debug!("maybe_start_animations: found animation {}", name);
|
||||||
|
had_animations = true;
|
||||||
|
let mut last_keyframe_style = compute_style_for_animation_step(context,
|
||||||
|
&animation.steps[0],
|
||||||
|
new_style);
|
||||||
|
// Apply the style inmediately. TODO: clone()...
|
||||||
|
// *new_style = last_keyframe_style.clone();
|
||||||
|
|
||||||
|
let mut ongoing_animation_percentage = animation.steps[0].duration_percentage.0;
|
||||||
|
let delay = new_style.as_servo().get_box().animation_delay.0.get_mod(i).seconds();
|
||||||
|
let animation_start = time::precise_time_s() + delay as f64;
|
||||||
|
|
||||||
|
// TODO: We can probably be smarter here and batch steps out or
|
||||||
|
// something.
|
||||||
|
for step in &animation.steps[1..] {
|
||||||
|
for transition_property in &animation.properties_changed {
|
||||||
|
debug!("maybe_start_animations: processing animation prop {:?} for animation {}", transition_property, name);
|
||||||
|
|
||||||
|
let new_keyframe_style = compute_style_for_animation_step(context,
|
||||||
|
step,
|
||||||
|
&last_keyframe_style);
|
||||||
|
// NB: This will get the previous frame timing function, or
|
||||||
|
// the old one if caught, which is what the spec says.
|
||||||
|
//
|
||||||
|
// We might need to reset to the initial timing function
|
||||||
|
// though.
|
||||||
|
let timing_function =
|
||||||
|
*last_keyframe_style.as_servo()
|
||||||
|
.get_box().animation_timing_function.0.get_mod(i);
|
||||||
|
|
||||||
|
let percentage = step.duration_percentage.0;
|
||||||
|
let this_keyframe_duration = total_duration * percentage;
|
||||||
|
if let Some(property_animation) = PropertyAnimation::from_transition_property(*transition_property,
|
||||||
|
timing_function,
|
||||||
|
Time(this_keyframe_duration),
|
||||||
|
&last_keyframe_style,
|
||||||
|
&new_keyframe_style) {
|
||||||
|
debug!("maybe_start_animations: got property animation for prop {:?}", transition_property);
|
||||||
|
|
||||||
|
let relative_start_time = ongoing_animation_percentage * total_duration;
|
||||||
|
let start_time = animation_start + relative_start_time as f64;
|
||||||
|
let end_time = start_time + (relative_start_time + this_keyframe_duration) as f64;
|
||||||
|
context.new_animations_sender.lock().unwrap().send(Animation {
|
||||||
|
kind: AnimationKind::Keyframe,
|
||||||
|
node: node,
|
||||||
|
property_animation: property_animation,
|
||||||
|
start_time: start_time,
|
||||||
|
end_time: end_time,
|
||||||
|
}).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
last_keyframe_style = new_keyframe_style;
|
||||||
|
ongoing_animation_percentage += percentage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
had_animations
|
||||||
|
}
|
||||||
|
|
||||||
/// Updates a single animation and associated style based on the current time. If `damage` is
|
/// Updates a single animation and associated style based on the current time. If `damage` is
|
||||||
/// provided, inserts the appropriate restyle damage.
|
/// provided, inserts the appropriate restyle damage.
|
||||||
pub fn update_style_for_animation<Damage: TRestyleDamage>(animation: &Animation,
|
pub fn update_style_for_animation<Damage: TRestyleDamage>(animation: &Animation,
|
||||||
|
@ -204,7 +312,7 @@ pub fn update_style_for_animation<Damage: TRestyleDamage>(animation: &Animation,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut new_style = (*style).clone();
|
let mut new_style = (*style).clone();
|
||||||
animation.property_animation.update(Arc::make_mut(&mut new_style).as_servo_mut(), progress);
|
animation.property_animation.update(Arc::make_mut(&mut new_style), progress);
|
||||||
if let Some(damage) = damage {
|
if let Some(damage) = damage {
|
||||||
*damage = *damage | Damage::compute(Some(style), &new_style);
|
*damage = *damage | Damage::compute(Some(style), &new_style);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub fn parse_keyframe_list(context: &ParserContext, input: &mut Parser) -> Resul
|
||||||
/// A number from 1 to 100, indicating the percentage of the animation where
|
/// A number from 1 to 100, indicating the percentage of the animation where
|
||||||
/// this keyframe should run.
|
/// this keyframe should run.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, HeapSizeOf)]
|
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, HeapSizeOf)]
|
||||||
pub struct KeyframePercentage(f32);
|
pub struct KeyframePercentage(pub f32);
|
||||||
|
|
||||||
impl ::std::cmp::Ord for KeyframePercentage {
|
impl ::std::cmp::Ord for KeyframePercentage {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -115,9 +115,9 @@ impl Keyframe {
|
||||||
pub struct KeyframesStep {
|
pub struct KeyframesStep {
|
||||||
/// The percentage of the animation duration that should be taken for this
|
/// The percentage of the animation duration that should be taken for this
|
||||||
/// step.
|
/// step.
|
||||||
duration_percentage: KeyframePercentage,
|
pub duration_percentage: KeyframePercentage,
|
||||||
/// Declarations that will determine the final style during the step.
|
/// Declarations that will determine the final style during the step.
|
||||||
declarations: Arc<Vec<PropertyDeclaration>>,
|
pub declarations: Arc<Vec<PropertyDeclaration>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyframesStep {
|
impl KeyframesStep {
|
||||||
|
@ -137,9 +137,9 @@ impl KeyframesStep {
|
||||||
/// It only takes into account animable properties.
|
/// It only takes into account animable properties.
|
||||||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||||||
pub struct KeyframesAnimation {
|
pub struct KeyframesAnimation {
|
||||||
steps: Vec<KeyframesStep>,
|
pub steps: Vec<KeyframesStep>,
|
||||||
/// The properties that change in this animation.
|
/// The properties that change in this animation.
|
||||||
properties_changed: Vec<TransitionProperty>,
|
pub properties_changed: Vec<TransitionProperty>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all the animated properties in a keyframes animation. Note that it's not
|
/// Get all the animated properties in a keyframes animation. Note that it's not
|
||||||
|
|
|
@ -27,6 +27,8 @@ use util::arc_ptr_eq;
|
||||||
use util::cache::{LRUCache, SimpleHashCache};
|
use util::cache::{LRUCache, SimpleHashCache};
|
||||||
use util::opts;
|
use util::opts;
|
||||||
use util::vec::ForgetfulSink;
|
use util::vec::ForgetfulSink;
|
||||||
|
use properties::longhands::animation_play_state::computed_value::AnimationPlayState;
|
||||||
|
use properties::style_struct_traits::Box;
|
||||||
|
|
||||||
fn create_common_style_affecting_attributes_from_element<E: TElement>(element: &E)
|
fn create_common_style_affecting_attributes_from_element<E: TElement>(element: &E)
|
||||||
-> CommonStyleAffectingAttributes {
|
-> CommonStyleAffectingAttributes {
|
||||||
|
@ -53,7 +55,9 @@ fn create_common_style_affecting_attributes_from_element<E: TElement>(element: &
|
||||||
|
|
||||||
pub struct ApplicableDeclarations<Impl: SelectorImplExt> {
|
pub struct ApplicableDeclarations<Impl: SelectorImplExt> {
|
||||||
pub normal: SmallVec<[DeclarationBlock; 16]>,
|
pub normal: SmallVec<[DeclarationBlock; 16]>,
|
||||||
pub per_pseudo: HashMap<Impl::PseudoElement, Vec<DeclarationBlock>, BuildHasherDefault<::fnv::FnvHasher>>,
|
pub per_pseudo: HashMap<Impl::PseudoElement,
|
||||||
|
Vec<DeclarationBlock>,
|
||||||
|
BuildHasherDefault<::fnv::FnvHasher>>,
|
||||||
|
|
||||||
/// Whether the `normal` declarations are shareable with other nodes.
|
/// Whether the `normal` declarations are shareable with other nodes.
|
||||||
pub normal_shareable: bool,
|
pub normal_shareable: bool,
|
||||||
|
@ -364,7 +368,7 @@ pub enum StyleSharingResult<ConcreteRestyleDamage: TRestyleDamage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PrivateMatchMethods: TNode
|
trait PrivateMatchMethods: TNode
|
||||||
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt {
|
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt<ComputedValues = Self::ConcreteComputedValues> {
|
||||||
/// Actually cascades style for a node or a pseudo-element of a node.
|
/// Actually cascades style for a node or a pseudo-element of a node.
|
||||||
///
|
///
|
||||||
/// Note that animations only apply to nodes or ::before or ::after
|
/// Note that animations only apply to nodes or ::before or ::after
|
||||||
|
@ -380,10 +384,8 @@ trait PrivateMatchMethods: TNode
|
||||||
animate_properties: bool)
|
animate_properties: bool)
|
||||||
-> (Self::ConcreteRestyleDamage, Arc<Self::ConcreteComputedValues>) {
|
-> (Self::ConcreteRestyleDamage, Arc<Self::ConcreteComputedValues>) {
|
||||||
let mut cacheable = true;
|
let mut cacheable = true;
|
||||||
let mut animations = None;
|
|
||||||
if animate_properties {
|
if animate_properties {
|
||||||
cacheable = !self.update_animations_for_cascade(context, &mut style) && cacheable;
|
cacheable = !self.update_animations_for_cascade(context, &mut style) && cacheable;
|
||||||
animations = Some(context.stylist.animations())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut this_style;
|
let mut this_style;
|
||||||
|
@ -420,12 +422,19 @@ trait PrivateMatchMethods: TNode
|
||||||
// it did trigger a transition.
|
// it did trigger a transition.
|
||||||
if animate_properties {
|
if animate_properties {
|
||||||
if let Some(ref style) = style {
|
if let Some(ref style) = style {
|
||||||
let animations_started =
|
let mut animations_started =
|
||||||
animation::start_transitions_if_applicable::<Self::ConcreteComputedValues>(
|
animation::start_transitions_if_applicable::<Self::ConcreteComputedValues>(
|
||||||
&context.new_animations_sender,
|
&context.new_animations_sender,
|
||||||
self.opaque(),
|
self.opaque(),
|
||||||
&**style,
|
&**style,
|
||||||
&mut this_style);
|
&mut this_style);
|
||||||
|
|
||||||
|
// TODO: Take into account animation-play-state
|
||||||
|
animations_started |= animation::maybe_start_animations::<<Self::ConcreteElement as Element>::Impl>(
|
||||||
|
&context,
|
||||||
|
self.opaque(),
|
||||||
|
&mut this_style);
|
||||||
|
|
||||||
cacheable = cacheable && !animations_started
|
cacheable = cacheable && !animations_started
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -462,7 +471,7 @@ trait PrivateMatchMethods: TNode
|
||||||
had_animations_to_expire = animations_to_expire.is_some();
|
had_animations_to_expire = animations_to_expire.is_some();
|
||||||
if let Some(ref animations) = animations_to_expire {
|
if let Some(ref animations) = animations_to_expire {
|
||||||
for animation in *animations {
|
for animation in *animations {
|
||||||
animation.property_animation.update(Arc::make_mut(style).as_servo_mut(), 1.0);
|
animation.property_animation.update(Arc::make_mut(style), 1.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -490,7 +499,8 @@ trait PrivateMatchMethods: TNode
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: TNode> PrivateMatchMethods for N
|
impl<N: TNode> PrivateMatchMethods for N
|
||||||
where <N::ConcreteElement as Element>::Impl: SelectorImplExt {}
|
where <N::ConcreteElement as Element>::Impl:
|
||||||
|
SelectorImplExt<ComputedValues = N::ConcreteComputedValues> {}
|
||||||
|
|
||||||
trait PrivateElementMatchMethods: TElement {
|
trait PrivateElementMatchMethods: TElement {
|
||||||
fn share_style_with_candidate_if_possible(&self,
|
fn share_style_with_candidate_if_possible(&self,
|
||||||
|
@ -648,7 +658,7 @@ pub trait MatchMethods : TNode {
|
||||||
local_context: &LocalStyleContext<Self::ConcreteComputedValues>,
|
local_context: &LocalStyleContext<Self::ConcreteComputedValues>,
|
||||||
parent: Option<Self>,
|
parent: Option<Self>,
|
||||||
applicable_declarations: &ApplicableDeclarations<<Self::ConcreteElement as Element>::Impl>)
|
applicable_declarations: &ApplicableDeclarations<<Self::ConcreteElement as Element>::Impl>)
|
||||||
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt {
|
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt<ComputedValues = Self::ConcreteComputedValues> {
|
||||||
// Get our parent's style. This must be unsafe so that we don't touch the parent's
|
// Get our parent's style. This must be unsafe so that we don't touch the parent's
|
||||||
// borrow flags.
|
// borrow flags.
|
||||||
//
|
//
|
||||||
|
|
|
@ -583,7 +583,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
|
||||||
pub use properties::longhands::transition_duration::{get_initial_value, parse, parse_one};
|
pub use properties::longhands::transition_duration::{get_initial_value, parse, parse_one};
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="animation-name" experimental="True">
|
<%helpers:longhand name="animation-name">
|
||||||
use values::computed::ComputedValueAsSpecified;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
|
|
||||||
pub mod computed_value {
|
pub mod computed_value {
|
||||||
|
@ -627,19 +627,19 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
|
||||||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="animation-duration" experimental="True">
|
<%helpers:longhand name="animation-duration">
|
||||||
pub use super::transition_duration::computed_value;
|
pub use super::transition_duration::computed_value;
|
||||||
pub use super::transition_duration::{parse, get_initial_value};
|
pub use super::transition_duration::{parse, get_initial_value};
|
||||||
pub use super::transition_duration::SpecifiedValue;
|
pub use super::transition_duration::SpecifiedValue;
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="animation-timing-function" experimental="True">
|
<%helpers:longhand name="animation-timing-function">
|
||||||
pub use super::transition_timing_function::computed_value;
|
pub use super::transition_timing_function::computed_value;
|
||||||
pub use super::transition_timing_function::{parse, get_initial_value};
|
pub use super::transition_timing_function::{parse, get_initial_value};
|
||||||
pub use super::transition_timing_function::SpecifiedValue;
|
pub use super::transition_timing_function::SpecifiedValue;
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="animation-iteration-count" experimental="True">
|
<%helpers:longhand name="animation-iteration-count">
|
||||||
use values::computed::ComputedValueAsSpecified;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
|
|
||||||
pub mod computed_value {
|
pub mod computed_value {
|
||||||
|
@ -709,18 +709,16 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
${helpers.keyword_list("animation-direction",
|
${helpers.keyword_list("animation-direction",
|
||||||
"normal reverse alternate alternate-reverse",
|
"normal reverse alternate alternate-reverse")}
|
||||||
experimental=True)}
|
|
||||||
|
|
||||||
${helpers.keyword_list("animation-play-state",
|
${helpers.keyword_list("animation-play-state",
|
||||||
"running paused",
|
"running paused",
|
||||||
experimental=True)}
|
need_clone=True)}
|
||||||
|
|
||||||
${helpers.keyword_list("animation-fill-mode",
|
${helpers.keyword_list("animation-fill-mode",
|
||||||
"none forwards backwards both",
|
"none forwards backwards both")}
|
||||||
experimental=True)}
|
|
||||||
|
|
||||||
<%helpers:longhand name="animation-delay" experimental="True">
|
<%helpers:longhand name="animation-delay">
|
||||||
pub use super::transition_duration::computed_value;
|
pub use super::transition_duration::computed_value;
|
||||||
pub use super::transition_duration::{parse, get_initial_value};
|
pub use super::transition_duration::{parse, get_initial_value};
|
||||||
pub use super::transition_duration::SpecifiedValue;
|
pub use super::transition_duration::SpecifiedValue;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue