diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 5c1b86f855f..c23aeec2a7c 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -641,6 +641,24 @@ impl Animatable for AnimationValue { } } + fn get_zero_value(&self) -> Option { + match self { + % for prop in data.longhands: + % if prop.animatable: + % if prop.animation_value_type == "discrete": + &AnimationValue::${prop.camel_case}(_) => { + None + } + % else: + &AnimationValue::${prop.camel_case}(ref base) => { + base.get_zero_value().map(AnimationValue::${prop.camel_case}) + } + % endif + % endif + % endfor + } + } + fn compute_distance(&self, other: &Self) -> Result { match (self, other) { % for prop in data.longhands: @@ -697,6 +715,17 @@ pub trait Animatable: Sized { self.add_weighted(other, count as f64, 1.0) } + /// 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 get_zero_value(&self) -> Option { + None + } + /// Compute distance between a value and another for a given property. fn compute_distance(&self, _other: &Self) -> Result { Err(()) } @@ -746,6 +775,9 @@ impl Animatable for Au { Ok(Au((self.0 as f64 * self_portion + other.0 as f64 * other_portion).round() as i32)) } + #[inline] + fn get_zero_value(&self) -> Option { Some(Au(0)) } + #[inline] fn compute_distance(&self, other: &Self) -> Result { self.0.compute_distance(&other.0) @@ -795,6 +827,9 @@ impl Animatable for f32 { Ok((*self as f64 * self_portion + *other as f64 * other_portion) as f32) } + #[inline] + fn get_zero_value(&self) -> Option { Some(0.) } + #[inline] fn compute_distance(&self, other: &Self) -> Result { Ok((*self - *other).abs() as f64) @@ -808,6 +843,9 @@ impl Animatable for f64 { Ok(*self * self_portion + *other * other_portion) } + #[inline] + fn get_zero_value(&self) -> Option { Some(0.) } + #[inline] fn compute_distance(&self, other: &Self) -> Result { Ok((*self - *other).abs()) @@ -821,6 +859,9 @@ impl Animatable for i32 { Ok((*self as f64 * self_portion + *other as f64 * other_portion).round() as i32) } + #[inline] + fn get_zero_value(&self) -> Option { Some(0) } + #[inline] fn compute_distance(&self, other: &Self) -> Result { Ok((*self - *other).abs() as f64) @@ -1007,6 +1048,9 @@ impl Animatable for LengthOrPercentage { } } + #[inline] + fn get_zero_value(&self) -> Option { Some(LengthOrPercentage::zero()) } + #[inline] fn compute_distance(&self, other: &Self) -> Result { match (*self, *other) { @@ -1079,6 +1123,18 @@ impl Animatable for LengthOrPercentageOrAuto { } } + #[inline] + fn get_zero_value(&self) -> Option { + match *self { + LengthOrPercentageOrAuto::Length(_) | + LengthOrPercentageOrAuto::Percentage(_) | + LengthOrPercentageOrAuto::Calc(_) => { + Some(LengthOrPercentageOrAuto::Length(Au(0))) + }, + LengthOrPercentageOrAuto::Auto => { None }, + } + } + #[inline] fn compute_distance(&self, other: &Self) -> Result { match (*self, *other) { @@ -1149,6 +1205,18 @@ impl Animatable for LengthOrPercentageOrNone { } } + #[inline] + fn get_zero_value(&self) -> Option { + match *self { + LengthOrPercentageOrNone::Length(_) | + LengthOrPercentageOrNone::Percentage(_) | + LengthOrPercentageOrNone::Calc(_) => { + Some(LengthOrPercentageOrNone::Length(Au(0))) + }, + LengthOrPercentageOrNone::None => { None }, + } + } + #[inline] fn compute_distance(&self, other: &Self) -> Result { match (*self, *other) { @@ -1223,7 +1291,8 @@ impl Animatable for FontWeight { fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { let a = (*self as u32) as f64; let b = (*other as u32) as f64; - let weight = a * self_portion + b * other_portion; + const NORMAL: f64 = 400.; + let weight = (a - NORMAL) * self_portion + (b - NORMAL) * other_portion + NORMAL; Ok(if weight < 150. { FontWeight::Weight100 } else if weight < 250. { @@ -1245,6 +1314,9 @@ impl Animatable for FontWeight { }) } + #[inline] + fn get_zero_value(&self) -> Option { Some(FontWeight::Weight400) } + #[inline] fn compute_distance(&self, other: &Self) -> Result { let a = (*self as u32) as f64; @@ -1306,6 +1378,11 @@ impl Into for f64 { } } +// Like std::macros::try!, but for Option<>. +macro_rules! option_try { + ($e:expr) => (match $e { Some(e) => e, None => return None }) +} + /// https://drafts.csswg.org/css-transitions/#animtype-simple-list impl Animatable for generic_position::Position { #[inline] @@ -1318,6 +1395,14 @@ impl Animatable for generic_position::Position Option { + Some(generic_position::Position { + horizontal: option_try!(self.horizontal.get_zero_value()), + vertical: option_try!(self.vertical.get_zero_value()), + }) + } + #[inline] fn compute_distance(&self, other: &Self) -> Result { self.compute_squared_distance(other).map(|sd| sd.sqrt()) @@ -2493,6 +2578,9 @@ impl Animatable for TransformList { } } } + + #[inline] + fn get_zero_value(&self) -> Option { Some(TransformList(None)) } } impl Animatable for Either @@ -2514,6 +2602,14 @@ impl Animatable for Either } } + #[inline] + fn get_zero_value(&self) -> Option { + match *self { + Either::First(ref this) => { this.get_zero_value().map(Either::First) }, + Either::Second(ref this) => { this.get_zero_value().map(Either::Second) }, + } + } + #[inline] fn compute_distance(&self, other: &Self) -> Result { match (self, other) { @@ -2616,6 +2712,11 @@ impl Animatable for IntermediateRGBA { } } + #[inline] + fn get_zero_value(&self) -> Option { + Some(IntermediateRGBA::new(0., 0., 0., 1.)) + } + #[inline] fn compute_distance(&self, other: &Self) -> Result { self.compute_squared_distance(other).map(|sq| sq.sqrt())