diff --git a/servo/components/style/animation.rs b/servo/components/style/animation.rs index 7bcd5c620672..37c0cbbdc605 100644 --- a/servo/components/style/animation.rs +++ b/servo/components/style/animation.rs @@ -7,10 +7,8 @@ // NOTE(emilio): This code isn't really executed in Gecko, but we don't want to // compile it out so that people remember it exists. -use crate::bezier::Bezier; use crate::context::{CascadeInputs, SharedStyleContext}; use crate::dom::{OpaqueNode, TDocument, TElement, TNode}; -use crate::piecewise_linear::PiecewiseLinearFunction; use crate::properties::animated_properties::{AnimationValue, AnimationValueMap}; use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection; use crate::properties::longhands::animation_fill_mode::computed_value::single_value::T as AnimationFillMode; @@ -27,12 +25,8 @@ use crate::style_resolver::StyleResolverForElement; use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue}; use crate::stylesheets::layer_rule::LayerOrder; use crate::values::animated::{Animate, Procedure}; -use crate::values::computed::easing::ComputedLinearStop; use crate::values::computed::{Time, TimingFunction}; use crate::values::generics::box_::AnimationIterationCount; -use crate::values::generics::easing::{ - StepPosition, TimingFunction as GenericTimingFunction, TimingKeyword, -}; use crate::Atom; use fxhash::FxHashMap; use parking_lot::RwLock; @@ -90,67 +84,7 @@ impl PropertyAnimation { /// The output of the timing function given the progress ration of this animation. fn timing_function_output(&self, progress: f64) -> f64 { let epsilon = 1. / (200. * self.duration); - match &self.timing_function { - GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => { - Bezier::new(*x1, *y1, *x2, *y2).solve(progress, epsilon) - }, - GenericTimingFunction::Steps(steps, pos) => { - let mut current_step = (progress * (*steps as f64)).floor() as i32; - - if *pos == StepPosition::Start || - *pos == StepPosition::JumpStart || - *pos == StepPosition::JumpBoth - { - current_step = current_step + 1; - } - - // FIXME: We should update current_step according to the "before flag". - // In order to get the before flag, we have to know the current animation phase - // and whether the iteration is reversed. For now, we skip this calculation. - // (i.e. Treat before_flag is unset,) - // https://drafts.csswg.org/css-easing/#step-timing-function-algo - - if progress >= 0.0 && current_step < 0 { - current_step = 0; - } - - let jumps = match pos { - StepPosition::JumpBoth => *steps + 1, - StepPosition::JumpNone => *steps - 1, - StepPosition::JumpStart | - StepPosition::JumpEnd | - StepPosition::Start | - StepPosition::End => *steps, - }; - - if progress <= 1.0 && current_step > jumps { - current_step = jumps; - } - - (current_step as f64) / (jumps as f64) - }, - GenericTimingFunction::LinearFunction(elements) => { - // TODO(dshin): For servo, which uses this code path, constructing the function - // every time the animation advances seem... expensive. - PiecewiseLinearFunction::from_iter( - elements - .iter() - .map(ComputedLinearStop::to_piecewise_linear_build_parameters), - ) - .at(progress as f32) - .into() - }, - GenericTimingFunction::Keyword(keyword) => { - let bezier = match keyword { - TimingKeyword::Linear => return progress, - TimingKeyword::Ease => Bezier::new(0.25, 0.1, 0.25, 1.), - TimingKeyword::EaseIn => Bezier::new(0.42, 0., 1., 1.), - TimingKeyword::EaseOut => Bezier::new(0., 0., 0.58, 1.), - TimingKeyword::EaseInOut => Bezier::new(0.42, 0., 0.58, 1.), - }; - bezier.solve(progress, epsilon) - }, - } + self.timing_function.calculate_output(progress, epsilon) } /// Update the given animation at a given point of progress. diff --git a/servo/components/style/values/computed/easing.rs b/servo/components/style/values/computed/easing.rs index 3a1a77a35758..4750088467c8 100644 --- a/servo/components/style/values/computed/easing.rs +++ b/servo/components/style/values/computed/easing.rs @@ -4,9 +4,10 @@ //! Computed types for CSS Easing functions. -use crate::piecewise_linear::PiecewiseLinearFunctionBuildParameters; +use crate::bezier::Bezier; +use crate::piecewise_linear::{PiecewiseLinearFunctionBuildParameters, PiecewiseLinearFunction}; use crate::values::computed::{Integer, Number, Percentage}; -use crate::values::generics::easing; +use crate::values::generics::easing::{self, StepPosition, TimingKeyword}; /// A computed timing function. pub type ComputedTimingFunction = easing::TimingFunction; @@ -28,3 +29,74 @@ impl ComputedLinearStop { ) } } + +impl ComputedTimingFunction { + fn calculate_step_output(steps: i32, pos: StepPosition, progress: f64) -> f64 { + let mut current_step = (progress * (steps as f64)).floor() as i32; + + if pos == StepPosition::Start || + pos == StepPosition::JumpStart || + pos == StepPosition::JumpBoth + { + current_step = current_step + 1; + } + + // FIXME: We should update current_step according to the "before flag". + // In order to get the before flag, we have to know the current animation phase + // and whether the iteration is reversed. For now, we skip this calculation. + // (i.e. Treat before_flag is unset,) + // https://drafts.csswg.org/css-easing/#step-timing-function-algo + + if progress >= 0.0 && current_step < 0 { + current_step = 0; + } + + let jumps = match pos { + StepPosition::JumpBoth => steps + 1, + StepPosition::JumpNone => steps - 1, + StepPosition::JumpStart | + StepPosition::JumpEnd | + StepPosition::Start | + StepPosition::End => steps, + }; + + if progress <= 1.0 && current_step > jumps { + current_step = jumps; + } + + (current_step as f64) / (jumps as f64) + } + + /// The output of the timing function given the progress ratio of this animation. + pub fn calculate_output(&self, progress: f64, epsilon: f64) -> f64 { + match self { + TimingFunction::CubicBezier { x1, y1, x2, y2 } => { + Bezier::new(*x1, *y1, *x2, *y2).solve(progress, epsilon) + }, + TimingFunction::Steps(steps, pos) => { + Self::calculate_step_output(*steps, *pos, progress) + }, + TimingFunction::LinearFunction(elements) => { + // TODO(dshin): For servo, which uses this code path, constructing the function + // every time the animation advances seem... expensive. + PiecewiseLinearFunction::from_iter( + elements + .iter() + .map(ComputedLinearStop::to_piecewise_linear_build_parameters), + ) + .at(progress as f32) + .into() + }, + TimingFunction::Keyword(keyword) => { + let bezier = match keyword { + TimingKeyword::Linear => return progress, + TimingKeyword::Ease => Bezier::new(0.25, 0.1, 0.25, 1.), + TimingKeyword::EaseIn => Bezier::new(0.42, 0., 1., 1.), + TimingKeyword::EaseOut => Bezier::new(0., 0., 0.58, 1.), + TimingKeyword::EaseInOut => Bezier::new(0.42, 0., 0.58, 1.), + }; + bezier.solve(progress, epsilon) + }, + } + } +}