mirror of
https://github.com/servo/servo.git
synced 2025-08-13 01:15:34 +01:00
Introduce values::animated::Animate
This replaces the Animatable trait and merges its three former methods into a single one.
This commit is contained in:
parent
0cceeb9d5c
commit
aea0cd7ec7
23 changed files with 876 additions and 937 deletions
|
@ -4,8 +4,7 @@
|
|||
|
||||
//! Animated types for CSS colors.
|
||||
|
||||
use properties::animated_properties::Animatable;
|
||||
use values::animated::ToAnimatedZero;
|
||||
use values::animated::{Animate, Procedure, ToAnimatedZero};
|
||||
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||
|
||||
/// An animated RGBA color.
|
||||
|
@ -39,13 +38,13 @@ impl RGBA {
|
|||
}
|
||||
}
|
||||
|
||||
/// Unlike Animatable for computed colors, we don't clamp any component values.
|
||||
/// Unlike Animate for computed colors, we don't clamp any component values.
|
||||
///
|
||||
/// FIXME(nox): Why do computed colors even implement Animatable?
|
||||
impl Animatable for RGBA {
|
||||
/// FIXME(nox): Why do computed colors even implement Animate?
|
||||
impl Animate for RGBA {
|
||||
#[inline]
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
let mut alpha = self.alpha.add_weighted(&other.alpha, self_portion, other_portion)?;
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
let mut alpha = self.alpha.animate(&other.alpha, procedure)?;
|
||||
if alpha <= 0. {
|
||||
// Ideally we should return color value that only alpha component is
|
||||
// 0, but this is what current gecko does.
|
||||
|
@ -53,15 +52,9 @@ impl Animatable for RGBA {
|
|||
}
|
||||
|
||||
alpha = alpha.min(1.);
|
||||
let red = (self.red * self.alpha).add_weighted(
|
||||
&(other.red * other.alpha), self_portion, other_portion
|
||||
)? * 1. / alpha;
|
||||
let green = (self.green * self.alpha).add_weighted(
|
||||
&(other.green * other.alpha), self_portion, other_portion
|
||||
)? * 1. / alpha;
|
||||
let blue = (self.blue * self.alpha).add_weighted(
|
||||
&(other.blue * other.alpha), self_portion, other_portion
|
||||
)? * 1. / alpha;
|
||||
let red = (self.red * self.alpha).animate(&(other.red * other.alpha), procedure)? * 1. / alpha;
|
||||
let green = (self.green * self.alpha).animate(&(other.green * other.alpha), procedure)? * 1. / alpha;
|
||||
let blue = (self.blue * self.alpha).animate(&(other.blue * other.alpha), procedure)? * 1. / alpha;
|
||||
|
||||
Ok(RGBA::new(red, green, blue, alpha))
|
||||
}
|
||||
|
@ -123,9 +116,9 @@ impl Color {
|
|||
}
|
||||
}
|
||||
|
||||
impl Animatable for Color {
|
||||
impl Animate for Color {
|
||||
#[inline]
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
// Common cases are interpolating between two numeric colors,
|
||||
// two currentcolors, and a numeric color and a currentcolor.
|
||||
//
|
||||
|
@ -133,35 +126,35 @@ impl Animatable for Color {
|
|||
// equals to one, so it may be broken for additive operation.
|
||||
// To properly support additive color interpolation, we would
|
||||
// need two ratio fields in computed color types.
|
||||
let (this_weight, other_weight) = procedure.weights();
|
||||
if self.foreground_ratio == other.foreground_ratio {
|
||||
if self.is_currentcolor() {
|
||||
Ok(Color::currentcolor())
|
||||
} else {
|
||||
Ok(Color {
|
||||
color: self.color.add_weighted(&other.color, self_portion, other_portion)?,
|
||||
color: self.color.animate(&other.color, procedure)?,
|
||||
foreground_ratio: self.foreground_ratio,
|
||||
})
|
||||
}
|
||||
} else if self.is_currentcolor() && other.is_numeric() {
|
||||
Ok(Color {
|
||||
color: other.color,
|
||||
foreground_ratio: self_portion as f32,
|
||||
foreground_ratio: this_weight as f32,
|
||||
})
|
||||
} else if self.is_numeric() && other.is_currentcolor() {
|
||||
Ok(Color {
|
||||
color: self.color,
|
||||
foreground_ratio: other_portion as f32,
|
||||
foreground_ratio: other_weight as f32,
|
||||
})
|
||||
} else {
|
||||
// For interpolating between two complex colors, we need to
|
||||
// generate colors with effective alpha value.
|
||||
let self_color = self.effective_intermediate_rgba();
|
||||
let other_color = other.effective_intermediate_rgba();
|
||||
let color = self_color.add_weighted(&other_color, self_portion, other_portion)?;
|
||||
let color = self_color.animate(&other_color, procedure)?;
|
||||
// Then we compute the final foreground ratio, and derive
|
||||
// the final alpha value from the effective alpha value.
|
||||
let foreground_ratio = self.foreground_ratio
|
||||
.add_weighted(&other.foreground_ratio, self_portion, other_portion)?;
|
||||
let foreground_ratio = self.foreground_ratio.animate(&other.foreground_ratio, procedure)?;
|
||||
let alpha = color.alpha / (1. - foreground_ratio);
|
||||
Ok(Color {
|
||||
color: RGBA {
|
||||
|
@ -177,7 +170,7 @@ impl Animatable for Color {
|
|||
impl ComputeSquaredDistance for Color {
|
||||
#[inline]
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||
// All comments in add_weighted also applies here.
|
||||
// All comments from the Animate impl also applies here.
|
||||
if self.foreground_ratio == other.foreground_ratio {
|
||||
if self.is_currentcolor() {
|
||||
Ok(SquaredDistance::Value(0.))
|
||||
|
|
|
@ -4,14 +4,13 @@
|
|||
|
||||
//! Animated types for CSS values related to effects.
|
||||
|
||||
use properties::animated_properties::Animatable;
|
||||
use properties::longhands::box_shadow::computed_value::T as ComputedBoxShadowList;
|
||||
use properties::longhands::filter::computed_value::T as ComputedFilterList;
|
||||
use properties::longhands::text_shadow::computed_value::T as ComputedTextShadowList;
|
||||
use std::cmp;
|
||||
#[cfg(not(feature = "gecko"))]
|
||||
use values::Impossible;
|
||||
use values::animated::{ToAnimatedValue, ToAnimatedZero};
|
||||
use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
|
||||
use values::animated::color::RGBA;
|
||||
use values::computed::{Angle, NonNegativeNumber};
|
||||
use values::computed::length::{Length, NonNegativeLength};
|
||||
|
@ -66,42 +65,36 @@ impl ToAnimatedValue for ComputedBoxShadowList {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S> Animatable for ShadowList<S>
|
||||
impl<S> Animate for ShadowList<S>
|
||||
where
|
||||
S: Animatable + Clone + ToAnimatedZero,
|
||||
S: Animate + Clone + ToAnimatedZero,
|
||||
{
|
||||
#[inline]
|
||||
fn add_weighted(
|
||||
&self,
|
||||
other: &Self,
|
||||
self_portion: f64,
|
||||
other_portion: f64,
|
||||
) -> Result<Self, ()> {
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
if procedure == Procedure::Add {
|
||||
return Ok(ShadowList(
|
||||
self.0.iter().chain(&other.0).cloned().collect(),
|
||||
));
|
||||
}
|
||||
// FIXME(nox): Use itertools here, to avoid the need for `unreachable!`.
|
||||
let max_len = cmp::max(self.0.len(), other.0.len());
|
||||
let mut shadows = Vec::with_capacity(max_len);
|
||||
for i in 0..max_len {
|
||||
shadows.push(match (self.0.get(i), other.0.get(i)) {
|
||||
(Some(shadow), Some(other)) => {
|
||||
shadow.add_weighted(other, self_portion, other_portion)?
|
||||
shadow.animate(other, procedure)?
|
||||
},
|
||||
(Some(shadow), None) => {
|
||||
shadow.add_weighted(&shadow.to_animated_zero()?, self_portion, other_portion)?
|
||||
shadow.animate(&shadow.to_animated_zero()?, procedure)?
|
||||
},
|
||||
(None, Some(shadow)) => {
|
||||
shadow.to_animated_zero()?.add_weighted(&shadow, self_portion, other_portion)?
|
||||
shadow.to_animated_zero()?.animate(shadow, procedure)?
|
||||
},
|
||||
(None, None) => unreachable!(),
|
||||
});
|
||||
}
|
||||
Ok(ShadowList(shadows))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add(&self, other: &Self) -> Result<Self, ()> {
|
||||
Ok(ShadowList(
|
||||
self.0.iter().cloned().chain(other.0.iter().cloned()).collect(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> ComputeSquaredDistance for ShadowList<S>
|
||||
|
@ -146,20 +139,15 @@ impl ToAnimatedValue for ComputedTextShadowList {
|
|||
}
|
||||
}
|
||||
|
||||
impl Animatable for BoxShadow {
|
||||
impl Animate for BoxShadow {
|
||||
#[inline]
|
||||
fn add_weighted(
|
||||
&self,
|
||||
other: &Self,
|
||||
self_portion: f64,
|
||||
other_portion: f64,
|
||||
) -> Result<Self, ()> {
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
if self.inset != other.inset {
|
||||
return Err(());
|
||||
}
|
||||
Ok(BoxShadow {
|
||||
base: self.base.add_weighted(&other.base, self_portion, other_portion)?,
|
||||
spread: self.spread.add_weighted(&other.spread, self_portion, other_portion)?,
|
||||
base: self.base.animate(&other.base, procedure)?,
|
||||
spread: self.spread.animate(&other.spread, procedure)?,
|
||||
inset: self.inset,
|
||||
})
|
||||
}
|
||||
|
@ -224,19 +212,14 @@ impl ToAnimatedZero for FilterList {
|
|||
}
|
||||
}
|
||||
|
||||
impl Animatable for SimpleShadow {
|
||||
impl Animate for SimpleShadow {
|
||||
#[inline]
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
let color = self.color.add_weighted(&other.color, self_portion, other_portion)?;
|
||||
let horizontal = self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?;
|
||||
let vertical = self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?;
|
||||
let blur = self.blur.add_weighted(&other.blur, self_portion, other_portion)?;
|
||||
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
Ok(SimpleShadow {
|
||||
color: color,
|
||||
horizontal: horizontal,
|
||||
vertical: vertical,
|
||||
blur: blur,
|
||||
color: self.color.animate(&other.color, procedure)?,
|
||||
horizontal: self.horizontal.animate(&other.horizontal, procedure)?,
|
||||
vertical: self.vertical.animate(&other.vertical, procedure)?,
|
||||
blur: self.blur.animate(&other.blur, procedure)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -245,11 +228,7 @@ impl ToAnimatedZero for SimpleShadow {
|
|||
#[inline]
|
||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||
Ok(SimpleShadow {
|
||||
color: if let Some(color) = self.color.as_ref() {
|
||||
Some(color.to_animated_zero()?)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
color: self.color.to_animated_zero()?,
|
||||
horizontal: self.horizontal.to_animated_zero()?,
|
||||
vertical: self.vertical.to_animated_zero()?,
|
||||
blur: self.blur.to_animated_zero()?,
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
//! module's raison d'être is to ultimately contain all these types.
|
||||
|
||||
use app_units::Au;
|
||||
use euclid::{Point2D, Size2D};
|
||||
use smallvec::SmallVec;
|
||||
use std::cmp::max;
|
||||
use values::computed::Angle as ComputedAngle;
|
||||
|
@ -27,6 +28,26 @@ use values::specified::url::SpecifiedUrl;
|
|||
pub mod color;
|
||||
pub mod effects;
|
||||
|
||||
/// Animating from one value to another.
|
||||
pub trait Animate: Sized {
|
||||
/// Animate a value towards another one, given an animation procedure.
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>;
|
||||
}
|
||||
|
||||
/// An animation procedure.
|
||||
///
|
||||
/// https://w3c.github.io/web-animations/#procedures-for-animating-properties
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Procedure {
|
||||
/// https://w3c.github.io/web-animations/#animation-interpolation
|
||||
Interpolate { progress: f64 },
|
||||
/// https://w3c.github.io/web-animations/#animation-addition
|
||||
Add,
|
||||
/// https://w3c.github.io/web-animations/#animation-accumulation
|
||||
Accumulate { count: u64 },
|
||||
}
|
||||
|
||||
/// Conversion between computed values and intermediate values for animations.
|
||||
///
|
||||
/// Notably, colors are represented as four floats during animations.
|
||||
|
@ -41,6 +62,108 @@ pub trait ToAnimatedValue {
|
|||
fn from_animated_value(animated: Self::AnimatedValue) -> Self;
|
||||
}
|
||||
|
||||
/// Marker trait for computed values with the same representation during animations.
|
||||
pub trait AnimatedValueAsComputed {}
|
||||
|
||||
/// Returns a value similar to `self` that represents zero.
|
||||
pub trait ToAnimatedZero: Sized {
|
||||
/// Returns a value that, when added with an underlying value, will produce the underlying
|
||||
/// value. This is used for SMIL animation's "by-animation" where SMIL first interpolates from
|
||||
/// the zero value to the 'by' value, and then adds the result to the underlying value.
|
||||
///
|
||||
/// This is not the necessarily the same as the initial value of a property. For example, the
|
||||
/// initial value of 'stroke-width' is 1, but the zero value is 0, since adding 1 to the
|
||||
/// underlying value will not produce the underlying value.
|
||||
fn to_animated_zero(&self) -> Result<Self, ()>;
|
||||
}
|
||||
|
||||
impl Procedure {
|
||||
/// Returns this procedure as a pair of weights.
|
||||
///
|
||||
/// This is useful for animations that don't animate differently
|
||||
/// depending on the used procedure.
|
||||
#[inline]
|
||||
pub fn weights(self) -> (f64, f64) {
|
||||
match self {
|
||||
Procedure::Interpolate { progress } => (1. - progress, progress),
|
||||
Procedure::Add => (1., 1.),
|
||||
Procedure::Accumulate { count } => (count as f64, 1.),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-number
|
||||
impl Animate for i32 {
|
||||
#[inline]
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
Ok(((*self as f64).animate(&(*other as f64), procedure)? + 0.5).floor() as i32)
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-number
|
||||
impl Animate for f32 {
|
||||
#[inline]
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
Ok((*self as f64).animate(&(*other as f64), procedure)? as f32)
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-number
|
||||
impl Animate for f64 {
|
||||
#[inline]
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
let (self_weight, other_weight) = procedure.weights();
|
||||
Ok(*self * self_weight + *other * other_weight)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Animate for Option<T>
|
||||
where
|
||||
T: Animate,
|
||||
{
|
||||
#[inline]
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
match (self.as_ref(), other.as_ref()) {
|
||||
(Some(ref this), Some(ref other)) => Ok(Some(this.animate(other, procedure)?)),
|
||||
(None, None) => Ok(None),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Animate for Au {
|
||||
#[inline]
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
Ok(Au(self.0.animate(&other.0, procedure)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Animate for Size2D<T>
|
||||
where
|
||||
T: Animate + Copy,
|
||||
{
|
||||
#[inline]
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
Ok(Size2D::new(
|
||||
self.width.animate(&other.width, procedure)?,
|
||||
self.height.animate(&other.height, procedure)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Animate for Point2D<T>
|
||||
where
|
||||
T: Animate + Copy,
|
||||
{
|
||||
#[inline]
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
Ok(Point2D::new(
|
||||
self.x.animate(&other.x, procedure)?,
|
||||
self.y.animate(&other.y, procedure)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToAnimatedValue for Option<T>
|
||||
where
|
||||
T: ToAnimatedValue,
|
||||
|
@ -92,9 +215,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Marker trait for computed values with the same representation during animations.
|
||||
pub trait AnimatedValueAsComputed {}
|
||||
|
||||
impl AnimatedValueAsComputed for Au {}
|
||||
impl AnimatedValueAsComputed for ComputedAngle {}
|
||||
impl AnimatedValueAsComputed for SpecifiedUrl {}
|
||||
|
@ -263,18 +383,6 @@ impl ToAnimatedValue for ComputedMozLength {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns a value similar to `self` that represents zero.
|
||||
pub trait ToAnimatedZero: Sized {
|
||||
/// Returns a value that, when added with an underlying value, will produce the underlying
|
||||
/// value. This is used for SMIL animation's "by-animation" where SMIL first interpolates from
|
||||
/// the zero value to the 'by' value, and then adds the result to the underlying value.
|
||||
///
|
||||
/// This is not the necessarily the same as the initial value of a property. For example, the
|
||||
/// initial value of 'stroke-width' is 1, but the zero value is 0, since adding 1 to the
|
||||
/// underlying value will not produce the underlying value.
|
||||
fn to_animated_zero(&self) -> Result<Self, ()>;
|
||||
}
|
||||
|
||||
impl ToAnimatedZero for Au {
|
||||
#[inline]
|
||||
fn to_animated_zero(&self) -> Result<Self, ()> { Ok(Au(0)) }
|
||||
|
@ -294,3 +402,16 @@ impl ToAnimatedZero for i32 {
|
|||
#[inline]
|
||||
fn to_animated_zero(&self) -> Result<Self, ()> { Ok(0) }
|
||||
}
|
||||
|
||||
impl<T> ToAnimatedZero for Option<T>
|
||||
where
|
||||
T: ToAnimatedZero,
|
||||
{
|
||||
#[inline]
|
||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||
match *self {
|
||||
Some(ref value) => Ok(Some(value.to_animated_zero()?)),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue