summaryrefslogtreecommitdiffstats
path: root/servo/components/style/values/computed/easing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/values/computed/easing.rs')
-rw-r--r--servo/components/style/values/computed/easing.rs109
1 files changed, 109 insertions, 0 deletions
diff --git a/servo/components/style/values/computed/easing.rs b/servo/components/style/values/computed/easing.rs
new file mode 100644
index 0000000000..d351b3c71d
--- /dev/null
+++ b/servo/components/style/values/computed/easing.rs
@@ -0,0 +1,109 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for CSS Easing functions.
+
+use euclid::approxeq::ApproxEq;
+
+use crate::bezier::Bezier;
+use crate::piecewise_linear::PiecewiseLinearFunction;
+use crate::values::computed::{Integer, Number};
+use crate::values::generics::easing::{self, BeforeFlag, StepPosition, TimingKeyword};
+
+/// A computed timing function.
+pub type ComputedTimingFunction = easing::TimingFunction<Integer, Number, PiecewiseLinearFunction>;
+
+/// An alias of the computed timing function.
+pub type TimingFunction = ComputedTimingFunction;
+
+impl ComputedTimingFunction {
+ fn calculate_step_output(
+ steps: i32,
+ pos: StepPosition,
+ progress: f64,
+ before_flag: BeforeFlag,
+ ) -> f64 {
+ // User specified values can cause overflow (bug 1706157). Increments/decrements
+ // should be gravefully handled.
+ let mut current_step = (progress * (steps as f64)).floor() as i32;
+
+ // Increment current step if it is jump-start or start.
+ if pos == StepPosition::Start ||
+ pos == StepPosition::JumpStart ||
+ pos == StepPosition::JumpBoth
+ {
+ current_step = current_step.checked_add(1).unwrap_or(current_step);
+ }
+
+ // If the "before flag" is set and we are at a transition point,
+ // drop back a step
+ if before_flag == BeforeFlag::Set &&
+ (progress * steps as f64).rem_euclid(1.0).approx_eq(&0.0)
+ {
+ current_step = current_step.checked_sub(1).unwrap_or(current_step);
+ }
+
+ // We should not produce a result outside [0, 1] unless we have an
+ // input outside that range. This takes care of steps that would otherwise
+ // occur at boundaries.
+ if progress >= 0.0 && current_step < 0 {
+ current_step = 0;
+ }
+
+ // |jumps| should always be in [1, i32::MAX].
+ let jumps = if pos == StepPosition::JumpBoth {
+ steps.checked_add(1).unwrap_or(steps)
+ } else if pos == StepPosition::JumpNone {
+ steps.checked_sub(1).unwrap_or(steps)
+ } else {
+ 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, before_flag: BeforeFlag, epsilon: f64) -> f64 {
+ let progress = match self {
+ TimingFunction::CubicBezier { x1, y1, x2, y2 } => {
+ Bezier::calculate_bezier_output(progress, epsilon, *x1, *y1, *x2, *y2)
+ },
+ TimingFunction::Steps(steps, pos) => {
+ Self::calculate_step_output(*steps, *pos, progress, before_flag)
+ },
+ TimingFunction::LinearFunction(function) => function.at(progress as f32).into(),
+ TimingFunction::Keyword(keyword) => match keyword {
+ TimingKeyword::Linear => progress,
+ TimingKeyword::Ease => {
+ Bezier::calculate_bezier_output(progress, epsilon, 0.25, 0.1, 0.25, 1.)
+ },
+ TimingKeyword::EaseIn => {
+ Bezier::calculate_bezier_output(progress, epsilon, 0.42, 0., 1., 1.)
+ },
+ TimingKeyword::EaseOut => {
+ Bezier::calculate_bezier_output(progress, epsilon, 0., 0., 0.58, 1.)
+ },
+ TimingKeyword::EaseInOut => {
+ Bezier::calculate_bezier_output(progress, epsilon, 0.42, 0., 0.58, 1.)
+ },
+ },
+ };
+
+ // The output progress value of an easing function is a real number in the range:
+ // [-inf, inf].
+ // https://drafts.csswg.org/css-easing-1/#output-progress-value
+ //
+ // However, we expect to use the finite progress for interpolation and web-animations
+ // https://drafts.csswg.org/css-values-4/#interpolation
+ // https://drafts.csswg.org/web-animations-1/#dom-computedeffecttiming-progress
+ //
+ // So we clamp the infinite progress, per the spec issue:
+ // https://github.com/w3c/csswg-drafts/issues/8344
+ progress.min(f64::MAX).max(f64::MIN)
+ }
+}