Partial implementation of transform animations spec.

Also tidy up some of the ComputedMatrix code.
This commit is contained in:
Glenn Watson 2015-06-04 11:04:45 +10:00
parent cc8ad768b9
commit 6dd082278d
5 changed files with 212 additions and 71 deletions

View file

@ -17,7 +17,11 @@ use properties::longhands::text_shadow::computed_value::TextShadow;
use properties::longhands::text_shadow::computed_value::T as TextShadowList;
use properties::longhands::background_position::computed_value::T as BackgroundPosition;
use properties::longhands::transition_property;
use values::computed::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, LengthOrPercentage, Length, Time};
use properties::longhands::transform::computed_value::ComputedMatrix;
use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
use properties::longhands::transform::computed_value::T as TransformList;
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
use values::computed::{LengthAndPercentage, LengthOrPercentage, Length, Time};
use values::CSSFloat;
use cssparser::{RGBA, Color};
@ -92,6 +96,10 @@ impl PropertyAnimation {
AnimatedProperty::TextShadow(old_style.get_effects().text_shadow.clone(),
new_style.get_effects().text_shadow.clone())
}
TransitionProperty::Transform => {
AnimatedProperty::Transform(old_style.get_effects().transform.clone(),
new_style.get_effects().transform.clone())
}
}
}
}
@ -220,6 +228,7 @@ impl PropertyAnimation {
[TextIndent; mutate_inheritedtext; text_indent],
[TextShadow; mutate_effects; text_shadow],
[Top; mutate_positionoffsets; top],
[Transform; mutate_effects; transform],
[VerticalAlign; mutate_box; vertical_align],
[Visibility; mutate_inheritedbox; visibility],
[Width; mutate_box; width],
@ -274,6 +283,7 @@ enum AnimatedProperty {
TextIndent(LengthOrPercentage, LengthOrPercentage),
TextShadow(TextShadowList, TextShadowList),
Top(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
Transform(TransformList, TransformList),
VerticalAlign(VerticalAlign, VerticalAlign),
Visibility(Visibility, Visibility),
Width(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
@ -329,6 +339,7 @@ impl AnimatedProperty {
AnimatedProperty::Visibility(ref a, ref b) => a == b,
AnimatedProperty::WordSpacing(ref a, ref b) => a == b,
AnimatedProperty::ZIndex(ref a, ref b) => a == b,
AnimatedProperty::Transform(ref a, ref b) => a == b,
}
}
}
@ -495,6 +506,17 @@ impl Interpolate for LengthOrPercentage {
}
}
impl Interpolate for LengthAndPercentage {
#[inline]
fn interpolate(&self, other: &LengthAndPercentage, time: f32)
-> Option<LengthAndPercentage> {
Some(LengthAndPercentage {
length: self.length.interpolate(&other.length, time).unwrap(),
percentage: self.percentage.interpolate(&other.percentage, time).unwrap(),
})
}
}
impl Interpolate for LengthOrPercentageOrAuto {
#[inline]
fn interpolate(&self, other: &LengthOrPercentageOrAuto, time: f32)
@ -670,6 +692,159 @@ impl Interpolate for TextShadowList {
}
}
/// Check if it's possible to do a direct numerical interpolation
/// between these two transform lists.
/// http://dev.w3.org/csswg/css-transforms/#transform-transform-animation
fn can_interpolate_list(from_list: &Vec<TransformOperation>,
to_list: &Vec<TransformOperation>) -> bool {
// Lists must be equal length
if from_list.len() != to_list.len() {
return false;
}
// Each transform operation must match primitive type in other list
for (from, to) in from_list.iter().zip(to_list.iter()) {
match (from, to) {
(&TransformOperation::Matrix(..), &TransformOperation::Matrix(..)) |
(&TransformOperation::Skew(..), &TransformOperation::Skew(..)) |
(&TransformOperation::Translate(..), &TransformOperation::Translate(..)) |
(&TransformOperation::Scale(..), &TransformOperation::Scale(..)) |
(&TransformOperation::Rotate(..), &TransformOperation::Rotate(..)) |
(&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => {}
_ => {
return false;
}
}
}
true
}
/// Interpolate two transform lists.
/// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
fn interpolate_transform_list(from_list: &Vec<TransformOperation>,
to_list: &Vec<TransformOperation>,
time: f32) -> TransformList {
let mut result = vec!();
if can_interpolate_list(from_list, to_list) {
for (from, to) in from_list.iter().zip(to_list.iter()) {
match (from, to) {
(&TransformOperation::Matrix(from),
&TransformOperation::Matrix(_to)) => {
// TODO(gw): Implement matrix decomposition and interpolation
result.push(TransformOperation::Matrix(from));
}
(&TransformOperation::Skew(fx, fy),
&TransformOperation::Skew(tx, ty)) => {
let ix = fx.interpolate(&tx, time).unwrap();
let iy = fy.interpolate(&ty, time).unwrap();
result.push(TransformOperation::Skew(ix, iy));
}
(&TransformOperation::Translate(fx, fy, fz),
&TransformOperation::Translate(tx, ty, tz)) => {
let ix = fx.interpolate(&tx, time).unwrap();
let iy = fy.interpolate(&ty, time).unwrap();
let iz = fz.interpolate(&tz, time).unwrap();
result.push(TransformOperation::Translate(ix, iy, iz));
}
(&TransformOperation::Scale(fx, fy, fz),
&TransformOperation::Scale(tx, ty, tz)) => {
let ix = fx.interpolate(&tx, time).unwrap();
let iy = fy.interpolate(&ty, time).unwrap();
let iz = fz.interpolate(&tz, time).unwrap();
result.push(TransformOperation::Scale(ix, iy, iz));
}
(&TransformOperation::Rotate(fx, fy, fz, fa),
&TransformOperation::Rotate(_tx, _ty, _tz, _ta)) => {
// TODO(gw): Implement matrix decomposition and interpolation
result.push(TransformOperation::Rotate(fx, fy, fz, fa));
}
(&TransformOperation::Perspective(fd),
&TransformOperation::Perspective(_td)) => {
// TODO(gw): Implement matrix decomposition and interpolation
result.push(TransformOperation::Perspective(fd));
}
_ => {
// This should be unreachable due to the can_interpolate_list() call.
unreachable!();
}
}
}
} else {
// TODO(gw): Implement matrix decomposition and interpolation
result.push_all(from_list);
}
Some(result)
}
/// Build an equivalent 'identity transform function list' based
/// on an existing transform list.
/// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
fn build_identity_transform_list(list: &Vec<TransformOperation>) -> Vec<TransformOperation> {
let mut result = vec!();
for operation in list {
match operation {
&TransformOperation::Matrix(..) => {
let identity = ComputedMatrix::identity();
result.push(TransformOperation::Matrix(identity));
}
&TransformOperation::Skew(..) => {
result.push(TransformOperation::Skew(0.0, 0.0));
}
&TransformOperation::Translate(..) => {
result.push(TransformOperation::Translate(LengthAndPercentage::zero(),
LengthAndPercentage::zero(),
Au(0)));
}
&TransformOperation::Scale(..) => {
result.push(TransformOperation::Scale(1.0, 1.0, 1.0));
}
&TransformOperation::Rotate(..) => {
result.push(TransformOperation::Rotate(0.0, 0.0, 1.0, Angle(0.0)));
}
&TransformOperation::Perspective(..) => {
// http://dev.w3.org/csswg/css-transforms/#identity-transform-function
let identity = ComputedMatrix::identity();
result.push(TransformOperation::Matrix(identity));
}
}
}
result
}
impl Interpolate for TransformList {
#[inline]
fn interpolate(&self, other: &TransformList, time: f32) -> Option<TransformList> {
// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
let result = match (self, other) {
(&Some(ref from_list), &Some(ref to_list)) => {
// Two lists of transforms
interpolate_transform_list(from_list, &to_list, time)
}
(&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, time)
}
(&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, time)
}
(&None, &None) => {
// http://dev.w3.org/csswg/css-transforms/#none-none-animation
None
}
};
Some(result)
}
}
/// Accesses an element of an array, "wrapping around" using modular arithmetic. This is needed
/// to handle values of differing lengths according to CSS-TRANSITIONS § 2.
pub trait GetMod {