--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -41,17 +41,20 @@ use values::computed::{ClipRect, Context
use values::computed::{Length, LengthOrPercentage, LengthOrPercentageOrAuto};
use values::computed::{LengthOrPercentageOrNone, MaxLength};
use values::computed::{NonNegativeNumber, Number, NumberOrPercentage, Percentage};
use values::computed::length::NonNegativeLengthOrPercentage;
use values::computed::ToComputedValue;
use values::computed::transform::{DirectionVector, Matrix, Matrix3D};
use values::computed::transform::TransformOperation as ComputedTransformOperation;
use values::computed::transform::Transform as ComputedTransform;
-use values::generics::transform::{self, Transform, TransformOperation};
+use values::computed::transform::Rotate as ComputedRotate;
+use values::computed::transform::Translate as ComputedTranslate;
+use values::computed::transform::Scale as ComputedScale;
+use values::generics::transform::{self, Rotate, Translate, Scale, Transform, TransformOperation};
use values::distance::{ComputeSquaredDistance, SquaredDistance};
#[cfg(feature = "gecko")] use values::generics::FontSettings as GenericFontSettings;
#[cfg(feature = "gecko")] use values::generics::FontSettingTag as GenericFontSettingTag;
#[cfg(feature = "gecko")] use values::generics::FontSettingTagFloat;
use values::generics::NonNegative;
use values::generics::effects::Filter;
use values::generics::position as generic_position;
use values::generics::svg::{SVGLength, SvgLengthOrPercentageOrNumber, SVGPaint};
@@ -2280,16 +2283,140 @@ impl Matrix3D {
self.m12*self.m21*self.m33 + self.m11*self.m22*self.m33),
};
Some(x)
}
}
/// <https://drafts.csswg.org/css-transforms/#interpolation-of-transforms>
+impl ComputedRotate {
+ fn fill_unspecified(rotate: &ComputedRotate)
+ -> Result<(Number, Number, Number, Angle), ()>{
+ // According to the spec:
+ // https://drafts.csswg.org/css-transforms-2/#individual-transforms
+ //
+ // If the axis is unspecified, it defaults to "0 0 1"
+ match rotate {
+ &Rotate::None =>
+ Ok((0., 0., 1., Angle::zero())),
+ &Rotate::Rotate3D(rx, ry, rz, angle) => Ok((rx, ry, rz, angle)),
+ &Rotate::Rotate(angle) => Ok((0., 0., 1., angle)),
+ }
+ }
+}
+
+impl Animate for ComputedRotate {
+ #[inline]
+ fn animate(
+ &self,
+ other: &Self,
+ procedure: Procedure,
+ ) -> Result<Self, ()> {
+ let from = ComputedRotate::fill_unspecified(self)?;
+ let to = ComputedRotate::fill_unspecified(other)?;
+
+ let (fx, fy, fz, fa) = transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
+ let (tx, ty, tz, ta) = transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
+ if (fx, fy, fz) == (tx, ty, tz) {
+ return Ok(Rotate::Rotate3D(fx, fy, fz, fa.animate(&ta, procedure)?));
+ }
+
+ let fv = DirectionVector::new(fx, fy, fz);
+ let tv = DirectionVector::new(tx, ty, tz);
+ let fq = Quaternion::from_direction_and_angle(&fv, fa.radians64());
+ let tq = Quaternion::from_direction_and_angle(&tv, ta.radians64());
+
+ let rq = MatrixDecomposed3D::slerp(&fq, &tq, procedure)?;
+ let (x, y, z, angle) =
+ transform::get_normalized_vector_and_angle(rq.0 as f32,
+ rq.1 as f32,
+ rq.2 as f32,
+ rq.3.acos() as f32 *2.0);
+
+ Ok(Rotate::Rotate3D(x, y, z, Angle::from_radians(angle)))
+ }
+}
+
+/// <https://drafts.csswg.org/css-transforms/#interpolation-of-transforms>
+impl ComputedTranslate {
+ fn fill_unspecified(translate: &ComputedTranslate)
+ -> Result<(LengthOrPercentage, LengthOrPercentage, Length), ()>{
+ // According to the spec:
+ // https://drafts.csswg.org/css-transforms-2/#individual-transforms
+ //
+ // Unspecified translations default to 0px
+ match translate {
+ &Translate::None => {
+ Ok((LengthOrPercentage::Length(Length::zero()),
+ LengthOrPercentage::Length(Length::zero()),
+ Length::zero()))
+ },
+ &Translate::Translate3D(tx, ty, tz) => Ok((tx, ty, tz)),
+ &Translate::Translate(tx, ty) => Ok((tx, ty, Length::zero())),
+ &Translate::TranslateX(tx) => Ok((tx, LengthOrPercentage::Length(Length::zero()), Length::zero())),
+ }
+ }
+}
+
+impl Animate for ComputedTranslate {
+ #[inline]
+ fn animate(
+ &self,
+ other: &Self,
+ procedure: Procedure,
+ ) -> Result<Self, ()> {
+ let from = ComputedTranslate::fill_unspecified(self)?;
+ let to = ComputedTranslate::fill_unspecified(other)?;
+
+ Ok(Translate::Translate3D(from.0.animate(&to.0, procedure)?,
+ from.1.animate(&to.1, procedure)?,
+ from.2.animate(&to.2, procedure)?))
+ }
+}
+
+/// <https://drafts.csswg.org/css-transforms/#interpolation-of-transforms>
+impl ComputedScale {
+ fn fill_unspecified(scale: &ComputedScale)
+ -> Result<(Number, Number, Number), ()>{
+ // According to the spec:
+ // https://drafts.csswg.org/css-transforms-2/#individual-transforms
+ //
+ // Unspecified scales default to 1
+ match scale {
+ &Scale::None => Ok((1.0, 1.0, 1.0)),
+ &Scale::Scale3D(sx, sy, sz) => Ok((sx, sy, sz)),
+ &Scale::Scale(sx, sy) => Ok((sx, sy, 1.)),
+ &Scale::ScaleX(sx) => Ok((sx, 1., 1.)),
+ }
+ }
+}
+
+impl Animate for ComputedScale {
+ #[inline]
+ fn animate(
+ &self,
+ other: &Self,
+ procedure: Procedure,
+ ) -> Result<Self, ()> {
+ let from = ComputedScale::fill_unspecified(self)?;
+ let to = ComputedScale::fill_unspecified(other)?;
+
+ if procedure == Procedure::Add {
+ // scale(x1,y1,z1)*scale(x2,y2,z2) = scale(x1*x2, y1*y2, z1*z2)
+ return Ok(Scale::Scale3D(from.0 * to.0, from.1 * to.1, from.2 * to.2));
+ }
+
+ Ok(Scale::Scale3D(animate_multiplicative_factor(from.0, to.0, procedure)?,
+ animate_multiplicative_factor(from.1, to.1, procedure)?,
+ animate_multiplicative_factor(from.2, to.2, procedure)?))
+ }
+}
+
+/// <https://drafts.csswg.org/css-transforms/#interpolation-of-transforms>
impl Animate for ComputedTransform {
#[inline]
fn animate(
&self,
other_: &Self,
procedure: Procedure,
) -> Result<Self, ()> {
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -751,47 +751,47 @@ pub fn get_normalized_vector_and_angle<T
// rotation to not be applied, so we use identity matrix (i.e. rotate3d(0, 0, 1, 0)).
(0., 0., 1., T::zero())
} else {
let vector = vector.normalize();
(vector.x, vector.y, vector.z, angle)
}
}
-#[derive(ToComputedValue, Animate, ComputeSquaredDistance, ToAnimatedZero)]
+#[derive(ToComputedValue, ComputeSquaredDistance, ToAnimatedZero)]
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
/// A value of the `Rotate` property
///
/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
pub enum Rotate<Number, Angle> {
/// 'none'
None,
/// '<angle>'
Rotate(Angle),
/// '<number>{3} <angle>'
Rotate3D(Number, Number, Number, Angle),
}
-#[derive(ToComputedValue, Animate, ComputeSquaredDistance, ToAnimatedZero)]
+#[derive(ToComputedValue, ComputeSquaredDistance, ToAnimatedZero)]
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
/// A value of the `Translate` property
///
/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
pub enum Translate<LengthOrPercentage, Length> {
/// 'none'
None,
/// '<length-percentage>'
TranslateX(LengthOrPercentage),
/// '<length-percentage> <length-percentage>'
Translate(LengthOrPercentage, LengthOrPercentage),
/// '<length-percentage> <length-percentage> <length>'
Translate3D(LengthOrPercentage, LengthOrPercentage, Length),
}
-#[derive(ToComputedValue, Animate, ComputeSquaredDistance, ToAnimatedZero)]
+#[derive(ToComputedValue, ComputeSquaredDistance, ToAnimatedZero)]
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
/// A value of the `Scale` property
///
/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
pub enum Scale<Number> {
/// 'none'
None,
/// '<number>'