mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
Auto merge of #16863 - birtles:additive-animation, r=hiro
Additive animation Pull request for [Mozilla bug 1329878](https://bugzilla.mozilla.org/show_bug.cgi?id=1329878). --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix [Mozilla bug 1329878](https://bugzilla.mozilla.org/show_bug.cgi?id=1329878). - [X] There are tests for these changes in web-platform-tests <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16863) <!-- Reviewable:end -->
This commit is contained in:
commit
94e977efeb
8 changed files with 359 additions and 216 deletions
|
@ -113,8 +113,13 @@
|
|||
% if delegate_animate:
|
||||
use properties::animated_properties::Animatable;
|
||||
impl Animatable for T {
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
self.0.interpolate(&other.0, progress).map(T)
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
||||
-> Result<Self, ()> {
|
||||
self.0.add_weighted(&other.0, self_portion, other_portion).map(T)
|
||||
}
|
||||
|
||||
fn add(&self, other: &Self) -> Result<Self, ()> {
|
||||
self.0.add(&other.0).map(T)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -976,16 +981,17 @@
|
|||
<%def name="impl_animatable_for_option_tuple(value_for_none)">
|
||||
impl Animatable for T {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
||||
-> Result<Self, ()> {
|
||||
match (self, other) {
|
||||
(&T(Some(ref this)), &T(Some(ref other))) => {
|
||||
Ok(T(this.interpolate(other, progress).ok()))
|
||||
Ok(T(this.add_weighted(other, self_portion, other_portion).ok()))
|
||||
},
|
||||
(&T(Some(ref this)), &T(None)) => {
|
||||
Ok(T(this.interpolate(&${value_for_none}, progress).ok()))
|
||||
Ok(T(this.add_weighted(&${value_for_none}, self_portion, other_portion).ok()))
|
||||
},
|
||||
(&T(None), &T(Some(ref other))) => {
|
||||
Ok(T(${value_for_none}.interpolate(other, progress).ok()))
|
||||
Ok(T(${value_for_none}.add_weighted(other, self_portion, other_portion).ok()))
|
||||
},
|
||||
(&T(None), &T(None)) => {
|
||||
Ok(T(None))
|
||||
|
|
|
@ -581,27 +581,52 @@ impl AnimationValue {
|
|||
}
|
||||
|
||||
impl Animatable for AnimationValue {
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
||||
-> Result<Self, ()> {
|
||||
match (self, other) {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
(&AnimationValue::${prop.camel_case}(ref from),
|
||||
&AnimationValue::${prop.camel_case}(ref to)) => {
|
||||
// https://w3c.github.io/web-animations/#discrete-animation-type
|
||||
% if prop.animation_value_type == "discrete":
|
||||
if progress < 0.5 {
|
||||
if self_portion > other_portion {
|
||||
Ok(AnimationValue::${prop.camel_case}(*from))
|
||||
} else {
|
||||
Ok(AnimationValue::${prop.camel_case}(*to))
|
||||
}
|
||||
% else:
|
||||
from.interpolate(to, progress).map(AnimationValue::${prop.camel_case})
|
||||
from.add_weighted(to, self_portion, other_portion)
|
||||
.map(AnimationValue::${prop.camel_case})
|
||||
% endif
|
||||
}
|
||||
% endif
|
||||
% endfor
|
||||
_ => {
|
||||
panic!("Expected interpolation of computed values of the same \
|
||||
panic!("Expected weighted addition of computed values of the same \
|
||||
property, got: {:?}, {:?}", self, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add(&self, other: &Self) -> Result<Self, ()> {
|
||||
match (self, other) {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
% if prop.animation_value_type == "discrete":
|
||||
(&AnimationValue::${prop.camel_case}(_),
|
||||
&AnimationValue::${prop.camel_case}(_)) => {
|
||||
Err(())
|
||||
}
|
||||
% else:
|
||||
(&AnimationValue::${prop.camel_case}(ref from),
|
||||
&AnimationValue::${prop.camel_case}(ref to)) => {
|
||||
from.add(to).map(AnimationValue::${prop.camel_case})
|
||||
}
|
||||
% endif
|
||||
% endif
|
||||
% endfor
|
||||
_ => {
|
||||
panic!("Expected weighted addition of computed values of the same \
|
||||
property, got: {:?}, {:?}", self, other);
|
||||
}
|
||||
}
|
||||
|
@ -635,10 +660,24 @@ impl Animatable for AnimationValue {
|
|||
|
||||
/// A trait used to implement various procedures used during animation.
|
||||
pub trait Animatable: Sized {
|
||||
/// Performs a weighted sum of this value and |other|. This is used for
|
||||
/// interpolation and addition of animation values.
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
||||
-> Result<Self, ()>;
|
||||
|
||||
/// [Interpolates][interpolation] a value with another for a given property.
|
||||
///
|
||||
/// [interpolation]: https://w3c.github.io/web-animations/#animation-interpolation
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()>;
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
self.add_weighted(other, 1.0 - progress, progress)
|
||||
}
|
||||
|
||||
/// Returns the [sum][animation-addition] of this value and |other|.
|
||||
///
|
||||
/// [animation-addition]: https://w3c.github.io/web-animations/#animation-addition
|
||||
fn add(&self, other: &Self) -> Result<Self, ()> {
|
||||
self.add_weighted(other, 1.0, 1.0)
|
||||
}
|
||||
|
||||
/// Compute distance between a value and another for a given property.
|
||||
fn compute_distance(&self, _other: &Self) -> Result<f64, ()> { Err(()) }
|
||||
|
@ -658,11 +697,12 @@ impl RepeatableListAnimatable for LengthOrPercentage {}
|
|||
impl RepeatableListAnimatable for Either<f32, LengthOrPercentage> {}
|
||||
|
||||
impl<T: RepeatableListAnimatable> Animatable for SmallVec<[T; 1]> {
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
||||
-> Result<Self, ()> {
|
||||
use num_integer::lcm;
|
||||
let len = lcm(self.len(), other.len());
|
||||
self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(me, you)| {
|
||||
me.interpolate(you, progress)
|
||||
me.add_weighted(you, self_portion, other_portion)
|
||||
}).collect()
|
||||
}
|
||||
|
||||
|
@ -684,8 +724,8 @@ impl<T: RepeatableListAnimatable> Animatable for SmallVec<[T; 1]> {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-number
|
||||
impl Animatable for Au {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
Ok(Au((self.0 as f64 + (other.0 as f64 - self.0 as f64) * progress).round() as i32))
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok(Au((self.0 as f64 * self_portion + other.0 as f64 * other_portion).round() as i32))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -698,10 +738,10 @@ impl <T> Animatable for Option<T>
|
|||
where T: Animatable,
|
||||
{
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Option<T>, progress: f64) -> Result<Option<T>, ()> {
|
||||
fn add_weighted(&self, other: &Option<T>, self_portion: f64, other_portion: f64) -> Result<Option<T>, ()> {
|
||||
match (self, other) {
|
||||
(&Some(ref this), &Some(ref other)) => {
|
||||
Ok(this.interpolate(other, progress).ok())
|
||||
Ok(this.add_weighted(other, self_portion, other_portion).ok())
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
|
@ -731,8 +771,8 @@ impl <T> Animatable for Option<T>
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-number
|
||||
impl Animatable for f32 {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &f32, progress: f64) -> Result<Self, ()> {
|
||||
Ok(((*self as f64) + ((*other as f64) - (*self as f64)) * progress) as f32)
|
||||
fn add_weighted(&self, other: &f32, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok((*self as f64 * self_portion + *other as f64 * other_portion) as f32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -744,8 +784,8 @@ impl Animatable for f32 {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-number
|
||||
impl Animatable for f64 {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &f64, progress: f64) -> Result<Self, ()> {
|
||||
Ok(*self + (*other - *self) * progress)
|
||||
fn add_weighted(&self, other: &f64, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok(*self * self_portion + *other * other_portion)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -757,10 +797,8 @@ impl Animatable for f64 {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-integer
|
||||
impl Animatable for i32 {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &i32, progress: f64) -> Result<Self, ()> {
|
||||
let a = *self as f64;
|
||||
let b = *other as f64;
|
||||
Ok((a + (b - a) * progress).round() as i32)
|
||||
fn add_weighted(&self, other: &i32, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok((*self as f64 * self_portion + *other as f64 * other_portion).round() as i32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -772,25 +810,24 @@ impl Animatable for i32 {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-number
|
||||
impl Animatable for Angle {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Angle, progress: f64) -> Result<Self, ()> {
|
||||
self.radians().interpolate(&other.radians(), progress).map(Angle::from_radians)
|
||||
fn add_weighted(&self, other: &Angle, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
self.radians()
|
||||
.add_weighted(&other.radians(), self_portion, other_portion)
|
||||
.map(Angle::from_radians)
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-visibility
|
||||
impl Animatable for Visibility {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(Visibility::visible, _) | (_, Visibility::visible) => {
|
||||
Ok(if progress >= 0.0 && progress <= 1.0 {
|
||||
Visibility::visible
|
||||
} else if progress < 0.0 {
|
||||
*self
|
||||
} else {
|
||||
*other
|
||||
})
|
||||
}
|
||||
(Visibility::visible, _) => {
|
||||
Ok(if self_portion > 0.0 { *self } else { *other })
|
||||
},
|
||||
(_, Visibility::visible) => {
|
||||
Ok(if other_portion > 0.0 { *other } else { *self })
|
||||
},
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
@ -807,9 +844,9 @@ impl Animatable for Visibility {
|
|||
|
||||
impl<T: Animatable + Copy> Animatable for Size2D<T> {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
let width = try!(self.width.interpolate(&other.width, progress));
|
||||
let height = try!(self.height.interpolate(&other.height, progress));
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
let width = try!(self.width.add_weighted(&other.width, self_portion, other_portion));
|
||||
let height = try!(self.height.add_weighted(&other.height, self_portion, other_portion));
|
||||
|
||||
Ok(Size2D::new(width, height))
|
||||
}
|
||||
|
@ -817,9 +854,9 @@ impl<T: Animatable + Copy> Animatable for Size2D<T> {
|
|||
|
||||
impl<T: Animatable + Copy> Animatable for Point2D<T> {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
let x = try!(self.x.interpolate(&other.x, progress));
|
||||
let y = try!(self.y.interpolate(&other.y, progress));
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
let x = try!(self.x.add_weighted(&other.x, self_portion, other_portion));
|
||||
let y = try!(self.y.add_weighted(&other.y, self_portion, other_portion));
|
||||
|
||||
Ok(Point2D::new(x, y))
|
||||
}
|
||||
|
@ -827,8 +864,8 @@ impl<T: Animatable + Copy> Animatable for Point2D<T> {
|
|||
|
||||
impl Animatable for BorderRadiusSize {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
self.0.interpolate(&other.0, progress).map(generics::BorderRadiusSize)
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
self.0.add_weighted(&other.0, self_portion, other_portion).map(generics::BorderRadiusSize)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -846,11 +883,11 @@ impl Animatable for BorderRadiusSize {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-length
|
||||
impl Animatable for VerticalAlign {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref this)),
|
||||
VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref other))) => {
|
||||
this.interpolate(other, progress).map(|value| {
|
||||
this.add_weighted(other, self_portion, other_portion).map(|value| {
|
||||
VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(value))
|
||||
})
|
||||
}
|
||||
|
@ -872,8 +909,8 @@ impl Animatable for VerticalAlign {
|
|||
|
||||
impl Animatable for BackgroundSizeList {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
self.0.interpolate(&other.0, progress).map(BackgroundSizeList)
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
self.0.add_weighted(&other.0, self_portion, other_portion).map(BackgroundSizeList)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -890,24 +927,28 @@ impl Animatable for BackgroundSizeList {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-color
|
||||
impl Animatable for RGBA {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &RGBA, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &RGBA, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
fn clamp(val: f32) -> f32 {
|
||||
val.max(0.).min(1.)
|
||||
}
|
||||
|
||||
let alpha = clamp(try!(self.alpha_f32().interpolate(&other.alpha_f32(), progress)));
|
||||
let alpha = clamp(try!(self.alpha_f32().add_weighted(&other.alpha_f32(),
|
||||
self_portion, other_portion)));
|
||||
if alpha == 0. {
|
||||
Ok(RGBA::transparent())
|
||||
} else {
|
||||
// NB: We rely on RGBA::from_floats clamping already.
|
||||
let red = try!((self.red_f32() * self.alpha_f32())
|
||||
.interpolate(&(other.red_f32() * other.alpha_f32()), progress))
|
||||
.add_weighted(&(other.red_f32() * other.alpha_f32()),
|
||||
self_portion, other_portion))
|
||||
* 1. / alpha;
|
||||
let green = try!((self.green_f32() * self.alpha_f32())
|
||||
.interpolate(&(other.green_f32() * other.alpha_f32()), progress))
|
||||
.add_weighted(&(other.green_f32() * other.alpha_f32()),
|
||||
self_portion, other_portion))
|
||||
* 1. / alpha;
|
||||
let blue = try!((self.blue_f32() * self.alpha_f32())
|
||||
.interpolate(&(other.blue_f32() * other.alpha_f32()), progress))
|
||||
.add_weighted(&(other.blue_f32() * other.alpha_f32()),
|
||||
self_portion, other_portion))
|
||||
* 1. / alpha;
|
||||
Ok(RGBA::from_floats(red, green, blue, alpha))
|
||||
}
|
||||
|
@ -948,10 +989,10 @@ impl Animatable for RGBA {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-color
|
||||
impl Animatable for CSSParserColor {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(CSSParserColor::RGBA(ref this), CSSParserColor::RGBA(ref other)) => {
|
||||
this.interpolate(other, progress).map(CSSParserColor::RGBA)
|
||||
this.add_weighted(other, self_portion, other_portion).map(CSSParserColor::RGBA)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
|
@ -976,10 +1017,11 @@ impl Animatable for CSSParserColor {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
||||
impl Animatable for CalcLengthOrPercentage {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn interpolate_half<T>(this: Option<T>,
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
fn add_weighted_half<T>(this: Option<T>,
|
||||
other: Option<T>,
|
||||
progress: f64)
|
||||
self_portion: f64,
|
||||
other_portion: f64)
|
||||
-> Result<Option<T>, ()>
|
||||
where T: Default + Animatable,
|
||||
{
|
||||
|
@ -988,14 +1030,15 @@ impl Animatable for CalcLengthOrPercentage {
|
|||
(this, other) => {
|
||||
let this = this.unwrap_or(T::default());
|
||||
let other = other.unwrap_or(T::default());
|
||||
this.interpolate(&other, progress).map(Some)
|
||||
this.add_weighted(&other, self_portion, other_portion).map(Some)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CalcLengthOrPercentage {
|
||||
length: try!(self.length.interpolate(&other.length, progress)),
|
||||
percentage: try!(interpolate_half(self.percentage, other.percentage, progress)),
|
||||
length: try!(self.length.add_weighted(&other.length, self_portion, other_portion)),
|
||||
percentage: try!(add_weighted_half(self.percentage, other.percentage,
|
||||
self_portion, other_portion)),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1015,20 +1058,22 @@ impl Animatable for CalcLengthOrPercentage {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
||||
impl Animatable for LengthOrPercentage {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(LengthOrPercentage::Length(ref this),
|
||||
LengthOrPercentage::Length(ref other)) => {
|
||||
this.interpolate(other, progress).map(LengthOrPercentage::Length)
|
||||
this.add_weighted(other, self_portion, other_portion)
|
||||
.map(LengthOrPercentage::Length)
|
||||
}
|
||||
(LengthOrPercentage::Percentage(ref this),
|
||||
LengthOrPercentage::Percentage(ref other)) => {
|
||||
this.interpolate(other, progress).map(LengthOrPercentage::Percentage)
|
||||
this.add_weighted(other, self_portion, other_portion)
|
||||
.map(LengthOrPercentage::Percentage)
|
||||
}
|
||||
(this, other) => {
|
||||
let this: CalcLengthOrPercentage = From::from(this);
|
||||
let other: CalcLengthOrPercentage = From::from(other);
|
||||
this.interpolate(&other, progress)
|
||||
this.add_weighted(&other, self_portion, other_portion)
|
||||
.map(LengthOrPercentage::Calc)
|
||||
}
|
||||
}
|
||||
|
@ -1080,15 +1125,17 @@ impl Animatable for LengthOrPercentage {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
||||
impl Animatable for LengthOrPercentageOrAuto {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(LengthOrPercentageOrAuto::Length(ref this),
|
||||
LengthOrPercentageOrAuto::Length(ref other)) => {
|
||||
this.interpolate(other, progress).map(LengthOrPercentageOrAuto::Length)
|
||||
this.add_weighted(other, self_portion, other_portion)
|
||||
.map(LengthOrPercentageOrAuto::Length)
|
||||
}
|
||||
(LengthOrPercentageOrAuto::Percentage(ref this),
|
||||
LengthOrPercentageOrAuto::Percentage(ref other)) => {
|
||||
this.interpolate(other, progress).map(LengthOrPercentageOrAuto::Percentage)
|
||||
this.add_weighted(other, self_portion, other_portion)
|
||||
.map(LengthOrPercentageOrAuto::Percentage)
|
||||
}
|
||||
(LengthOrPercentageOrAuto::Auto, LengthOrPercentageOrAuto::Auto) => {
|
||||
Ok(LengthOrPercentageOrAuto::Auto)
|
||||
|
@ -1096,7 +1143,7 @@ impl Animatable for LengthOrPercentageOrAuto {
|
|||
(this, other) => {
|
||||
let this: Option<CalcLengthOrPercentage> = From::from(this);
|
||||
let other: Option<CalcLengthOrPercentage> = From::from(other);
|
||||
match this.interpolate(&other, progress) {
|
||||
match this.add_weighted(&other, self_portion, other_portion) {
|
||||
Ok(Some(result)) => Ok(LengthOrPercentageOrAuto::Calc(result)),
|
||||
_ => Err(()),
|
||||
}
|
||||
|
@ -1155,15 +1202,17 @@ impl Animatable for LengthOrPercentageOrAuto {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
||||
impl Animatable for LengthOrPercentageOrNone {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(LengthOrPercentageOrNone::Length(ref this),
|
||||
LengthOrPercentageOrNone::Length(ref other)) => {
|
||||
this.interpolate(other, progress).map(LengthOrPercentageOrNone::Length)
|
||||
this.add_weighted(other, self_portion, other_portion)
|
||||
.map(LengthOrPercentageOrNone::Length)
|
||||
}
|
||||
(LengthOrPercentageOrNone::Percentage(ref this),
|
||||
LengthOrPercentageOrNone::Percentage(ref other)) => {
|
||||
this.interpolate(other, progress).map(LengthOrPercentageOrNone::Percentage)
|
||||
this.add_weighted(other, self_portion, other_portion)
|
||||
.map(LengthOrPercentageOrNone::Percentage)
|
||||
}
|
||||
(LengthOrPercentageOrNone::None, LengthOrPercentageOrNone::None) => {
|
||||
Ok(LengthOrPercentageOrNone::None)
|
||||
|
@ -1191,11 +1240,12 @@ impl Animatable for LengthOrPercentageOrNone {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
||||
impl Animatable for MinLength {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(MinLength::LengthOrPercentage(ref this),
|
||||
MinLength::LengthOrPercentage(ref other)) => {
|
||||
this.interpolate(other, progress).map(MinLength::LengthOrPercentage)
|
||||
this.add_weighted(other, self_portion, other_portion)
|
||||
.map(MinLength::LengthOrPercentage)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
|
@ -1216,11 +1266,12 @@ impl Animatable for MinLength {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
||||
impl Animatable for MaxLength {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(MaxLength::LengthOrPercentage(ref this),
|
||||
MaxLength::LengthOrPercentage(ref other)) => {
|
||||
this.interpolate(other, progress).map(MaxLength::LengthOrPercentage)
|
||||
this.add_weighted(other, self_portion, other_portion)
|
||||
.map(MaxLength::LengthOrPercentage)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
|
@ -1242,15 +1293,15 @@ impl Animatable for MaxLength {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-length
|
||||
impl Animatable for LineHeight {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(LineHeight::Length(ref this),
|
||||
LineHeight::Length(ref other)) => {
|
||||
this.interpolate(other, progress).map(LineHeight::Length)
|
||||
this.add_weighted(other, self_portion, other_portion).map(LineHeight::Length)
|
||||
}
|
||||
(LineHeight::Number(ref this),
|
||||
LineHeight::Number(ref other)) => {
|
||||
this.interpolate(other, progress).map(LineHeight::Number)
|
||||
this.add_weighted(other, self_portion, other_portion).map(LineHeight::Number)
|
||||
}
|
||||
(LineHeight::Normal, LineHeight::Normal) => {
|
||||
Ok(LineHeight::Normal)
|
||||
|
@ -1278,10 +1329,10 @@ impl Animatable for LineHeight {
|
|||
/// http://dev.w3.org/csswg/css-transitions/#animtype-font-weight
|
||||
impl Animatable for FontWeight {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
let a = (*self as u32) as f64;
|
||||
let b = (*other as u32) as f64;
|
||||
let weight = a + (b - a) * progress;
|
||||
let weight = a * self_portion + b * other_portion;
|
||||
Ok(if weight < 150. {
|
||||
FontWeight::Weight100
|
||||
} else if weight < 250. {
|
||||
|
@ -1314,10 +1365,12 @@ impl Animatable for FontWeight {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-simple-list
|
||||
impl<H: Animatable, V: Animatable> Animatable for generic_position::Position<H, V> {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok(generic_position::Position {
|
||||
horizontal: try!(self.horizontal.interpolate(&other.horizontal, progress)),
|
||||
vertical: try!(self.vertical.interpolate(&other.vertical, progress)),
|
||||
horizontal: try!(self.horizontal.add_weighted(&other.horizontal,
|
||||
self_portion, other_portion)),
|
||||
vertical: try!(self.vertical.add_weighted(&other.vertical,
|
||||
self_portion, other_portion)),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1339,12 +1392,13 @@ impl<H, V> RepeatableListAnimatable for generic_position::Position<H, V>
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-rect
|
||||
impl Animatable for ClipRect {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
||||
-> Result<Self, ()> {
|
||||
Ok(ClipRect {
|
||||
top: try!(self.top.interpolate(&other.top, time)),
|
||||
right: try!(self.right.interpolate(&other.right, time)),
|
||||
bottom: try!(self.bottom.interpolate(&other.bottom, time)),
|
||||
left: try!(self.left.interpolate(&other.left, time)),
|
||||
top: try!(self.top.add_weighted(&other.top, self_portion, other_portion)),
|
||||
right: try!(self.right.add_weighted(&other.right, self_portion, other_portion)),
|
||||
bottom: try!(self.bottom.add_weighted(&other.bottom, self_portion, other_portion)),
|
||||
left: try!(self.left.add_weighted(&other.left, self_portion, other_portion)),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1366,7 +1420,7 @@ impl Animatable for ClipRect {
|
|||
<%def name="impl_animatable_for_shadow(item, transparent_color)">
|
||||
impl Animatable for ${item} {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
% if "Box" in item:
|
||||
// It can't be interpolated if inset does not match.
|
||||
if self.inset != other.inset {
|
||||
|
@ -1374,12 +1428,14 @@ impl Animatable for ClipRect {
|
|||
}
|
||||
% endif
|
||||
|
||||
let x = try!(self.offset_x.interpolate(&other.offset_x, progress));
|
||||
let y = try!(self.offset_y.interpolate(&other.offset_y, progress));
|
||||
let color = try!(self.color.interpolate(&other.color, progress));
|
||||
let blur = try!(self.blur_radius.interpolate(&other.blur_radius, progress));
|
||||
let x = try!(self.offset_x.add_weighted(&other.offset_x, self_portion, other_portion));
|
||||
let y = try!(self.offset_y.add_weighted(&other.offset_y, self_portion, other_portion));
|
||||
let color = try!(self.color.add_weighted(&other.color, self_portion, other_portion));
|
||||
let blur = try!(self.blur_radius.add_weighted(&other.blur_radius,
|
||||
self_portion, other_portion));
|
||||
% if "Box" in item:
|
||||
let spread = try!(self.spread_radius.interpolate(&other.spread_radius, progress));
|
||||
let spread = try!(self.spread_radius.add_weighted(&other.spread_radius,
|
||||
self_portion, other_portion));
|
||||
% endif
|
||||
|
||||
Ok(${item} {
|
||||
|
@ -1421,7 +1477,7 @@ impl Animatable for ClipRect {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
|
||||
impl Animatable for ${item}List {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
// The inset value must change
|
||||
% if "Box" in item:
|
||||
let mut zero = ${item} {
|
||||
|
@ -1449,18 +1505,18 @@ impl Animatable for ClipRect {
|
|||
for i in 0..max_len {
|
||||
let shadow = match (self.0.get(i), other.0.get(i)) {
|
||||
(Some(shadow), Some(other))
|
||||
=> try!(shadow.interpolate(other, progress)),
|
||||
=> try!(shadow.add_weighted(other, self_portion, other_portion)),
|
||||
(Some(shadow), None) => {
|
||||
% if "Box" in item:
|
||||
zero.inset = shadow.inset;
|
||||
% endif
|
||||
shadow.interpolate(&zero, progress).unwrap()
|
||||
shadow.add_weighted(&zero, self_portion, other_portion).unwrap()
|
||||
}
|
||||
(None, Some(shadow)) => {
|
||||
% if "Box" in item:
|
||||
zero.inset = shadow.inset;
|
||||
% endif
|
||||
zero.interpolate(&shadow, progress).unwrap()
|
||||
zero.add_weighted(&shadow, self_portion, other_portion).unwrap()
|
||||
}
|
||||
(None, None) => unreachable!(),
|
||||
};
|
||||
|
@ -1469,6 +1525,21 @@ impl Animatable for ClipRect {
|
|||
|
||||
Ok(${item}List(result))
|
||||
}
|
||||
|
||||
fn add(&self, other: &Self) -> Result<Self, ()> {
|
||||
let len = self.0.len() + other.0.len();
|
||||
|
||||
let mut result = if len > 1 {
|
||||
SmallVec::from_vec(Vec::with_capacity(len))
|
||||
} else {
|
||||
SmallVec::new()
|
||||
};
|
||||
|
||||
result.extend(self.0.iter().cloned());
|
||||
result.extend(other.0.iter().cloned());
|
||||
|
||||
Ok(${item}List(result))
|
||||
}
|
||||
}
|
||||
</%def>
|
||||
|
||||
|
@ -1541,11 +1612,12 @@ fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOp
|
|||
result
|
||||
}
|
||||
|
||||
/// Interpolate two transform lists.
|
||||
/// Add two transform lists.
|
||||
/// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
|
||||
fn interpolate_transform_list(from_list: &[TransformOperation],
|
||||
fn add_weighted_transform_lists(from_list: &[TransformOperation],
|
||||
to_list: &[TransformOperation],
|
||||
progress: f64) -> TransformList {
|
||||
self_portion: f64,
|
||||
other_portion: f64) -> TransformList {
|
||||
let mut result = vec![];
|
||||
|
||||
if can_interpolate_list(from_list, to_list) {
|
||||
|
@ -1553,33 +1625,33 @@ fn interpolate_transform_list(from_list: &[TransformOperation],
|
|||
match (from, to) {
|
||||
(&TransformOperation::Matrix(from),
|
||||
&TransformOperation::Matrix(_to)) => {
|
||||
let interpolated = from.interpolate(&_to, progress).unwrap();
|
||||
result.push(TransformOperation::Matrix(interpolated));
|
||||
let sum = from.add_weighted(&_to, self_portion, other_portion).unwrap();
|
||||
result.push(TransformOperation::Matrix(sum));
|
||||
}
|
||||
(&TransformOperation::MatrixWithPercents(_),
|
||||
&TransformOperation::MatrixWithPercents(_)) => {
|
||||
// We don't interpolate `-moz-transform` matrices yet.
|
||||
// We don't add_weighted `-moz-transform` matrices yet.
|
||||
// They contain percentage values.
|
||||
{}
|
||||
}
|
||||
(&TransformOperation::Skew(fx, fy),
|
||||
&TransformOperation::Skew(tx, ty)) => {
|
||||
let ix = fx.interpolate(&tx, progress).unwrap();
|
||||
let iy = fy.interpolate(&ty, progress).unwrap();
|
||||
let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap();
|
||||
let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap();
|
||||
result.push(TransformOperation::Skew(ix, iy));
|
||||
}
|
||||
(&TransformOperation::Translate(fx, fy, fz),
|
||||
&TransformOperation::Translate(tx, ty, tz)) => {
|
||||
let ix = fx.interpolate(&tx, progress).unwrap();
|
||||
let iy = fy.interpolate(&ty, progress).unwrap();
|
||||
let iz = fz.interpolate(&tz, progress).unwrap();
|
||||
let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap();
|
||||
let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap();
|
||||
let iz = fz.add_weighted(&tz, self_portion, other_portion).unwrap();
|
||||
result.push(TransformOperation::Translate(ix, iy, iz));
|
||||
}
|
||||
(&TransformOperation::Scale(fx, fy, fz),
|
||||
&TransformOperation::Scale(tx, ty, tz)) => {
|
||||
let ix = fx.interpolate(&tx, progress).unwrap();
|
||||
let iy = fy.interpolate(&ty, progress).unwrap();
|
||||
let iz = fz.interpolate(&tz, progress).unwrap();
|
||||
let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap();
|
||||
let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap();
|
||||
let iz = fz.add_weighted(&tz, self_portion, other_portion).unwrap();
|
||||
result.push(TransformOperation::Scale(ix, iy, iz));
|
||||
}
|
||||
(&TransformOperation::Rotate(fx, fy, fz, fa),
|
||||
|
@ -1589,14 +1661,15 @@ fn interpolate_transform_list(from_list: &[TransformOperation],
|
|||
let (fx, fy, fz) = (fx / norm_f, fy / norm_f, fz / norm_f);
|
||||
let (tx, ty, tz) = (tx / norm_t, ty / norm_t, tz / norm_t);
|
||||
if fx == tx && fy == ty && fz == tz {
|
||||
let ia = fa.interpolate(&ta, progress).unwrap();
|
||||
let ia = fa.add_weighted(&ta, self_portion, other_portion).unwrap();
|
||||
result.push(TransformOperation::Rotate(fx, fy, fz, ia));
|
||||
} else {
|
||||
let matrix_f = rotate_to_matrix(fx, fy, fz, fa);
|
||||
let matrix_t = rotate_to_matrix(tx, ty, tz, ta);
|
||||
let interpolated = matrix_f.interpolate(&matrix_t, progress).unwrap();
|
||||
let sum = matrix_f.add_weighted(&matrix_t, self_portion, other_portion)
|
||||
.unwrap();
|
||||
|
||||
result.push(TransformOperation::Matrix(interpolated));
|
||||
result.push(TransformOperation::Matrix(sum));
|
||||
}
|
||||
}
|
||||
(&TransformOperation::Perspective(fd),
|
||||
|
@ -1605,8 +1678,9 @@ fn interpolate_transform_list(from_list: &[TransformOperation],
|
|||
let mut td_matrix = ComputedMatrix::identity();
|
||||
fd_matrix.m43 = -1. / fd.to_f32_px();
|
||||
td_matrix.m43 = -1. / _td.to_f32_px();
|
||||
let interpolated = fd_matrix.interpolate(&td_matrix, progress).unwrap();
|
||||
result.push(TransformOperation::Matrix(interpolated));
|
||||
let sum = fd_matrix.add_weighted(&td_matrix, self_portion, other_portion)
|
||||
.unwrap();
|
||||
result.push(TransformOperation::Matrix(sum));
|
||||
}
|
||||
_ => {
|
||||
// This should be unreachable due to the can_interpolate_list() call.
|
||||
|
@ -1685,37 +1759,37 @@ pub struct MatrixDecomposed2D {
|
|||
}
|
||||
|
||||
impl Animatable for InnerMatrix2D {
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok(InnerMatrix2D {
|
||||
m11: try!(self.m11.interpolate(&other.m11, progress)),
|
||||
m12: try!(self.m12.interpolate(&other.m12, progress)),
|
||||
m21: try!(self.m21.interpolate(&other.m21, progress)),
|
||||
m22: try!(self.m22.interpolate(&other.m22, progress)),
|
||||
m11: try!(self.m11.add_weighted(&other.m11, self_portion, other_portion)),
|
||||
m12: try!(self.m12.add_weighted(&other.m12, self_portion, other_portion)),
|
||||
m21: try!(self.m21.add_weighted(&other.m21, self_portion, other_portion)),
|
||||
m22: try!(self.m22.add_weighted(&other.m22, self_portion, other_portion)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Animatable for Translate2D {
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok(Translate2D(
|
||||
try!(self.0.interpolate(&other.0, progress)),
|
||||
try!(self.1.interpolate(&other.1, progress))
|
||||
try!(self.0.add_weighted(&other.0, self_portion, other_portion)),
|
||||
try!(self.1.add_weighted(&other.1, self_portion, other_portion))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Animatable for Scale2D {
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok(Scale2D(
|
||||
try!(self.0.interpolate(&other.0, progress)),
|
||||
try!(self.1.interpolate(&other.1, progress))
|
||||
try!(self.0.add_weighted(&other.0, self_portion, other_portion)),
|
||||
try!(self.1.add_weighted(&other.1, self_portion, other_portion))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Animatable for MatrixDecomposed2D {
|
||||
/// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-2d-matrix-values
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
// If x-axis of one is flipped, and y-axis of the other,
|
||||
// convert to an unflipped rotation.
|
||||
let mut scale = self.scale;
|
||||
|
@ -1745,10 +1819,11 @@ impl Animatable for MatrixDecomposed2D {
|
|||
}
|
||||
|
||||
// Interpolate all values.
|
||||
let translate = try!(self.translate.interpolate(&other.translate, progress));
|
||||
let scale = try!(scale.interpolate(&other.scale, progress));
|
||||
let angle = try!(angle.interpolate(&other_angle, progress));
|
||||
let matrix = try!(self.matrix.interpolate(&other.matrix, progress));
|
||||
let translate = try!(self.translate.add_weighted(&other.translate,
|
||||
self_portion, other_portion));
|
||||
let scale = try!(scale.add_weighted(&other.scale, self_portion, other_portion));
|
||||
let angle = try!(angle.add_weighted(&other_angle, self_portion, other_portion));
|
||||
let matrix = try!(self.matrix.add_weighted(&other.matrix, self_portion, other_portion));
|
||||
|
||||
Ok(MatrixDecomposed2D {
|
||||
translate: translate,
|
||||
|
@ -1760,25 +1835,26 @@ impl Animatable for MatrixDecomposed2D {
|
|||
}
|
||||
|
||||
impl Animatable for ComputedMatrix {
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
if self.is_3d() || other.is_3d() {
|
||||
let decomposed_from = decompose_3d_matrix(*self);
|
||||
let decomposed_to = decompose_3d_matrix(*other);
|
||||
match (decomposed_from, decomposed_to) {
|
||||
(Ok(from), Ok(to)) => {
|
||||
let interpolated = try!(from.interpolate(&to, progress));
|
||||
Ok(ComputedMatrix::from(interpolated))
|
||||
let sum = try!(from.add_weighted(&to, self_portion, other_portion));
|
||||
Ok(ComputedMatrix::from(sum))
|
||||
},
|
||||
_ => {
|
||||
let interpolated = if progress < 0.5 {*self} else {*other};
|
||||
Ok(interpolated)
|
||||
let result = if self_portion > other_portion {*self} else {*other};
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let decomposed_from = MatrixDecomposed2D::from(*self);
|
||||
let decomposed_to = MatrixDecomposed2D::from(*other);
|
||||
let interpolated = try!(decomposed_from.interpolate(&decomposed_to, progress));
|
||||
Ok(ComputedMatrix::from(interpolated))
|
||||
let sum = try!(decomposed_from.add_weighted(&decomposed_to,
|
||||
self_portion, other_portion));
|
||||
Ok(ComputedMatrix::from(sum))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2094,58 +2170,64 @@ fn cross(row1: [f32; 3], row2: [f32; 3]) -> [f32; 3] {
|
|||
}
|
||||
|
||||
impl Animatable for Translate3D {
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok(Translate3D(
|
||||
try!(self.0.interpolate(&other.0, progress)),
|
||||
try!(self.1.interpolate(&other.1, progress)),
|
||||
try!(self.2.interpolate(&other.2, progress))
|
||||
try!(self.0.add_weighted(&other.0, self_portion, other_portion)),
|
||||
try!(self.1.add_weighted(&other.1, self_portion, other_portion)),
|
||||
try!(self.2.add_weighted(&other.2, self_portion, other_portion))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Animatable for Scale3D {
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok(Scale3D(
|
||||
try!(self.0.interpolate(&other.0, progress)),
|
||||
try!(self.1.interpolate(&other.1, progress)),
|
||||
try!(self.2.interpolate(&other.2, progress))
|
||||
try!(self.0.add_weighted(&other.0, self_portion, other_portion)),
|
||||
try!(self.1.add_weighted(&other.1, self_portion, other_portion)),
|
||||
try!(self.2.add_weighted(&other.2, self_portion, other_portion))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Animatable for Skew {
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok(Skew(
|
||||
try!(self.0.interpolate(&other.0, progress)),
|
||||
try!(self.1.interpolate(&other.1, progress)),
|
||||
try!(self.2.interpolate(&other.2, progress))
|
||||
try!(self.0.add_weighted(&other.0, self_portion, other_portion)),
|
||||
try!(self.1.add_weighted(&other.1, self_portion, other_portion)),
|
||||
try!(self.2.add_weighted(&other.2, self_portion, other_portion))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Animatable for Perspective {
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok(Perspective(
|
||||
try!(self.0.interpolate(&other.0, progress)),
|
||||
try!(self.1.interpolate(&other.1, progress)),
|
||||
try!(self.2.interpolate(&other.2, progress)),
|
||||
try!(self.3.interpolate(&other.3, progress))
|
||||
try!(self.0.add_weighted(&other.0, self_portion, other_portion)),
|
||||
try!(self.1.add_weighted(&other.1, self_portion, other_portion)),
|
||||
try!(self.2.add_weighted(&other.2, self_portion, other_portion)),
|
||||
try!(self.3.add_weighted(&other.3, self_portion, other_portion))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Animatable for MatrixDecomposed3D {
|
||||
/// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-3d-matrix-values
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
let mut interpolated = *self;
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
||||
-> Result<Self, ()> {
|
||||
assert!(self_portion + other_portion == 1.0f64,
|
||||
"add_weighted should only be used for interpolating transforms");
|
||||
|
||||
// Interpolate translate, scale, skew and perspective components.
|
||||
interpolated.translate = try!(self.translate.interpolate(&other.translate, progress));
|
||||
interpolated.scale = try!(self.scale.interpolate(&other.scale, progress));
|
||||
interpolated.skew = try!(self.skew.interpolate(&other.skew, progress));
|
||||
interpolated.perspective = try!(self.perspective.interpolate(&other.perspective, progress));
|
||||
let mut sum = *self;
|
||||
|
||||
// Interpolate quaternions using spherical linear interpolation (Slerp).
|
||||
// Add translate, scale, skew and perspective components.
|
||||
sum.translate = try!(self.translate.add_weighted(&other.translate,
|
||||
self_portion, other_portion));
|
||||
sum.scale = try!(self.scale.add_weighted(&other.scale, self_portion, other_portion));
|
||||
sum.skew = try!(self.skew.add_weighted(&other.skew, self_portion, other_portion));
|
||||
sum.perspective = try!(self.perspective.add_weighted(&other.perspective,
|
||||
self_portion, other_portion));
|
||||
|
||||
// Add quaternions using spherical linear interpolation (Slerp).
|
||||
let mut product = self.quaternion.0 * other.quaternion.0 +
|
||||
self.quaternion.1 * other.quaternion.1 +
|
||||
self.quaternion.2 * other.quaternion.2 +
|
||||
|
@ -2156,21 +2238,21 @@ impl Animatable for MatrixDecomposed3D {
|
|||
product = product.max(-1.0);
|
||||
|
||||
if product == 1.0 {
|
||||
return Ok(interpolated);
|
||||
return Ok(sum);
|
||||
}
|
||||
|
||||
let theta = product.acos();
|
||||
let w = (progress as f32 * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
|
||||
let w = (other_portion as f32 * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
|
||||
|
||||
let mut a = *self;
|
||||
let mut b = *other;
|
||||
% for i in range(4):
|
||||
a.quaternion.${i} *= (progress as f32 * theta).cos() - product * w;
|
||||
a.quaternion.${i} *= (other_portion as f32 * theta).cos() - product * w;
|
||||
b.quaternion.${i} *= w;
|
||||
interpolated.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i};
|
||||
sum.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i};
|
||||
% endfor
|
||||
|
||||
Ok(interpolated)
|
||||
Ok(sum)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2374,22 +2456,22 @@ impl ComputedMatrix {
|
|||
/// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms
|
||||
impl Animatable for TransformList {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &TransformList, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &TransformList, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
|
||||
let result = match (&self.0, &other.0) {
|
||||
(&Some(ref from_list), &Some(ref to_list)) => {
|
||||
// Two lists of transforms
|
||||
interpolate_transform_list(from_list, &to_list, progress)
|
||||
add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion)
|
||||
}
|
||||
(&Some(ref from_list), &None) => {
|
||||
// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
|
||||
let to_list = build_identity_transform_list(from_list);
|
||||
interpolate_transform_list(from_list, &to_list, progress)
|
||||
add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion)
|
||||
}
|
||||
(&None, &Some(ref to_list)) => {
|
||||
// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
|
||||
let from_list = build_identity_transform_list(to_list);
|
||||
interpolate_transform_list(&from_list, to_list, progress)
|
||||
add_weighted_transform_lists(&from_list, to_list, self_portion, other_portion)
|
||||
}
|
||||
_ => {
|
||||
// http://dev.w3.org/csswg/css-transforms/#none-none-animation
|
||||
|
@ -2399,23 +2481,40 @@ impl Animatable for TransformList {
|
|||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn add(&self, other: &Self) -> Result<Self, ()> {
|
||||
match (&self.0, &other.0) {
|
||||
(&Some(ref from_list), &Some(ref to_list)) => {
|
||||
Ok(TransformList(Some([&from_list[..], &to_list[..]].concat())))
|
||||
}
|
||||
(&Some(_), &None) => {
|
||||
Ok(self.clone())
|
||||
}
|
||||
(&None, &Some(_)) => {
|
||||
Ok(other.clone())
|
||||
}
|
||||
_ => {
|
||||
Ok(TransformList(None))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Animatable for Either<T, U>
|
||||
where T: Animatable + Copy, U: Animatable + Copy,
|
||||
{
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(Either::First(ref this), Either::First(ref other)) => {
|
||||
this.interpolate(&other, progress).map(Either::First)
|
||||
this.add_weighted(&other, self_portion, other_portion).map(Either::First)
|
||||
},
|
||||
(Either::Second(ref this), Either::Second(ref other)) => {
|
||||
this.interpolate(&other, progress).map(Either::Second)
|
||||
this.add_weighted(&other, self_portion, other_portion).map(Either::Second)
|
||||
},
|
||||
_ => {
|
||||
let interpolated = if progress < 0.5 { *self } else { *other };
|
||||
Ok(interpolated)
|
||||
let result = if self_portion > other_portion {*self} else {*other};
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2497,21 +2596,26 @@ impl IntermediateRGBA {
|
|||
/// Unlike Animatable for RGBA we don't clamp any component values.
|
||||
impl Animatable for IntermediateRGBA {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &IntermediateRGBA, progress: f64) -> Result<Self, ()> {
|
||||
let alpha = try!(self.alpha.interpolate(&other.alpha, progress));
|
||||
if alpha == 0. {
|
||||
fn add_weighted(&self, other: &IntermediateRGBA, self_portion: f64, other_portion: f64)
|
||||
-> Result<Self, ()> {
|
||||
let mut alpha = try!(self.alpha.add_weighted(&other.alpha, self_portion, other_portion));
|
||||
if alpha <= 0. {
|
||||
// Ideally we should return color value that only alpha component is
|
||||
// 0, but this is what current gecko does.
|
||||
Ok(IntermediateRGBA::transparent())
|
||||
} else {
|
||||
alpha = alpha.min(1.);
|
||||
let red = try!((self.red * self.alpha)
|
||||
.interpolate(&(other.red * other.alpha), progress))
|
||||
.add_weighted(&(other.red * other.alpha),
|
||||
self_portion, other_portion))
|
||||
* 1. / alpha;
|
||||
let green = try!((self.green * self.alpha)
|
||||
.interpolate(&(other.green * other.alpha), progress))
|
||||
.add_weighted(&(other.green * other.alpha),
|
||||
self_portion, other_portion))
|
||||
* 1. / alpha;
|
||||
let blue = try!((self.blue * self.alpha)
|
||||
.interpolate(&(other.blue * other.alpha), progress))
|
||||
.add_weighted(&(other.blue * other.alpha),
|
||||
self_portion, other_portion))
|
||||
* 1. / alpha;
|
||||
Ok(IntermediateRGBA::new(red, green, blue, alpha))
|
||||
}
|
||||
|
@ -2588,10 +2692,12 @@ pub enum IntermediateColor {
|
|||
|
||||
impl Animatable for IntermediateColor {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(IntermediateColor::IntermediateRGBA(ref this), IntermediateColor::IntermediateRGBA(ref other)) => {
|
||||
this.interpolate(other, progress).map(IntermediateColor::IntermediateRGBA)
|
||||
(IntermediateColor::IntermediateRGBA(ref this),
|
||||
IntermediateColor::IntermediateRGBA(ref other)) => {
|
||||
this.add_weighted(other, self_portion, other_portion)
|
||||
.map(IntermediateColor::IntermediateRGBA)
|
||||
}
|
||||
// FIXME: Bug 1345709: Implement currentColor animations.
|
||||
_ => Err(()),
|
||||
|
|
|
@ -190,13 +190,16 @@ ${helpers.single_keyword("background-origin",
|
|||
impl RepeatableListAnimatable for T {}
|
||||
|
||||
impl Animatable for T {
|
||||
fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
||||
-> Result<Self, ()> {
|
||||
use properties::longhands::background_size::single_value::computed_value::ExplicitSize;
|
||||
match (self, other) {
|
||||
(&T::Explicit(ref me), &T::Explicit(ref other)) => {
|
||||
Ok(T::Explicit(ExplicitSize {
|
||||
width: try!(me.width.interpolate(&other.width, time)),
|
||||
height: try!(me.height.interpolate(&other.height, time)),
|
||||
width: try!(me.width.add_weighted(&other.width,
|
||||
self_portion, other_portion)),
|
||||
height: try!(me.height.add_weighted(&other.height,
|
||||
self_portion, other_portion)),
|
||||
}))
|
||||
}
|
||||
_ => Err(()),
|
||||
|
|
|
@ -2198,11 +2198,14 @@ ${helpers.single_keyword("transform-style",
|
|||
|
||||
impl Animatable for T {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
||||
-> Result<Self, ()> {
|
||||
Ok(T {
|
||||
horizontal: try!(self.horizontal.interpolate(&other.horizontal, time)),
|
||||
vertical: try!(self.vertical.interpolate(&other.vertical, time)),
|
||||
depth: try!(self.depth.interpolate(&other.depth, time)),
|
||||
horizontal: try!(self.horizontal.add_weighted(&other.horizontal,
|
||||
self_portion, other_portion)),
|
||||
vertical: try!(self.vertical.add_weighted(&other.vertical,
|
||||
self_portion, other_portion)),
|
||||
depth: try!(self.depth.add_weighted(&other.depth, self_portion, other_portion)),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1073,10 +1073,12 @@ ${helpers.single_keyword_system("font-variant-caps",
|
|||
}
|
||||
|
||||
impl Animatable for T {
|
||||
fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
||||
-> Result<Self, ()> {
|
||||
match (*self, *other) {
|
||||
(T::Number(ref number), T::Number(ref other)) =>
|
||||
Ok(T::Number(try!(number.interpolate(other, time)))),
|
||||
Ok(T::Number(try!(number.add_weighted(other,
|
||||
self_portion, other_portion)))),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,10 +42,13 @@ ${helpers.single_keyword("caption-side", "top bottom",
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-simple-list
|
||||
impl Animatable for T {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
||||
-> Result<Self, ()> {
|
||||
Ok(T {
|
||||
horizontal: try!(self.horizontal.interpolate(&other.horizontal, time)),
|
||||
vertical: try!(self.vertical.interpolate(&other.vertical, time)),
|
||||
horizontal: try!(self.horizontal.add_weighted(&other.horizontal,
|
||||
self_portion, other_portion)),
|
||||
vertical: try!(self.vertical.add_weighted(&other.vertical,
|
||||
self_portion, other_portion)),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,8 @@ macro_rules! define_keyword_type {
|
|||
|
||||
impl Animatable for $name {
|
||||
#[inline]
|
||||
fn interpolate(&self, _other: &Self, _progress: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, _other: &Self, _self_progress: f64, _other_progress: f64)
|
||||
-> Result<Self, ()> {
|
||||
Ok($name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ use style::gecko_bindings::structs;
|
|||
use style::gecko_bindings::structs::{RawServoStyleRule, ServoStyleSheet};
|
||||
use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom, nsCSSPropertyID};
|
||||
use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, nsCSSFontFaceRule};
|
||||
use style::gecko_bindings::structs::CompositeOperation;
|
||||
use style::gecko_bindings::structs::Loader;
|
||||
use style::gecko_bindings::structs::RawGeckoPresContextOwned;
|
||||
use style::gecko_bindings::structs::ServoElementSnapshotTable;
|
||||
|
@ -325,12 +326,16 @@ pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMa
|
|||
let property: TransitionProperty = css_property.into();
|
||||
let value_map = AnimationValueMap::from_ffi_mut(raw_value_map);
|
||||
|
||||
let need_underlying_value = segment.mFromValue.mServo.mRawPtr.is_null() ||
|
||||
segment.mToValue.mServo.mRawPtr.is_null() ||
|
||||
segment.mFromComposite != CompositeOperation::Replace ||
|
||||
segment.mToComposite != CompositeOperation::Replace;
|
||||
|
||||
// If either of the segment endpoints are null, get the underlying value to
|
||||
// use from the current value in the values map (set by a lower-priority
|
||||
// effect), or, if there is no current value, look up the cached base value
|
||||
// for this property.
|
||||
let underlying_value = if segment.mFromValue.mServo.mRawPtr.is_null() ||
|
||||
segment.mToValue.mServo.mRawPtr.is_null() {
|
||||
let underlying_value = if need_underlying_value {
|
||||
let previous_composed_value = value_map.get(&property).cloned();
|
||||
previous_composed_value.or_else(|| {
|
||||
let raw_base_style = unsafe { Gecko_AnimationGetBaseStyle(base_values, css_property) };
|
||||
|
@ -340,26 +345,40 @@ pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMa
|
|||
None
|
||||
};
|
||||
|
||||
if (segment.mFromValue.mServo.mRawPtr.is_null() ||
|
||||
segment.mToValue.mServo.mRawPtr.is_null()) &&
|
||||
underlying_value.is_none() {
|
||||
warn!("Underlying value should be valid in the case where either 'from' value or 'to' value is null");
|
||||
if need_underlying_value && underlying_value.is_none() {
|
||||
warn!("Underlying value should be valid when we expect to use it");
|
||||
return;
|
||||
}
|
||||
|
||||
// Declare for making derefenced raw pointer alive outside the if block.
|
||||
// Temporaries used in the following if-block whose lifetimes we need to prlong.
|
||||
let raw_from_value;
|
||||
let from_composite_result;
|
||||
let from_value = if !segment.mFromValue.mServo.mRawPtr.is_null() {
|
||||
raw_from_value = unsafe { &*segment.mFromValue.mServo.mRawPtr };
|
||||
AnimationValue::as_arc(&raw_from_value).as_ref()
|
||||
match segment.mFromComposite {
|
||||
CompositeOperation::Add => {
|
||||
let value_to_composite = AnimationValue::as_arc(&raw_from_value).as_ref();
|
||||
from_composite_result = underlying_value.as_ref().unwrap().add(value_to_composite);
|
||||
from_composite_result.as_ref().unwrap_or(value_to_composite)
|
||||
}
|
||||
_ => { AnimationValue::as_arc(&raw_from_value) }
|
||||
}
|
||||
} else {
|
||||
underlying_value.as_ref().unwrap()
|
||||
};
|
||||
|
||||
let raw_to_value;
|
||||
let to_composite_result;
|
||||
let to_value = if !segment.mToValue.mServo.mRawPtr.is_null() {
|
||||
raw_to_value = unsafe { &*segment.mToValue.mServo.mRawPtr };
|
||||
AnimationValue::as_arc(&raw_to_value).as_ref()
|
||||
match segment.mToComposite {
|
||||
CompositeOperation::Add => {
|
||||
let value_to_composite = AnimationValue::as_arc(&raw_to_value).as_ref();
|
||||
to_composite_result = underlying_value.as_ref().unwrap().add(value_to_composite);
|
||||
to_composite_result.as_ref().unwrap_or(value_to_composite)
|
||||
}
|
||||
_ => { AnimationValue::as_arc(&raw_to_value) }
|
||||
}
|
||||
} else {
|
||||
underlying_value.as_ref().unwrap()
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue