mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
style: Refactor the @keyframes parsing and add adequate computation for it.
This commit is contained in:
parent
6a362ae8e8
commit
058bfb39ae
4 changed files with 103 additions and 43 deletions
|
@ -3,9 +3,11 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use cssparser::{Parser, Delimiter};
|
||||
use selectors::matching::DeclarationBlock;
|
||||
use parser::ParserContext;
|
||||
use properties::{ComputedValues, PropertyDeclarationBlock, parse_property_declaration_list};
|
||||
use properties::animated_properties::AnimatedProperty;
|
||||
use properties::{ComputedValues, PropertyDeclaration, PropertyDeclarationBlock, parse_property_declaration_list};
|
||||
use properties::animated_properties::TransitionProperty;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Parses a keyframes list, like:
|
||||
/// 0%, 50% {
|
||||
|
@ -30,9 +32,19 @@ pub fn parse_keyframe_list(context: &ParserContext, input: &mut Parser) -> Resul
|
|||
|
||||
/// A number from 1 to 100, indicating the percentage of the animation where
|
||||
/// this keyframe should run.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, HeapSizeOf)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, HeapSizeOf)]
|
||||
pub struct KeyframePercentage(f32);
|
||||
|
||||
impl ::std::cmp::Ord for KeyframePercentage {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
|
||||
// We know we have a number from 0 to 1, so unwrap() here is safe.
|
||||
self.0.partial_cmp(&other.0).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::cmp::Eq for KeyframePercentage { }
|
||||
|
||||
impl KeyframePercentage {
|
||||
#[inline]
|
||||
pub fn new(value: f32) -> KeyframePercentage {
|
||||
|
@ -73,7 +85,7 @@ impl KeyframeSelector {
|
|||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||||
pub struct Keyframe {
|
||||
pub selector: KeyframeSelector,
|
||||
pub declarations: PropertyDeclarationBlock,
|
||||
pub declarations: Arc<Vec<PropertyDeclaration>>,
|
||||
}
|
||||
|
||||
impl Keyframe {
|
||||
|
@ -89,25 +101,33 @@ impl Keyframe {
|
|||
Ok(parse_property_declaration_list(context, input))
|
||||
}).unwrap();
|
||||
|
||||
// NB: Other browsers seem to ignore important declarations in keyframe
|
||||
// animations too.
|
||||
Ok(Keyframe {
|
||||
selector: selector,
|
||||
declarations: declarations,
|
||||
declarations: declarations.normal,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A single step from a keyframe animation.
|
||||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||||
pub struct ComputedKeyframesStep<C: ComputedValues> {
|
||||
pub struct KeyframesStep {
|
||||
/// The percentage of the animation duration that should be taken for this
|
||||
/// step.
|
||||
duration_percentage: KeyframePercentage,
|
||||
// XXX: Can we optimise this? Probably not such a show-stopper... Probably
|
||||
// storing declared values could work/should be the thing to do?
|
||||
/// The computed values at the beginning of the step.
|
||||
begin: C,
|
||||
/// The computed values at the end of the step.
|
||||
end: C,
|
||||
/// Declarations that will determine the final style during the step.
|
||||
declarations: Arc<Vec<PropertyDeclaration>>,
|
||||
}
|
||||
|
||||
impl KeyframesStep {
|
||||
fn new(percentage: KeyframePercentage,
|
||||
declarations: Arc<Vec<PropertyDeclaration>>) -> Self {
|
||||
KeyframesStep {
|
||||
duration_percentage: percentage,
|
||||
declarations: declarations,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This structure represents a list of animation steps computed from the list
|
||||
|
@ -115,19 +135,32 @@ pub struct ComputedKeyframesStep<C: ComputedValues> {
|
|||
///
|
||||
/// It only takes into account animable properties.
|
||||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||||
pub struct ComputedKeyframesAnimation<C: ComputedValues> {
|
||||
steps: Vec<ComputedKeyframesStep<C>>,
|
||||
pub struct KeyframesAnimation {
|
||||
steps: Vec<KeyframesStep>,
|
||||
/// The properties that change in this animation.
|
||||
properties_changed: Vec<AnimatedProperty>,
|
||||
properties_changed: Vec<TransitionProperty>,
|
||||
}
|
||||
|
||||
fn get_animated_properties(keyframe: &Keyframe) -> Vec<AnimatedProperty> {
|
||||
// TODO
|
||||
vec![]
|
||||
/// Get all the animated properties in a keyframes animation. Note that it's not
|
||||
/// defined what happens when a property is not on a keyframe, so we only peek
|
||||
/// the props of the first one.
|
||||
///
|
||||
/// In practice, browsers seem to try to do their best job at it, so we might
|
||||
/// want to go through all the actual keyframes and deduplicate properties.
|
||||
fn get_animated_properties(keyframe: &Keyframe) -> Vec<TransitionProperty> {
|
||||
let mut ret = vec![];
|
||||
// NB: declarations are already deduplicated, so we don't have to check for
|
||||
// it here.
|
||||
for declaration in keyframe.declarations.iter() {
|
||||
if let Some(property) = TransitionProperty::from_declaration(&declaration) {
|
||||
ret.push(property);
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
impl<C: ComputedValues> ComputedKeyframesAnimation<C> {
|
||||
pub fn from_keyframes(keyframes: &[Keyframe]) -> Option<ComputedKeyframesAnimation<C>> {
|
||||
impl KeyframesAnimation {
|
||||
pub fn from_keyframes(keyframes: &[Keyframe]) -> Option<Self> {
|
||||
debug_assert!(keyframes.len() > 1);
|
||||
let mut steps = vec![];
|
||||
|
||||
|
@ -135,18 +168,37 @@ impl<C: ComputedValues> ComputedKeyframesAnimation<C> {
|
|||
// appeareance, then sorting them, then updating with the real
|
||||
// "duration_percentage".
|
||||
let mut animated_properties = get_animated_properties(&keyframes[0]);
|
||||
|
||||
if animated_properties.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
for keyframe in keyframes {
|
||||
for step in keyframe.selector.0.iter() {
|
||||
|
||||
for percentage in keyframe.selector.0.iter() {
|
||||
steps.push(KeyframesStep::new(*percentage,
|
||||
keyframe.declarations.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Some(ComputedKeyframesAnimation {
|
||||
steps.sort_by_key(|step| step.duration_percentage);
|
||||
|
||||
if steps[0].duration_percentage != KeyframePercentage(0.0) {
|
||||
// TODO: we could just insert a step from 0 and without declarations
|
||||
// so we won't animate at the beginning. Seems like what other
|
||||
// engines do, but might be a bit tricky so I'd rather leave it as a
|
||||
// follow-up.
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut remaining = 1.0;
|
||||
let mut last_step_end = 0.0;
|
||||
debug_assert!(steps.len() > 1);
|
||||
for current_step in &mut steps[1..] {
|
||||
let new_duration_percentage = KeyframePercentage(current_step.duration_percentage.0 - last_step_end);
|
||||
last_step_end = current_step.duration_percentage.0;
|
||||
current_step.duration_percentage = new_duration_percentage;
|
||||
}
|
||||
|
||||
Some(KeyframesAnimation {
|
||||
steps: steps,
|
||||
properties_changed: animated_properties,
|
||||
})
|
||||
|
|
|
@ -7,6 +7,7 @@ use bezier::Bezier;
|
|||
use cssparser::{Color as CSSParserColor, Parser, RGBA, ToCss};
|
||||
use dom::{OpaqueNode, TRestyleDamage};
|
||||
use euclid::{Point2D, Size2D};
|
||||
use properties::PropertyDeclaration;
|
||||
use properties::longhands;
|
||||
use properties::longhands::background_position::computed_value::T as BackgroundPosition;
|
||||
use properties::longhands::background_size::computed_value::T as BackgroundSize;
|
||||
|
@ -68,6 +69,17 @@ impl TransitionProperty {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_declaration(declaration: &PropertyDeclaration) -> Option<Self> {
|
||||
match *declaration {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
PropertyDeclaration::${prop.camel_case}(..)
|
||||
=> Some(TransitionProperty::${prop.camel_case}),
|
||||
% endif
|
||||
% endfor
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for TransitionProperty {
|
||||
|
|
|
@ -8,7 +8,7 @@ use dom::PresentationalHintsSynthetizer;
|
|||
use element_state::*;
|
||||
use error_reporting::StdoutErrorReporter;
|
||||
use keyframes::Keyframe;
|
||||
use keyframes::ComputedKeyframesAnimation;
|
||||
use keyframes::KeyframesAnimation;
|
||||
use media_queries::{Device, MediaType};
|
||||
use parser::ParserContextExtraData;
|
||||
use properties::{self, PropertyDeclaration, PropertyDeclarationBlock};
|
||||
|
@ -129,7 +129,7 @@ pub struct Stylist<Impl: SelectorImplExt> {
|
|||
BuildHasherDefault<::fnv::FnvHasher>>,
|
||||
|
||||
/// A map with all the animations indexed by name.
|
||||
animations: HashMap<String, ComputedKeyframesAnimation<Impl::ComputedValues>>,
|
||||
animations: HashMap<String, KeyframesAnimation>,
|
||||
|
||||
/// Applicable declarations for a given non-eagerly cascaded pseudo-element.
|
||||
/// These are eagerly computed once, and then used to resolve the new
|
||||
|
@ -253,9 +253,11 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
|||
self.rules_source_order = rules_source_order;
|
||||
}
|
||||
CSSRule::Keyframes(ref keyframes_rule) => {
|
||||
if let Some(computed) = ComputedKeyframesAnimation::from_keyframes(&keyframes_rule.keyframes) {
|
||||
debug!("Found valid keyframes rule: {:?}", keyframes_rule);
|
||||
if let Some(animation) = KeyframesAnimation::from_keyframes(&keyframes_rule.keyframes) {
|
||||
debug!("Found valid keyframe animation: {:?}", animation);
|
||||
self.animations.insert(keyframes_rule.name.clone(),
|
||||
computed);
|
||||
animation);
|
||||
}
|
||||
}
|
||||
// We don't care about any other rule.
|
||||
|
@ -459,7 +461,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn animations(&self) -> &HashMap<String, ComputedKeyframesAnimation<Impl::ComputedValues>> {
|
||||
pub fn animations(&self) -> &HashMap<String, KeyframesAnimation> {
|
||||
&self.animations
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,24 +156,18 @@ fn test_parse_stylesheet() {
|
|||
Keyframe {
|
||||
selector: KeyframeSelector::new_for_unit_testing(
|
||||
vec![KeyframePercentage::new(0.)]),
|
||||
declarations: PropertyDeclarationBlock {
|
||||
normal: Arc::new(vec![
|
||||
PropertyDeclaration::Width(DeclaredValue::Value(
|
||||
LengthOrPercentageOrAuto::Percentage(Percentage(0.)))),
|
||||
]),
|
||||
important: Arc::new(vec![]),
|
||||
}
|
||||
declarations: Arc::new(vec![
|
||||
PropertyDeclaration::Width(DeclaredValue::Value(
|
||||
LengthOrPercentageOrAuto::Percentage(Percentage(0.)))),
|
||||
]),
|
||||
},
|
||||
Keyframe {
|
||||
selector: KeyframeSelector::new_for_unit_testing(
|
||||
vec![KeyframePercentage::new(1.)]),
|
||||
declarations: PropertyDeclarationBlock {
|
||||
normal: Arc::new(vec![
|
||||
PropertyDeclaration::Width(DeclaredValue::Value(
|
||||
LengthOrPercentageOrAuto::Percentage(Percentage(1.)))),
|
||||
]),
|
||||
important: Arc::new(vec![]),
|
||||
}
|
||||
declarations: Arc::new(vec![
|
||||
PropertyDeclaration::Width(DeclaredValue::Value(
|
||||
LengthOrPercentageOrAuto::Percentage(Percentage(1.)))),
|
||||
]),
|
||||
},
|
||||
]
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue