mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
style: Refactor all the animated properties to use the style system properly
This commit is contained in:
parent
818bc6d4a2
commit
6a362ae8e8
11 changed files with 929 additions and 1124 deletions
|
@ -6,36 +6,17 @@
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use bezier::Bezier;
|
use bezier::Bezier;
|
||||||
use cssparser::{Color, RGBA};
|
|
||||||
use dom::{OpaqueNode, TRestyleDamage};
|
|
||||||
use euclid::point::Point2D;
|
use euclid::point::Point2D;
|
||||||
use properties::longhands::background_position::computed_value::T as BackgroundPosition;
|
use dom::{OpaqueNode, TRestyleDamage};
|
||||||
use properties::longhands::border_spacing::computed_value::T as BorderSpacing;
|
use properties::animated_properties::{AnimatedProperty, TransitionProperty};
|
||||||
use properties::longhands::clip::computed_value::ClipRect;
|
|
||||||
use properties::longhands::font_weight::computed_value::T as FontWeight;
|
|
||||||
use properties::longhands::line_height::computed_value::T as LineHeight;
|
|
||||||
use properties::longhands::text_shadow::computed_value::T as TextShadowList;
|
|
||||||
use properties::longhands::text_shadow::computed_value::TextShadow;
|
|
||||||
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 properties::longhands::transition_property;
|
|
||||||
use properties::longhands::transition_property::computed_value::TransitionProperty;
|
|
||||||
use properties::longhands::transition_timing_function::computed_value::StartEnd;
|
use properties::longhands::transition_timing_function::computed_value::StartEnd;
|
||||||
use properties::longhands::transition_timing_function::computed_value::TransitionTimingFunction;
|
use properties::longhands::transition_timing_function::computed_value::TransitionTimingFunction;
|
||||||
use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
|
|
||||||
use properties::longhands::visibility::computed_value::T as Visibility;
|
|
||||||
use properties::longhands::z_index::computed_value::T as ZIndex;
|
|
||||||
use properties::style_struct_traits::Box;
|
use properties::style_struct_traits::Box;
|
||||||
use properties::{ComputedValues, ServoComputedValues};
|
use properties::{ComputedValues, ServoComputedValues};
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::iter::repeat;
|
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use time;
|
use time;
|
||||||
use values::CSSFloat;
|
use values::computed::Time;
|
||||||
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
|
|
||||||
use values::computed::{CalcLengthOrPercentage, Length, LengthOrPercentage, Time};
|
|
||||||
|
|
||||||
/// State relating to an animation.
|
/// State relating to an animation.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -74,13 +55,18 @@ impl PropertyAnimation {
|
||||||
old_style: &ServoComputedValues,
|
old_style: &ServoComputedValues,
|
||||||
new_style: &mut ServoComputedValues)
|
new_style: &mut ServoComputedValues)
|
||||||
-> Vec<PropertyAnimation> {
|
-> Vec<PropertyAnimation> {
|
||||||
let mut result = Vec::new();
|
let mut result = vec![];
|
||||||
let transition_property =
|
let box_style = new_style.as_servo().get_box();
|
||||||
new_style.as_servo().get_box().transition_property.0[transition_index];
|
let transition_property = box_style.transition_property.0[transition_index];
|
||||||
|
let timing_function = *box_style.transition_timing_function.0.get_mod(transition_index);
|
||||||
|
let duration = *box_style.transition_duration.0.get_mod(transition_index);
|
||||||
|
|
||||||
|
|
||||||
if transition_property != TransitionProperty::All {
|
if transition_property != TransitionProperty::All {
|
||||||
if let Some(property_animation) =
|
if let Some(property_animation) =
|
||||||
PropertyAnimation::from_transition_property(transition_property,
|
PropertyAnimation::from_transition_property(transition_property,
|
||||||
transition_index,
|
timing_function,
|
||||||
|
duration,
|
||||||
old_style,
|
old_style,
|
||||||
new_style) {
|
new_style) {
|
||||||
result.push(property_animation)
|
result.push(property_animation)
|
||||||
|
@ -88,114 +74,40 @@ impl PropertyAnimation {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
for transition_property in
|
TransitionProperty::each(|transition_property| {
|
||||||
transition_property::computed_value::ALL_TRANSITION_PROPERTIES.iter() {
|
|
||||||
if let Some(property_animation) =
|
if let Some(property_animation) =
|
||||||
PropertyAnimation::from_transition_property(*transition_property,
|
PropertyAnimation::from_transition_property(transition_property,
|
||||||
transition_index,
|
timing_function,
|
||||||
|
duration,
|
||||||
old_style,
|
old_style,
|
||||||
new_style) {
|
new_style) {
|
||||||
result.push(property_animation)
|
result.push(property_animation)
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_transition_property(transition_property: TransitionProperty,
|
fn from_transition_property(transition_property: TransitionProperty,
|
||||||
transition_index: usize,
|
timing_function: TransitionTimingFunction,
|
||||||
|
duration: Time,
|
||||||
old_style: &ServoComputedValues,
|
old_style: &ServoComputedValues,
|
||||||
new_style: &mut ServoComputedValues)
|
new_style: &ServoComputedValues)
|
||||||
-> Option<PropertyAnimation> {
|
-> Option<PropertyAnimation> {
|
||||||
let box_style = new_style.get_box();
|
let animated_property = AnimatedProperty::from_transition_property(&transition_property,
|
||||||
macro_rules! match_transition {
|
old_style,
|
||||||
( $( [$name:ident; $structname:ident; $field:ident] ),* ) => {
|
new_style);
|
||||||
match transition_property {
|
|
||||||
TransitionProperty::All => {
|
|
||||||
panic!("Don't use `TransitionProperty::All` with \
|
|
||||||
`PropertyAnimation::from_transition_property`!")
|
|
||||||
}
|
|
||||||
$(
|
|
||||||
TransitionProperty::$name => {
|
|
||||||
AnimatedProperty::$name(old_style.$structname().$field,
|
|
||||||
new_style.$structname().$field)
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
TransitionProperty::Clip => {
|
|
||||||
AnimatedProperty::Clip(old_style.get_effects().clip.0,
|
|
||||||
new_style.get_effects().clip.0)
|
|
||||||
}
|
|
||||||
TransitionProperty::LetterSpacing => {
|
|
||||||
AnimatedProperty::LetterSpacing(old_style.get_inheritedtext().letter_spacing.0,
|
|
||||||
new_style.get_inheritedtext().letter_spacing.0)
|
|
||||||
}
|
|
||||||
TransitionProperty::TextShadow => {
|
|
||||||
AnimatedProperty::TextShadow(old_style.get_inheritedtext().text_shadow.clone(),
|
|
||||||
new_style.get_inheritedtext().text_shadow.clone())
|
|
||||||
}
|
|
||||||
TransitionProperty::Transform => {
|
|
||||||
AnimatedProperty::Transform(old_style.get_effects().transform.clone(),
|
|
||||||
new_style.get_effects().transform.clone())
|
|
||||||
}
|
|
||||||
TransitionProperty::WordSpacing => {
|
|
||||||
AnimatedProperty::WordSpacing(old_style.get_inheritedtext().word_spacing.0,
|
|
||||||
new_style.get_inheritedtext().word_spacing.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let animated_property = match_transition!(
|
|
||||||
[BackgroundColor; get_background; background_color],
|
|
||||||
[BackgroundPosition; get_background; background_position],
|
|
||||||
[BorderBottomColor; get_border; border_bottom_color],
|
|
||||||
[BorderBottomWidth; get_border; border_bottom_width],
|
|
||||||
[BorderLeftColor; get_border; border_left_color],
|
|
||||||
[BorderLeftWidth; get_border; border_left_width],
|
|
||||||
[BorderRightColor; get_border; border_right_color],
|
|
||||||
[BorderRightWidth; get_border; border_right_width],
|
|
||||||
[BorderSpacing; get_inheritedtable; border_spacing],
|
|
||||||
[BorderTopColor; get_border; border_top_color],
|
|
||||||
[BorderTopWidth; get_border; border_top_width],
|
|
||||||
[Bottom; get_position; bottom],
|
|
||||||
[Color; get_color; color],
|
|
||||||
[FontSize; get_font; font_size],
|
|
||||||
[FontWeight; get_font; font_weight],
|
|
||||||
[Height; get_position; height],
|
|
||||||
[Left; get_position; left],
|
|
||||||
[LineHeight; get_inheritedtext; line_height],
|
|
||||||
[MarginBottom; get_margin; margin_bottom],
|
|
||||||
[MarginLeft; get_margin; margin_left],
|
|
||||||
[MarginRight; get_margin; margin_right],
|
|
||||||
[MarginTop; get_margin; margin_top],
|
|
||||||
[MaxHeight; get_position; max_height],
|
|
||||||
[MaxWidth; get_position; max_width],
|
|
||||||
[MinHeight; get_position; min_height],
|
|
||||||
[MinWidth; get_position; min_width],
|
|
||||||
[Opacity; get_effects; opacity],
|
|
||||||
[OutlineColor; get_outline; outline_color],
|
|
||||||
[OutlineWidth; get_outline; outline_width],
|
|
||||||
[PaddingBottom; get_padding; padding_bottom],
|
|
||||||
[PaddingLeft; get_padding; padding_left],
|
|
||||||
[PaddingRight; get_padding; padding_right],
|
|
||||||
[PaddingTop; get_padding; padding_top],
|
|
||||||
[Right; get_position; right],
|
|
||||||
[TextIndent; get_inheritedtext; text_indent],
|
|
||||||
[Top; get_position; top],
|
|
||||||
[VerticalAlign; get_box; vertical_align],
|
|
||||||
[Visibility; get_inheritedbox; visibility],
|
|
||||||
[Width; get_position; width],
|
|
||||||
[ZIndex; get_position; z_index]);
|
|
||||||
|
|
||||||
let property_animation = PropertyAnimation {
|
let property_animation = PropertyAnimation {
|
||||||
property: animated_property,
|
property: animated_property,
|
||||||
timing_function:
|
timing_function: timing_function,
|
||||||
*box_style.transition_timing_function.0.get_mod(transition_index),
|
duration: duration,
|
||||||
duration: *box_style.transition_duration.0.get_mod(transition_index),
|
|
||||||
};
|
};
|
||||||
if property_animation.does_not_animate() {
|
|
||||||
None
|
if property_animation.does_animate() {
|
||||||
} else {
|
|
||||||
Some(property_animation)
|
Some(property_animation)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,725 +127,12 @@ impl PropertyAnimation {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! match_property(
|
self.property.update(style, progress);
|
||||||
( $( [$name:ident; $structname:ident; $field:ident] ),* ) => {
|
|
||||||
match self.property {
|
|
||||||
$(
|
|
||||||
AnimatedProperty::$name(ref start, ref end) => {
|
|
||||||
if let Some(value) = start.interpolate(end, progress) {
|
|
||||||
style.$structname().$field = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
AnimatedProperty::Clip(ref start, ref end) => {
|
|
||||||
if let Some(value) = start.interpolate(end, progress) {
|
|
||||||
style.mutate_effects().clip.0 = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AnimatedProperty::LetterSpacing(ref start, ref end) => {
|
|
||||||
if let Some(value) = start.interpolate(end, progress) {
|
|
||||||
style.mutate_inheritedtext().letter_spacing.0 = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AnimatedProperty::WordSpacing(ref start, ref end) => {
|
|
||||||
if let Some(value) = start.interpolate(end, progress) {
|
|
||||||
style.mutate_inheritedtext().word_spacing.0 = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
match_property!(
|
|
||||||
[BackgroundColor; mutate_background; background_color],
|
|
||||||
[BackgroundPosition; mutate_background; background_position],
|
|
||||||
[BorderBottomColor; mutate_border; border_bottom_color],
|
|
||||||
[BorderBottomWidth; mutate_border; border_bottom_width],
|
|
||||||
[BorderLeftColor; mutate_border; border_left_color],
|
|
||||||
[BorderLeftWidth; mutate_border; border_left_width],
|
|
||||||
[BorderRightColor; mutate_border; border_right_color],
|
|
||||||
[BorderRightWidth; mutate_border; border_right_width],
|
|
||||||
[BorderSpacing; mutate_inheritedtable; border_spacing],
|
|
||||||
[BorderTopColor; mutate_border; border_top_color],
|
|
||||||
[BorderTopWidth; mutate_border; border_top_width],
|
|
||||||
[Bottom; mutate_position; bottom],
|
|
||||||
[Color; mutate_color; color],
|
|
||||||
[FontSize; mutate_font; font_size],
|
|
||||||
[FontWeight; mutate_font; font_weight],
|
|
||||||
[Height; mutate_position; height],
|
|
||||||
[Left; mutate_position; left],
|
|
||||||
[LineHeight; mutate_inheritedtext; line_height],
|
|
||||||
[MarginBottom; mutate_margin; margin_bottom],
|
|
||||||
[MarginLeft; mutate_margin; margin_left],
|
|
||||||
[MarginRight; mutate_margin; margin_right],
|
|
||||||
[MarginTop; mutate_margin; margin_top],
|
|
||||||
[MaxHeight; mutate_position; max_height],
|
|
||||||
[MaxWidth; mutate_position; max_width],
|
|
||||||
[MinHeight; mutate_position; min_height],
|
|
||||||
[MinWidth; mutate_position; min_width],
|
|
||||||
[Opacity; mutate_effects; opacity],
|
|
||||||
[OutlineColor; mutate_outline; outline_color],
|
|
||||||
[OutlineWidth; mutate_outline; outline_width],
|
|
||||||
[PaddingBottom; mutate_padding; padding_bottom],
|
|
||||||
[PaddingLeft; mutate_padding; padding_left],
|
|
||||||
[PaddingRight; mutate_padding; padding_right],
|
|
||||||
[PaddingTop; mutate_padding; padding_top],
|
|
||||||
[Right; mutate_position; right],
|
|
||||||
[TextIndent; mutate_inheritedtext; text_indent],
|
|
||||||
[TextShadow; mutate_inheritedtext; text_shadow],
|
|
||||||
[Top; mutate_position; top],
|
|
||||||
[Transform; mutate_effects; transform],
|
|
||||||
[VerticalAlign; mutate_box; vertical_align],
|
|
||||||
[Visibility; mutate_inheritedbox; visibility],
|
|
||||||
[Width; mutate_position; width],
|
|
||||||
[ZIndex; mutate_position; z_index]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn does_not_animate(&self) -> bool {
|
fn does_animate(&self) -> bool {
|
||||||
self.property.does_not_animate() || self.duration == Time(0.0)
|
self.property.does_animate() && self.duration != Time(0.0)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
enum AnimatedProperty {
|
|
||||||
BackgroundColor(Color, Color),
|
|
||||||
BackgroundPosition(BackgroundPosition, BackgroundPosition),
|
|
||||||
BorderBottomColor(Color, Color),
|
|
||||||
BorderBottomWidth(Length, Length),
|
|
||||||
BorderLeftColor(Color, Color),
|
|
||||||
BorderLeftWidth(Length, Length),
|
|
||||||
BorderRightColor(Color, Color),
|
|
||||||
BorderRightWidth(Length, Length),
|
|
||||||
BorderSpacing(BorderSpacing, BorderSpacing),
|
|
||||||
BorderTopColor(Color, Color),
|
|
||||||
BorderTopWidth(Length, Length),
|
|
||||||
Bottom(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
|
|
||||||
Color(RGBA, RGBA),
|
|
||||||
Clip(Option<ClipRect>, Option<ClipRect>),
|
|
||||||
FontSize(Length, Length),
|
|
||||||
FontWeight(FontWeight, FontWeight),
|
|
||||||
Height(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
|
|
||||||
Left(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
|
|
||||||
LetterSpacing(Option<Au>, Option<Au>),
|
|
||||||
LineHeight(LineHeight, LineHeight),
|
|
||||||
MarginBottom(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
|
|
||||||
MarginLeft(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
|
|
||||||
MarginRight(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
|
|
||||||
MarginTop(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
|
|
||||||
MaxHeight(LengthOrPercentageOrNone, LengthOrPercentageOrNone),
|
|
||||||
MaxWidth(LengthOrPercentageOrNone, LengthOrPercentageOrNone),
|
|
||||||
MinHeight(LengthOrPercentage, LengthOrPercentage),
|
|
||||||
MinWidth(LengthOrPercentage, LengthOrPercentage),
|
|
||||||
Opacity(CSSFloat, CSSFloat),
|
|
||||||
OutlineColor(Color, Color),
|
|
||||||
OutlineWidth(Length, Length),
|
|
||||||
PaddingBottom(LengthOrPercentage, LengthOrPercentage),
|
|
||||||
PaddingLeft(LengthOrPercentage, LengthOrPercentage),
|
|
||||||
PaddingRight(LengthOrPercentage, LengthOrPercentage),
|
|
||||||
PaddingTop(LengthOrPercentage, LengthOrPercentage),
|
|
||||||
Right(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
|
|
||||||
TextIndent(LengthOrPercentage, LengthOrPercentage),
|
|
||||||
TextShadow(TextShadowList, TextShadowList),
|
|
||||||
Top(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
|
|
||||||
Transform(TransformList, TransformList),
|
|
||||||
VerticalAlign(VerticalAlign, VerticalAlign),
|
|
||||||
Visibility(Visibility, Visibility),
|
|
||||||
Width(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
|
|
||||||
WordSpacing(Option<Au>, Option<Au>),
|
|
||||||
ZIndex(ZIndex, ZIndex),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AnimatedProperty {
|
|
||||||
#[inline]
|
|
||||||
fn does_not_animate(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
AnimatedProperty::Top(ref a, ref b) |
|
|
||||||
AnimatedProperty::Right(ref a, ref b) |
|
|
||||||
AnimatedProperty::Bottom(ref a, ref b) |
|
|
||||||
AnimatedProperty::Left(ref a, ref b) |
|
|
||||||
AnimatedProperty::MarginTop(ref a, ref b) |
|
|
||||||
AnimatedProperty::MarginRight(ref a, ref b) |
|
|
||||||
AnimatedProperty::MarginBottom(ref a, ref b) |
|
|
||||||
AnimatedProperty::MarginLeft(ref a, ref b) |
|
|
||||||
AnimatedProperty::Width(ref a, ref b) |
|
|
||||||
AnimatedProperty::Height(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::MaxWidth(ref a, ref b) |
|
|
||||||
AnimatedProperty::MaxHeight(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::MinWidth(ref a, ref b) |
|
|
||||||
AnimatedProperty::MinHeight(ref a, ref b) |
|
|
||||||
AnimatedProperty::TextIndent(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::FontSize(ref a, ref b) |
|
|
||||||
AnimatedProperty::BorderTopWidth(ref a, ref b) |
|
|
||||||
AnimatedProperty::BorderRightWidth(ref a, ref b) |
|
|
||||||
AnimatedProperty::BorderBottomWidth(ref a, ref b) |
|
|
||||||
AnimatedProperty::BorderLeftWidth(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::BorderTopColor(ref a, ref b) |
|
|
||||||
AnimatedProperty::BorderRightColor(ref a, ref b) |
|
|
||||||
AnimatedProperty::BorderBottomColor(ref a, ref b) |
|
|
||||||
AnimatedProperty::BorderLeftColor(ref a, ref b) |
|
|
||||||
AnimatedProperty::OutlineColor(ref a, ref b) |
|
|
||||||
AnimatedProperty::BackgroundColor(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::PaddingTop(ref a, ref b) |
|
|
||||||
AnimatedProperty::PaddingRight(ref a, ref b) |
|
|
||||||
AnimatedProperty::PaddingBottom(ref a, ref b) |
|
|
||||||
AnimatedProperty::PaddingLeft(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::LineHeight(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::LetterSpacing(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::BackgroundPosition(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::BorderSpacing(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::Clip(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::Color(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::FontWeight(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::Opacity(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::OutlineWidth(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::TextShadow(ref a, ref b) => a == b,
|
|
||||||
AnimatedProperty::VerticalAlign(ref a, ref b) => a == b,
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait used to implement [interpolation][interpolated-types].
|
|
||||||
///
|
|
||||||
/// [interpolated-types]: https://drafts.csswg.org/css-transitions/#interpolated-types
|
|
||||||
trait Interpolate: Sized {
|
|
||||||
fn interpolate(&self, other: &Self, time: f64) -> Option<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interpolate for Au {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &Au, time: f64) -> Option<Au> {
|
|
||||||
Some(Au((self.0 as f64 + (other.0 as f64 - self.0 as f64) * time).round() as i32))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <T> Interpolate for Option<T> where T: Interpolate {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &Option<T>, time: f64) -> Option<Option<T>> {
|
|
||||||
match (self, other) {
|
|
||||||
(&Some(ref this), &Some(ref other)) => {
|
|
||||||
this.interpolate(other, time).and_then(|value| {
|
|
||||||
Some(Some(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(_, _) => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-number
|
|
||||||
impl Interpolate for f32 {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &f32, time: f64) -> Option<f32> {
|
|
||||||
Some(((*self as f64) + ((*other as f64) - (*self as f64)) * time) as f32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-number
|
|
||||||
impl Interpolate for f64 {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &f64, time: f64) -> Option<f64> {
|
|
||||||
Some(*self + (*other - *self) * time)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-integer
|
|
||||||
impl Interpolate for i32 {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &i32, time: f64) -> Option<i32> {
|
|
||||||
let a = *self as f64;
|
|
||||||
let b = *other as f64;
|
|
||||||
Some((a + (b - a) * time).round() as i32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interpolate for Angle {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &Angle, time: f64) -> Option<Angle> {
|
|
||||||
self.radians().interpolate(&other.radians(), time).map(Angle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-visibility
|
|
||||||
impl Interpolate for Visibility {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &Visibility, time: f64)
|
|
||||||
-> Option<Visibility> {
|
|
||||||
match (*self, *other) {
|
|
||||||
(Visibility::visible, _) | (_, Visibility::visible) => {
|
|
||||||
if time >= 0.0 && time <= 1.0 {
|
|
||||||
Some(Visibility::visible)
|
|
||||||
} else if time < 0.0 {
|
|
||||||
Some(*self)
|
|
||||||
} else {
|
|
||||||
Some(*other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(_, _) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-integer
|
|
||||||
impl Interpolate for ZIndex {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &ZIndex, time: f64)
|
|
||||||
-> Option<ZIndex> {
|
|
||||||
match (*self, *other) {
|
|
||||||
(ZIndex::Number(ref this),
|
|
||||||
ZIndex::Number(ref other)) => {
|
|
||||||
this.interpolate(other, time).and_then(|value| {
|
|
||||||
Some(ZIndex::Number(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(_, _) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-length
|
|
||||||
impl Interpolate for VerticalAlign {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &VerticalAlign, time: f64)
|
|
||||||
-> Option<VerticalAlign> {
|
|
||||||
match (*self, *other) {
|
|
||||||
(VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref this)),
|
|
||||||
VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref other))) => {
|
|
||||||
this.interpolate(other, time).and_then(|value| {
|
|
||||||
Some(VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(value)))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(_, _) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-simple-list
|
|
||||||
impl Interpolate for BorderSpacing {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &BorderSpacing, time: f64)
|
|
||||||
-> Option<BorderSpacing> {
|
|
||||||
self.horizontal.interpolate(&other.horizontal, time).and_then(|horizontal| {
|
|
||||||
self.vertical.interpolate(&other.vertical, time).and_then(|vertical| {
|
|
||||||
Some(BorderSpacing { horizontal: horizontal, vertical: vertical })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-color
|
|
||||||
impl Interpolate for RGBA {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &RGBA, time: f64) -> Option<RGBA> {
|
|
||||||
match (self.red.interpolate(&other.red, time),
|
|
||||||
self.green.interpolate(&other.green, time),
|
|
||||||
self.blue.interpolate(&other.blue, time),
|
|
||||||
self.alpha.interpolate(&other.alpha, time)) {
|
|
||||||
(Some(red), Some(green), Some(blue), Some(alpha)) => {
|
|
||||||
Some(RGBA { red: red, green: green, blue: blue, alpha: alpha })
|
|
||||||
}
|
|
||||||
(_, _, _, _) => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-color
|
|
||||||
impl Interpolate for Color {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &Color, time: f64) -> Option<Color> {
|
|
||||||
match (*self, *other) {
|
|
||||||
(Color::RGBA(ref this), Color::RGBA(ref other)) => {
|
|
||||||
this.interpolate(other, time).and_then(|value| {
|
|
||||||
Some(Color::RGBA(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(_, _) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
|
||||||
impl Interpolate for CalcLengthOrPercentage {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &CalcLengthOrPercentage, time: f64)
|
|
||||||
-> Option<CalcLengthOrPercentage> {
|
|
||||||
Some(CalcLengthOrPercentage {
|
|
||||||
length: self.length().interpolate(&other.length(), time),
|
|
||||||
percentage: self.percentage().interpolate(&other.percentage(), time),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
|
||||||
impl Interpolate for LengthOrPercentage {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &LengthOrPercentage, time: f64)
|
|
||||||
-> Option<LengthOrPercentage> {
|
|
||||||
match (*self, *other) {
|
|
||||||
(LengthOrPercentage::Length(ref this),
|
|
||||||
LengthOrPercentage::Length(ref other)) => {
|
|
||||||
this.interpolate(other, time).and_then(|value| {
|
|
||||||
Some(LengthOrPercentage::Length(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(LengthOrPercentage::Percentage(ref this),
|
|
||||||
LengthOrPercentage::Percentage(ref other)) => {
|
|
||||||
this.interpolate(other, time).and_then(|value| {
|
|
||||||
Some(LengthOrPercentage::Percentage(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(this, other) => {
|
|
||||||
let this: CalcLengthOrPercentage = From::from(this);
|
|
||||||
let other: CalcLengthOrPercentage = From::from(other);
|
|
||||||
this.interpolate(&other, time).and_then(|value| {
|
|
||||||
Some(LengthOrPercentage::Calc(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
|
||||||
impl Interpolate for LengthOrPercentageOrAuto {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &LengthOrPercentageOrAuto, time: f64)
|
|
||||||
-> Option<LengthOrPercentageOrAuto> {
|
|
||||||
match (*self, *other) {
|
|
||||||
(LengthOrPercentageOrAuto::Length(ref this),
|
|
||||||
LengthOrPercentageOrAuto::Length(ref other)) => {
|
|
||||||
this.interpolate(other, time).and_then(|value| {
|
|
||||||
Some(LengthOrPercentageOrAuto::Length(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(LengthOrPercentageOrAuto::Percentage(ref this),
|
|
||||||
LengthOrPercentageOrAuto::Percentage(ref other)) => {
|
|
||||||
this.interpolate(other, time).and_then(|value| {
|
|
||||||
Some(LengthOrPercentageOrAuto::Percentage(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(LengthOrPercentageOrAuto::Auto, LengthOrPercentageOrAuto::Auto) => {
|
|
||||||
Some(LengthOrPercentageOrAuto::Auto)
|
|
||||||
}
|
|
||||||
(this, other) => {
|
|
||||||
let this: Option<CalcLengthOrPercentage> = From::from(this);
|
|
||||||
let other: Option<CalcLengthOrPercentage> = From::from(other);
|
|
||||||
this.interpolate(&other, time).unwrap_or(None).and_then(|value| {
|
|
||||||
Some(LengthOrPercentageOrAuto::Calc(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
|
||||||
impl Interpolate for LengthOrPercentageOrNone {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &LengthOrPercentageOrNone, time: f64)
|
|
||||||
-> Option<LengthOrPercentageOrNone> {
|
|
||||||
match (*self, *other) {
|
|
||||||
(LengthOrPercentageOrNone::Length(ref this),
|
|
||||||
LengthOrPercentageOrNone::Length(ref other)) => {
|
|
||||||
this.interpolate(other, time).and_then(|value| {
|
|
||||||
Some(LengthOrPercentageOrNone::Length(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(LengthOrPercentageOrNone::Percentage(ref this),
|
|
||||||
LengthOrPercentageOrNone::Percentage(ref other)) => {
|
|
||||||
this.interpolate(other, time).and_then(|value| {
|
|
||||||
Some(LengthOrPercentageOrNone::Percentage(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(LengthOrPercentageOrNone::None, LengthOrPercentageOrNone::None) => {
|
|
||||||
Some(LengthOrPercentageOrNone::None)
|
|
||||||
}
|
|
||||||
(_, _) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-number
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-length
|
|
||||||
impl Interpolate for LineHeight {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &LineHeight, time: f64)
|
|
||||||
-> Option<LineHeight> {
|
|
||||||
match (*self, *other) {
|
|
||||||
(LineHeight::Length(ref this),
|
|
||||||
LineHeight::Length(ref other)) => {
|
|
||||||
this.interpolate(other, time).and_then(|value| {
|
|
||||||
Some(LineHeight::Length(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(LineHeight::Number(ref this),
|
|
||||||
LineHeight::Number(ref other)) => {
|
|
||||||
this.interpolate(other, time).and_then(|value| {
|
|
||||||
Some(LineHeight::Number(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(LineHeight::Normal, LineHeight::Normal) => {
|
|
||||||
Some(LineHeight::Normal)
|
|
||||||
}
|
|
||||||
(_, _) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-font-weight
|
|
||||||
impl Interpolate for FontWeight {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &FontWeight, time: f64)
|
|
||||||
-> Option<FontWeight> {
|
|
||||||
let a = (*self as u32) as f64;
|
|
||||||
let b = (*other as u32) as f64;
|
|
||||||
let weight = a + (b - a) * time;
|
|
||||||
Some(if weight < 150. {
|
|
||||||
FontWeight::Weight100
|
|
||||||
} else if weight < 250. {
|
|
||||||
FontWeight::Weight200
|
|
||||||
} else if weight < 350. {
|
|
||||||
FontWeight::Weight300
|
|
||||||
} else if weight < 450. {
|
|
||||||
FontWeight::Weight400
|
|
||||||
} else if weight < 550. {
|
|
||||||
FontWeight::Weight500
|
|
||||||
} else if weight < 650. {
|
|
||||||
FontWeight::Weight600
|
|
||||||
} else if weight < 750. {
|
|
||||||
FontWeight::Weight700
|
|
||||||
} else if weight < 850. {
|
|
||||||
FontWeight::Weight800
|
|
||||||
} else {
|
|
||||||
FontWeight::Weight900
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-rect
|
|
||||||
impl Interpolate for ClipRect {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &ClipRect, time: f64)
|
|
||||||
-> Option<ClipRect> {
|
|
||||||
match (self.top.interpolate(&other.top, time),
|
|
||||||
self.right.interpolate(&other.right, time),
|
|
||||||
self.bottom.interpolate(&other.bottom, time),
|
|
||||||
self.left.interpolate(&other.left, time)) {
|
|
||||||
(Some(top), Some(right), Some(bottom), Some(left)) => {
|
|
||||||
Some(ClipRect { top: top, right: right, bottom: bottom, left: left })
|
|
||||||
},
|
|
||||||
(_, _, _, _) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-simple-list
|
|
||||||
impl Interpolate for BackgroundPosition {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &BackgroundPosition, time: f64)
|
|
||||||
-> Option<BackgroundPosition> {
|
|
||||||
match (self.horizontal.interpolate(&other.horizontal, time),
|
|
||||||
self.vertical.interpolate(&other.vertical, time)) {
|
|
||||||
(Some(horizontal), Some(vertical)) => {
|
|
||||||
Some(BackgroundPosition { horizontal: horizontal, vertical: vertical })
|
|
||||||
},
|
|
||||||
(_, _) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
|
|
||||||
impl Interpolate for TextShadow {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &TextShadow, time: f64)
|
|
||||||
-> Option<TextShadow> {
|
|
||||||
match (self.offset_x.interpolate(&other.offset_x, time),
|
|
||||||
self.offset_y.interpolate(&other.offset_y, time),
|
|
||||||
self.blur_radius.interpolate(&other.blur_radius, time),
|
|
||||||
self.color.interpolate(&other.color, time)) {
|
|
||||||
(Some(offset_x), Some(offset_y), Some(blur_radius), Some(color)) => {
|
|
||||||
Some(TextShadow { offset_x: offset_x, offset_y: offset_y, blur_radius: blur_radius, color: color })
|
|
||||||
},
|
|
||||||
(_, _, _, _) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
|
|
||||||
impl Interpolate for TextShadowList {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &TextShadowList, time: f64)
|
|
||||||
-> Option<TextShadowList> {
|
|
||||||
let zero = TextShadow {
|
|
||||||
offset_x: Au(0),
|
|
||||||
offset_y: Au(0),
|
|
||||||
blur_radius: Au(0),
|
|
||||||
color: Color::RGBA(RGBA {
|
|
||||||
red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let interpolate_each = |(a, b): (&TextShadow, &TextShadow)| {
|
|
||||||
a.interpolate(b, time).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(TextShadowList(match self.0.len().cmp(&other.0.len()) {
|
|
||||||
Ordering::Less => other.0.iter().chain(repeat(&zero)).zip(other.0.iter()).map(interpolate_each).collect(),
|
|
||||||
_ => self.0.iter().zip(other.0.iter().chain(repeat(&zero))).map(interpolate_each).collect(),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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: &[TransformOperation],
|
|
||||||
to_list: &[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) {
|
|
||||||
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.
|
|
||||||
/// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms
|
|
||||||
fn interpolate_transform_list(from_list: &[TransformOperation],
|
|
||||||
to_list: &[TransformOperation],
|
|
||||||
time: f64) -> TransformList {
|
|
||||||
let mut result = vec![];
|
|
||||||
|
|
||||||
if can_interpolate_list(from_list, to_list) {
|
|
||||||
for (from, to) in from_list.iter().zip(to_list) {
|
|
||||||
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.extend_from_slice(from_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
TransformList(Some(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build an equivalent 'identity transform function list' based
|
|
||||||
/// on an existing transform list.
|
|
||||||
/// https://drafts.csswg.org/css-transforms/#none-transform-animation
|
|
||||||
fn build_identity_transform_list(list: &[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(Angle(0.0), Angle(0.0)));
|
|
||||||
}
|
|
||||||
TransformOperation::Translate(..) => {
|
|
||||||
result.push(TransformOperation::Translate(LengthOrPercentage::zero(),
|
|
||||||
LengthOrPercentage::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
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms
|
|
||||||
impl Interpolate for TransformList {
|
|
||||||
#[inline]
|
|
||||||
fn interpolate(&self, other: &TransformList, time: f64) -> Option<TransformList> {
|
|
||||||
let result = match (&self.0, &other.0) {
|
|
||||||
(&Some(ref from_list), &Some(ref to_list)) => {
|
|
||||||
// https://drafts.csswg.org/css-transforms/#transform-transform-animation
|
|
||||||
interpolate_transform_list(from_list, &to_list, time)
|
|
||||||
}
|
|
||||||
(&Some(ref from_list), &None) => {
|
|
||||||
// https://drafts.csswg.org/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)) => {
|
|
||||||
// https://drafts.csswg.org/css-transforms/#none-transform-animation
|
|
||||||
let from_list = build_identity_transform_list(to_list);
|
|
||||||
interpolate_transform_list(&from_list, to_list, time)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// https://drafts.csswg.org/css-transforms/#none-none-animation
|
|
||||||
TransformList(None)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -948,6 +147,7 @@ pub trait GetMod {
|
||||||
|
|
||||||
impl<T> GetMod for Vec<T> {
|
impl<T> GetMod for Vec<T> {
|
||||||
type Item = T;
|
type Item = T;
|
||||||
|
#[inline]
|
||||||
fn get_mod(&self, i: usize) -> &T {
|
fn get_mod(&self, i: usize) -> &T {
|
||||||
&(*self)[i % self.len()]
|
&(*self)[i % self.len()]
|
||||||
}
|
}
|
||||||
|
@ -991,10 +191,9 @@ pub fn start_transitions_if_applicable<C: ComputedValues>(new_animations_sender:
|
||||||
|
|
||||||
/// Updates a single animation and associated style based on the current time. If `damage` is
|
/// Updates a single animation and associated style based on the current time. If `damage` is
|
||||||
/// provided, inserts the appropriate restyle damage.
|
/// provided, inserts the appropriate restyle damage.
|
||||||
pub fn update_style_for_animation<C: ComputedValues,
|
pub fn update_style_for_animation<Damage: TRestyleDamage>(animation: &Animation,
|
||||||
Damage: TRestyleDamage<ConcreteComputedValues=C>>(animation: &Animation,
|
style: &mut Arc<Damage::ConcreteComputedValues>,
|
||||||
style: &mut Arc<C>,
|
damage: Option<&mut Damage>) {
|
||||||
damage: Option<&mut Damage>) {
|
|
||||||
let now = time::precise_time_s();
|
let now = time::precise_time_s();
|
||||||
let mut progress = (now - animation.start_time) / animation.duration();
|
let mut progress = (now - animation.start_time) / animation.duration();
|
||||||
if progress > 1.0 {
|
if progress > 1.0 {
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
|
|
||||||
use cssparser::{Parser, Delimiter};
|
use cssparser::{Parser, Delimiter};
|
||||||
use parser::ParserContext;
|
use parser::ParserContext;
|
||||||
use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
|
use properties::{ComputedValues, PropertyDeclarationBlock, parse_property_declaration_list};
|
||||||
|
use properties::animated_properties::AnimatedProperty;
|
||||||
|
|
||||||
/// Parses a keyframes list, like:
|
/// Parses a keyframes list, like:
|
||||||
/// 0%, 50% {
|
/// 0%, 50% {
|
||||||
|
@ -19,6 +20,11 @@ pub fn parse_keyframe_list(context: &ParserContext, input: &mut Parser) -> Resul
|
||||||
while !input.is_exhausted() {
|
while !input.is_exhausted() {
|
||||||
keyframes.push(try!(Keyframe::parse(context, input)));
|
keyframes.push(try!(Keyframe::parse(context, input)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if keyframes.len() < 2 {
|
||||||
|
return Err(())
|
||||||
|
}
|
||||||
|
|
||||||
Ok(keyframes)
|
Ok(keyframes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,3 +95,60 @@ impl Keyframe {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A single step from a keyframe animation.
|
||||||
|
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||||||
|
pub struct ComputedKeyframesStep<C: ComputedValues> {
|
||||||
|
/// The percentage of the animation duration that should be taken for this
|
||||||
|
/// step.
|
||||||
|
duration_percentage: KeyframePercentage,
|
||||||
|
// XXX: Can we optimise this? Probably not such a show-stopper... Probably
|
||||||
|
// storing declared values could work/should be the thing to do?
|
||||||
|
/// The computed values at the beginning of the step.
|
||||||
|
begin: C,
|
||||||
|
/// The computed values at the end of the step.
|
||||||
|
end: C,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This structure represents a list of animation steps computed from the list
|
||||||
|
/// of keyframes, in order.
|
||||||
|
///
|
||||||
|
/// It only takes into account animable properties.
|
||||||
|
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||||||
|
pub struct ComputedKeyframesAnimation<C: ComputedValues> {
|
||||||
|
steps: Vec<ComputedKeyframesStep<C>>,
|
||||||
|
/// The properties that change in this animation.
|
||||||
|
properties_changed: Vec<AnimatedProperty>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_animated_properties(keyframe: &Keyframe) -> Vec<AnimatedProperty> {
|
||||||
|
// TODO
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ComputedValues> ComputedKeyframesAnimation<C> {
|
||||||
|
pub fn from_keyframes(keyframes: &[Keyframe]) -> Option<ComputedKeyframesAnimation<C>> {
|
||||||
|
debug_assert!(keyframes.len() > 1);
|
||||||
|
let mut steps = vec![];
|
||||||
|
|
||||||
|
// NB: we do two passes, first storing the steps in the order of
|
||||||
|
// appeareance, then sorting them, then updating with the real
|
||||||
|
// "duration_percentage".
|
||||||
|
let mut animated_properties = get_animated_properties(&keyframes[0]);
|
||||||
|
|
||||||
|
if animated_properties.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
for keyframe in keyframes {
|
||||||
|
for step in keyframe.selector.0.iter() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(ComputedKeyframesAnimation {
|
||||||
|
steps: steps,
|
||||||
|
properties_changed: animated_properties,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -400,7 +400,6 @@ trait PrivateMatchMethods: TNode
|
||||||
shareable,
|
shareable,
|
||||||
Some(&***parent_style),
|
Some(&***parent_style),
|
||||||
cached_computed_values,
|
cached_computed_values,
|
||||||
animations,
|
|
||||||
context.error_reporter.clone());
|
context.error_reporter.clone());
|
||||||
cacheable = cacheable && is_cacheable;
|
cacheable = cacheable && is_cacheable;
|
||||||
this_style = the_style
|
this_style = the_style
|
||||||
|
@ -411,7 +410,6 @@ trait PrivateMatchMethods: TNode
|
||||||
shareable,
|
shareable,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
animations,
|
|
||||||
context.error_reporter.clone());
|
context.error_reporter.clone());
|
||||||
cacheable = cacheable && is_cacheable;
|
cacheable = cacheable && is_cacheable;
|
||||||
this_style = the_style
|
this_style = the_style
|
||||||
|
@ -482,8 +480,7 @@ trait PrivateMatchMethods: TNode
|
||||||
if had_running_animations {
|
if had_running_animations {
|
||||||
let mut all_running_animations = context.running_animations.write().unwrap();
|
let mut all_running_animations = context.running_animations.write().unwrap();
|
||||||
for running_animation in all_running_animations.get(&this_opaque).unwrap() {
|
for running_animation in all_running_animations.get(&this_opaque).unwrap() {
|
||||||
animation::update_style_for_animation::<Self::ConcreteComputedValues,
|
animation::update_style_for_animation::<Self::ConcreteRestyleDamage>(running_animation, style, None);
|
||||||
Self::ConcreteRestyleDamage>(running_animation, style, None);
|
|
||||||
}
|
}
|
||||||
all_running_animations.remove(&this_opaque);
|
all_running_animations.remove(&this_opaque);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,53 @@ def to_camel_case(ident):
|
||||||
return re.sub("(^|_|-)([a-z])", lambda m: m.group(2).upper(), ident.strip("_").strip("-"))
|
return re.sub("(^|_|-)([a-z])", lambda m: m.group(2).upper(), ident.strip("_").strip("-"))
|
||||||
|
|
||||||
|
|
||||||
|
# https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties
|
||||||
|
def is_known_animatable_property(name):
|
||||||
|
return name in [
|
||||||
|
"-moz-outline-radius", "-moz-outline-radius-bottomleft",
|
||||||
|
"-moz-outline-radius-bottomright", "-moz-outline-radius-topleft",
|
||||||
|
"-moz-outline-radius-topright", "-webkit-text-fill-color",
|
||||||
|
"-webkit-text-stroke", "-webkit-text-stroke-color",
|
||||||
|
"-webkit-touch-callout", "all", "backdrop-filter", "background",
|
||||||
|
"background-color", "background-position", "background-size", "border",
|
||||||
|
"border-bottom", "border-bottom-color", "border-bottom-left-radius",
|
||||||
|
"border-bottom-right-radius", "border-bottom-width", "border-color",
|
||||||
|
"border-left", "border-left-color", "border-left-width", "border-radius",
|
||||||
|
"border-right", "border-right-color", "border-right-width", "border-top",
|
||||||
|
"border-top-color", "border-top-left-radius", "border-top-right-radius",
|
||||||
|
"border-top-width", "border-width", "bottom", "box-shadow", "clip",
|
||||||
|
"clip-path", "color", "column-count", "column-gap", "column-rule",
|
||||||
|
"column-rule-color", "column-rule-width", "column-width", "columns",
|
||||||
|
"filter", "flex", "flex-basis", "flex-grow", "flex-shrink", "font",
|
||||||
|
"font-size", "font-size-adjust", "font-stretch", "font-weight",
|
||||||
|
"grid-column-gap", "grid-gap", "grid-row-gap", "height", "left",
|
||||||
|
"letter-spacing", "line-height", "margin", "margin-bottom",
|
||||||
|
"margin-left", "margin-right", "margin-top", "mask", "mask-position",
|
||||||
|
"mask-size", "max-height", "max-width", "min-height", "min-width",
|
||||||
|
"motion-offset", "motion-rotation", "object-position", "opacity",
|
||||||
|
"order", "outline", "outline-color", "outline-offset", "outline-width",
|
||||||
|
"padding", "padding-bottom", "padding-left", "padding-right",
|
||||||
|
"padding-top", "perspective", "perspective-origin", "right",
|
||||||
|
"scroll-snap-coordinate", "scroll-snap-destination",
|
||||||
|
"shape-image-threshold", "shape-margin", "shape-outside",
|
||||||
|
"text-decoration", "text-decoration-color", "text-emphasis",
|
||||||
|
"text-emphasis-color", "text-indent", "text-shadow", "top", "transform",
|
||||||
|
"transform-origin", "vertical-align", "visibility", "width",
|
||||||
|
"word-spacing", "z-index"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME: Servo doesn't support some animatable properties yet,those are in the
|
||||||
|
# following list, and can be implemented removing it from the list and
|
||||||
|
# implementing the Interpolate trait in helpers/animated_properties.mako.rs
|
||||||
|
def is_not_supported_animatable_property(name):
|
||||||
|
return name in [
|
||||||
|
"flex-basis", "column-width", "column-height", "column-count",
|
||||||
|
"column-gap", "box-shadow", "clip", "filter", "transform-origin",
|
||||||
|
"perspective-origin", "font-stretch", "letter-spacing", "word-spacing",
|
||||||
|
"text-decoration" ]
|
||||||
|
|
||||||
|
|
||||||
class Keyword(object):
|
class Keyword(object):
|
||||||
def __init__(self, name, values, gecko_constant_prefix=None,
|
def __init__(self, name, values, gecko_constant_prefix=None,
|
||||||
extra_gecko_values=None, extra_servo_values=None):
|
extra_gecko_values=None, extra_servo_values=None):
|
||||||
|
@ -47,7 +94,7 @@ class Keyword(object):
|
||||||
class Longhand(object):
|
class Longhand(object):
|
||||||
def __init__(self, style_struct, name, derived_from=None, keyword=None,
|
def __init__(self, style_struct, name, derived_from=None, keyword=None,
|
||||||
predefined_type=None, custom_cascade=False, experimental=False, internal=False,
|
predefined_type=None, custom_cascade=False, experimental=False, internal=False,
|
||||||
need_clone=False, gecko_ffi_name=None):
|
need_clone=False, gecko_ffi_name=None, animatable=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.keyword = keyword
|
self.keyword = keyword
|
||||||
self.predefined_type = predefined_type
|
self.predefined_type = predefined_type
|
||||||
|
@ -60,6 +107,10 @@ class Longhand(object):
|
||||||
self.need_clone = need_clone
|
self.need_clone = need_clone
|
||||||
self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
|
self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
|
||||||
self.derived_from = (derived_from or "").split()
|
self.derived_from = (derived_from or "").split()
|
||||||
|
if animatable is not None:
|
||||||
|
self.animatable = animatable
|
||||||
|
else:
|
||||||
|
self.animatable = is_known_animatable_property(name) and not is_not_supported_animatable_property(name)
|
||||||
|
|
||||||
|
|
||||||
class Shorthand(object):
|
class Shorthand(object):
|
||||||
|
|
728
components/style/properties/helpers/animated_properties.mako.rs
Normal file
728
components/style/properties/helpers/animated_properties.mako.rs
Normal file
|
@ -0,0 +1,728 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use app_units::Au;
|
||||||
|
use bezier::Bezier;
|
||||||
|
use cssparser::{Color as CSSParserColor, Parser, RGBA, ToCss};
|
||||||
|
use dom::{OpaqueNode, TRestyleDamage};
|
||||||
|
use euclid::{Point2D, Size2D};
|
||||||
|
use properties::longhands;
|
||||||
|
use properties::longhands::background_position::computed_value::T as BackgroundPosition;
|
||||||
|
use properties::longhands::background_size::computed_value::T as BackgroundSize;
|
||||||
|
use properties::longhands::border_spacing::computed_value::T as BorderSpacing;
|
||||||
|
use properties::longhands::clip::computed_value::ClipRect;
|
||||||
|
use properties::longhands::font_weight::computed_value::T as FontWeight;
|
||||||
|
use properties::longhands::line_height::computed_value::T as LineHeight;
|
||||||
|
use properties::longhands::text_shadow::computed_value::T as TextShadowList;
|
||||||
|
use properties::longhands::text_shadow::computed_value::TextShadow;
|
||||||
|
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 properties::longhands::transition_property;
|
||||||
|
use properties::longhands::transition_timing_function::computed_value::StartEnd;
|
||||||
|
use properties::longhands::transition_timing_function::computed_value::TransitionTimingFunction;
|
||||||
|
use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
|
||||||
|
use properties::longhands::visibility::computed_value::T as Visibility;
|
||||||
|
use properties::longhands::z_index::computed_value::T as ZIndex;
|
||||||
|
use properties::style_struct_traits::*;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::fmt;
|
||||||
|
use std::iter::repeat;
|
||||||
|
use super::ComputedValues;
|
||||||
|
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
|
||||||
|
use values::computed::{BorderRadiusSize, LengthOrNone};
|
||||||
|
use values::computed::{CalcLengthOrPercentage, Length, LengthOrPercentage, Time};
|
||||||
|
|
||||||
|
// NB: This needs to be here because it needs all the longhands generated
|
||||||
|
// beforehand.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf)]
|
||||||
|
pub enum TransitionProperty {
|
||||||
|
All,
|
||||||
|
% for prop in data.longhands:
|
||||||
|
% if prop.animatable:
|
||||||
|
${prop.camel_case},
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransitionProperty {
|
||||||
|
/// Iterates over each property that is not `All`.
|
||||||
|
pub fn each<F: FnMut(TransitionProperty) -> ()>(mut cb: F) {
|
||||||
|
% for prop in data.longhands:
|
||||||
|
% if prop.animatable:
|
||||||
|
cb(TransitionProperty::${prop.camel_case});
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
match_ignore_ascii_case! { try!(input.expect_ident()),
|
||||||
|
"all" => Ok(TransitionProperty::All),
|
||||||
|
% for prop in data.longhands:
|
||||||
|
% if prop.animatable:
|
||||||
|
"${prop.name}" => Ok(TransitionProperty::${prop.camel_case}),
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
_ => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for TransitionProperty {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
match *self {
|
||||||
|
TransitionProperty::All => dest.write_str("all"),
|
||||||
|
% for prop in data.longhands:
|
||||||
|
% if prop.animatable:
|
||||||
|
TransitionProperty::${prop.camel_case} => dest.write_str("${prop.name}"),
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, HeapSizeOf)]
|
||||||
|
pub enum AnimatedProperty {
|
||||||
|
% for prop in data.longhands:
|
||||||
|
% if prop.animatable:
|
||||||
|
${prop.camel_case}(longhands::${prop.ident}::computed_value::T,
|
||||||
|
longhands::${prop.ident}::computed_value::T),
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimatedProperty {
|
||||||
|
pub fn does_animate(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
% for prop in data.longhands:
|
||||||
|
% if prop.animatable:
|
||||||
|
AnimatedProperty::${prop.camel_case}(ref from, ref to) => from != to,
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update<C: ComputedValues>(&self, style: &mut C, progress: f64) {
|
||||||
|
match *self {
|
||||||
|
% for prop in data.longhands:
|
||||||
|
% if prop.animatable:
|
||||||
|
AnimatedProperty::${prop.camel_case}(ref from, ref to) => {
|
||||||
|
if let Some(value) = from.interpolate(to, progress) {
|
||||||
|
style.mutate_${prop.style_struct.ident.strip("_")}().set_${prop.ident}(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: Transition properties need clone
|
||||||
|
pub fn from_transition_property<C: ComputedValues>(transition_property: &TransitionProperty,
|
||||||
|
old_style: &C,
|
||||||
|
new_style: &C) -> AnimatedProperty {
|
||||||
|
// TODO: Generalise this for GeckoLib, adding clone_xxx to the
|
||||||
|
// appropiate longhands.
|
||||||
|
let old_style = old_style.as_servo();
|
||||||
|
let new_style = new_style.as_servo();
|
||||||
|
match *transition_property {
|
||||||
|
TransitionProperty::All => panic!("Can't use TransitionProperty::All here."),
|
||||||
|
% for prop in data.longhands:
|
||||||
|
% if prop.animatable:
|
||||||
|
TransitionProperty::${prop.camel_case} => {
|
||||||
|
AnimatedProperty::${prop.camel_case}(old_style.get_${prop.style_struct.ident.strip("_")}().${prop.ident}.clone(),
|
||||||
|
new_style.get_${prop.style_struct.ident.strip("_")}().${prop.ident}.clone())
|
||||||
|
}
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Interpolate: Sized {
|
||||||
|
fn interpolate(&self, other: &Self, time: f64) -> Option<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for Au {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &Au, time: f64) -> Option<Au> {
|
||||||
|
Some(Au((self.0 as f64 + (other.0 as f64 - self.0 as f64) * time).round() as i32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <T> Interpolate for Option<T> where T: Interpolate {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &Option<T>, time: f64) -> Option<Option<T>> {
|
||||||
|
match (self, other) {
|
||||||
|
(&Some(ref this), &Some(ref other)) => {
|
||||||
|
this.interpolate(other, time).and_then(|value| {
|
||||||
|
Some(Some(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(_, _) => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for f32 {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &f32, time: f64) -> Option<f32> {
|
||||||
|
Some(((*self as f64) + ((*other as f64) - (*self as f64)) * time) as f32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for f64 {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &f64, time: f64) -> Option<f64> {
|
||||||
|
Some(*self + (*other - *self) * time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for i32 {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &i32, time: f64) -> Option<i32> {
|
||||||
|
let a = *self as f64;
|
||||||
|
let b = *other as f64;
|
||||||
|
Some((a + (b - a) * time).round() as i32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for Angle {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &Angle, time: f64) -> Option<Angle> {
|
||||||
|
self.radians().interpolate(&other.radians(), time).map(Angle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for Visibility {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &Visibility, time: f64)
|
||||||
|
-> Option<Visibility> {
|
||||||
|
match (*self, *other) {
|
||||||
|
(Visibility::visible, _) | (_, Visibility::visible) => {
|
||||||
|
if time >= 0.0 && time <= 1.0 {
|
||||||
|
Some(Visibility::visible)
|
||||||
|
} else if time < 0.0 {
|
||||||
|
Some(*self)
|
||||||
|
} else {
|
||||||
|
Some(*other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(_, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for ZIndex {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &ZIndex, time: f64)
|
||||||
|
-> Option<ZIndex> {
|
||||||
|
match (*self, *other) {
|
||||||
|
(ZIndex::Number(ref this),
|
||||||
|
ZIndex::Number(ref other)) => {
|
||||||
|
this.interpolate(other, time).and_then(|value| {
|
||||||
|
Some(ZIndex::Number(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(_, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Interpolate + Clone> Interpolate for Size2D<T> {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
|
||||||
|
let width = match self.width.interpolate(&other.width, time) {
|
||||||
|
Some(width) => width,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let height = match self.height.interpolate(&other.height, time) {
|
||||||
|
Some(height) => height,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
Some(Size2D::new(width, height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Interpolate + Clone> Interpolate for Point2D<T> {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
|
||||||
|
let x = match self.x.interpolate(&other.x, time) {
|
||||||
|
Some(x) => x,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let y = match self.y.interpolate(&other.y, time) {
|
||||||
|
Some(y) => y,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Point2D::new(x, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for BorderRadiusSize {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
|
||||||
|
self.0.interpolate(&other.0, time).map(BorderRadiusSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for VerticalAlign {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &VerticalAlign, time: f64)
|
||||||
|
-> Option<VerticalAlign> {
|
||||||
|
match (*self, *other) {
|
||||||
|
(VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref this)),
|
||||||
|
VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref other))) => {
|
||||||
|
this.interpolate(other, time).and_then(|value| {
|
||||||
|
Some(VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(value)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(_, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for BorderSpacing {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &BorderSpacing, time: f64)
|
||||||
|
-> Option<BorderSpacing> {
|
||||||
|
self.horizontal.interpolate(&other.horizontal, time).and_then(|horizontal| {
|
||||||
|
self.vertical.interpolate(&other.vertical, time).and_then(|vertical| {
|
||||||
|
Some(BorderSpacing { horizontal: horizontal, vertical: vertical })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for RGBA {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &RGBA, time: f64) -> Option<RGBA> {
|
||||||
|
match (self.red.interpolate(&other.red, time),
|
||||||
|
self.green.interpolate(&other.green, time),
|
||||||
|
self.blue.interpolate(&other.blue, time),
|
||||||
|
self.alpha.interpolate(&other.alpha, time)) {
|
||||||
|
(Some(red), Some(green), Some(blue), Some(alpha)) => {
|
||||||
|
Some(RGBA { red: red, green: green, blue: blue, alpha: alpha })
|
||||||
|
}
|
||||||
|
(_, _, _, _) => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for CSSParserColor {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
|
||||||
|
match (*self, *other) {
|
||||||
|
(CSSParserColor::RGBA(ref this), CSSParserColor::RGBA(ref other)) => {
|
||||||
|
this.interpolate(other, time).and_then(|value| {
|
||||||
|
Some(CSSParserColor::RGBA(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(_, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for CalcLengthOrPercentage {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &CalcLengthOrPercentage, time: f64)
|
||||||
|
-> Option<CalcLengthOrPercentage> {
|
||||||
|
Some(CalcLengthOrPercentage {
|
||||||
|
length: self.length().interpolate(&other.length(), time),
|
||||||
|
percentage: self.percentage().interpolate(&other.percentage(), time),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for LengthOrPercentage {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &LengthOrPercentage, time: f64)
|
||||||
|
-> Option<LengthOrPercentage> {
|
||||||
|
match (*self, *other) {
|
||||||
|
(LengthOrPercentage::Length(ref this),
|
||||||
|
LengthOrPercentage::Length(ref other)) => {
|
||||||
|
this.interpolate(other, time).and_then(|value| {
|
||||||
|
Some(LengthOrPercentage::Length(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(LengthOrPercentage::Percentage(ref this),
|
||||||
|
LengthOrPercentage::Percentage(ref other)) => {
|
||||||
|
this.interpolate(other, time).and_then(|value| {
|
||||||
|
Some(LengthOrPercentage::Percentage(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(this, other) => {
|
||||||
|
let this: CalcLengthOrPercentage = From::from(this);
|
||||||
|
let other: CalcLengthOrPercentage = From::from(other);
|
||||||
|
this.interpolate(&other, time).and_then(|value| {
|
||||||
|
Some(LengthOrPercentage::Calc(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for LengthOrPercentageOrAuto {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &LengthOrPercentageOrAuto, time: f64)
|
||||||
|
-> Option<LengthOrPercentageOrAuto> {
|
||||||
|
match (*self, *other) {
|
||||||
|
(LengthOrPercentageOrAuto::Length(ref this),
|
||||||
|
LengthOrPercentageOrAuto::Length(ref other)) => {
|
||||||
|
this.interpolate(other, time).and_then(|value| {
|
||||||
|
Some(LengthOrPercentageOrAuto::Length(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(LengthOrPercentageOrAuto::Percentage(ref this),
|
||||||
|
LengthOrPercentageOrAuto::Percentage(ref other)) => {
|
||||||
|
this.interpolate(other, time).and_then(|value| {
|
||||||
|
Some(LengthOrPercentageOrAuto::Percentage(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(LengthOrPercentageOrAuto::Auto, LengthOrPercentageOrAuto::Auto) => {
|
||||||
|
Some(LengthOrPercentageOrAuto::Auto)
|
||||||
|
}
|
||||||
|
(this, other) => {
|
||||||
|
let this: Option<CalcLengthOrPercentage> = From::from(this);
|
||||||
|
let other: Option<CalcLengthOrPercentage> = From::from(other);
|
||||||
|
this.interpolate(&other, time).unwrap_or(None).and_then(|value| {
|
||||||
|
Some(LengthOrPercentageOrAuto::Calc(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for LengthOrPercentageOrNone {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &LengthOrPercentageOrNone, time: f64)
|
||||||
|
-> Option<LengthOrPercentageOrNone> {
|
||||||
|
match (*self, *other) {
|
||||||
|
(LengthOrPercentageOrNone::Length(ref this),
|
||||||
|
LengthOrPercentageOrNone::Length(ref other)) => {
|
||||||
|
this.interpolate(other, time).and_then(|value| {
|
||||||
|
Some(LengthOrPercentageOrNone::Length(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(LengthOrPercentageOrNone::Percentage(ref this),
|
||||||
|
LengthOrPercentageOrNone::Percentage(ref other)) => {
|
||||||
|
this.interpolate(other, time).and_then(|value| {
|
||||||
|
Some(LengthOrPercentageOrNone::Percentage(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(LengthOrPercentageOrNone::None, LengthOrPercentageOrNone::None) => {
|
||||||
|
Some(LengthOrPercentageOrNone::None)
|
||||||
|
}
|
||||||
|
(_, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for LineHeight {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &LineHeight, time: f64)
|
||||||
|
-> Option<LineHeight> {
|
||||||
|
match (*self, *other) {
|
||||||
|
(LineHeight::Length(ref this),
|
||||||
|
LineHeight::Length(ref other)) => {
|
||||||
|
this.interpolate(other, time).and_then(|value| {
|
||||||
|
Some(LineHeight::Length(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(LineHeight::Number(ref this),
|
||||||
|
LineHeight::Number(ref other)) => {
|
||||||
|
this.interpolate(other, time).and_then(|value| {
|
||||||
|
Some(LineHeight::Number(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(LineHeight::Normal, LineHeight::Normal) => {
|
||||||
|
Some(LineHeight::Normal)
|
||||||
|
}
|
||||||
|
(_, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// http://dev.w3.org/csswg/css-transitions/#animtype-font-weight
|
||||||
|
impl Interpolate for FontWeight {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &FontWeight, time: f64)
|
||||||
|
-> Option<FontWeight> {
|
||||||
|
let a = (*self as u32) as f64;
|
||||||
|
let b = (*other as u32) as f64;
|
||||||
|
let weight = a + (b - a) * time;
|
||||||
|
Some(if weight < 150. {
|
||||||
|
FontWeight::Weight100
|
||||||
|
} else if weight < 250. {
|
||||||
|
FontWeight::Weight200
|
||||||
|
} else if weight < 350. {
|
||||||
|
FontWeight::Weight300
|
||||||
|
} else if weight < 450. {
|
||||||
|
FontWeight::Weight400
|
||||||
|
} else if weight < 550. {
|
||||||
|
FontWeight::Weight500
|
||||||
|
} else if weight < 650. {
|
||||||
|
FontWeight::Weight600
|
||||||
|
} else if weight < 750. {
|
||||||
|
FontWeight::Weight700
|
||||||
|
} else if weight < 850. {
|
||||||
|
FontWeight::Weight800
|
||||||
|
} else {
|
||||||
|
FontWeight::Weight900
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for ClipRect {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &ClipRect, time: f64)
|
||||||
|
-> Option<ClipRect> {
|
||||||
|
match (self.top.interpolate(&other.top, time),
|
||||||
|
self.right.interpolate(&other.right, time),
|
||||||
|
self.bottom.interpolate(&other.bottom, time),
|
||||||
|
self.left.interpolate(&other.left, time)) {
|
||||||
|
(Some(top), Some(right), Some(bottom), Some(left)) => {
|
||||||
|
Some(ClipRect { top: top, right: right, bottom: bottom, left: left })
|
||||||
|
},
|
||||||
|
(_, _, _, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for BackgroundPosition {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &BackgroundPosition, time: f64)
|
||||||
|
-> Option<BackgroundPosition> {
|
||||||
|
match (self.horizontal.interpolate(&other.horizontal, time),
|
||||||
|
self.vertical.interpolate(&other.vertical, time)) {
|
||||||
|
(Some(horizontal), Some(vertical)) => {
|
||||||
|
Some(BackgroundPosition { horizontal: horizontal, vertical: vertical })
|
||||||
|
},
|
||||||
|
(_, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for BackgroundSize {
|
||||||
|
fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
|
||||||
|
use properties::longhands::background_size::computed_value::ExplicitSize;
|
||||||
|
match (self, other) {
|
||||||
|
(&BackgroundSize::Explicit(ref me), &BackgroundSize::Explicit(ref other))
|
||||||
|
=> match (me.width.interpolate(&other.width, time),
|
||||||
|
me.height.interpolate(&other.height, time)) {
|
||||||
|
(Some(width), Some(height))
|
||||||
|
=> Some(BackgroundSize::Explicit(
|
||||||
|
ExplicitSize { width: width, height: height })),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for TextShadow {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &TextShadow, time: f64)
|
||||||
|
-> Option<TextShadow> {
|
||||||
|
match (self.offset_x.interpolate(&other.offset_x, time),
|
||||||
|
self.offset_y.interpolate(&other.offset_y, time),
|
||||||
|
self.blur_radius.interpolate(&other.blur_radius, time),
|
||||||
|
self.color.interpolate(&other.color, time)) {
|
||||||
|
(Some(offset_x), Some(offset_y), Some(blur_radius), Some(color)) => {
|
||||||
|
Some(TextShadow { offset_x: offset_x, offset_y: offset_y, blur_radius: blur_radius, color: color })
|
||||||
|
},
|
||||||
|
(_, _, _, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for TextShadowList {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &TextShadowList, time: f64)
|
||||||
|
-> Option<TextShadowList> {
|
||||||
|
let zero = TextShadow {
|
||||||
|
offset_x: Au(0),
|
||||||
|
offset_y: Au(0),
|
||||||
|
blur_radius: Au(0),
|
||||||
|
color: CSSParserColor::RGBA(RGBA {
|
||||||
|
red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let interpolate_each = |(a, b): (&TextShadow, &TextShadow)| {
|
||||||
|
a.interpolate(b, time).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(TextShadowList(match self.0.len().cmp(&other.0.len()) {
|
||||||
|
Ordering::Less => other.0.iter().chain(repeat(&zero)).zip(other.0.iter()).map(interpolate_each).collect(),
|
||||||
|
_ => self.0.iter().zip(other.0.iter().chain(repeat(&zero))).map(interpolate_each).collect(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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: &[TransformOperation],
|
||||||
|
to_list: &[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) {
|
||||||
|
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: &[TransformOperation],
|
||||||
|
to_list: &[TransformOperation],
|
||||||
|
time: f64) -> TransformList {
|
||||||
|
let mut result = vec![];
|
||||||
|
|
||||||
|
if can_interpolate_list(from_list, to_list) {
|
||||||
|
for (from, to) in from_list.iter().zip(to_list) {
|
||||||
|
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.extend_from_slice(from_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
TransformList(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: &[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(Angle(0.0), Angle(0.0)));
|
||||||
|
}
|
||||||
|
TransformOperation::Translate(..) => {
|
||||||
|
result.push(TransformOperation::Translate(LengthOrPercentage::zero(),
|
||||||
|
LengthOrPercentage::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 LengthOrNone {
|
||||||
|
fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
|
||||||
|
match (*self, *other) {
|
||||||
|
(LengthOrNone::Length(ref len), LengthOrNone::Length(ref other)) =>
|
||||||
|
len.interpolate(&other, time).map(LengthOrNone::Length),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpolate for TransformList {
|
||||||
|
#[inline]
|
||||||
|
fn interpolate(&self, other: &TransformList, time: f64) -> Option<TransformList> {
|
||||||
|
// 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, 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)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// http://dev.w3.org/csswg/css-transforms/#none-none-animation
|
||||||
|
TransformList(None)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
}
|
|
@ -525,170 +525,17 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
// TODO(pcwalton): Lots more properties.
|
|
||||||
<%helpers:longhand name="transition-property">
|
<%helpers:longhand name="transition-property">
|
||||||
use self::computed_value::TransitionProperty;
|
|
||||||
|
|
||||||
pub use self::computed_value::SingleComputedValue as SingleSpecifiedValue;
|
pub use self::computed_value::SingleComputedValue as SingleSpecifiedValue;
|
||||||
pub use self::computed_value::T as SpecifiedValue;
|
pub use self::computed_value::T as SpecifiedValue;
|
||||||
|
|
||||||
pub mod computed_value {
|
pub mod computed_value {
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
// NB: Can't generate the type here because it needs all the longhands
|
||||||
pub use self::TransitionProperty as SingleComputedValue;
|
// generated beforehand.
|
||||||
|
pub use properties::animated_properties::TransitionProperty;
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
pub use properties::animated_properties::TransitionProperty as SingleComputedValue;
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub enum TransitionProperty {
|
|
||||||
All,
|
|
||||||
BackgroundColor,
|
|
||||||
BackgroundPosition,
|
|
||||||
BorderBottomColor,
|
|
||||||
BorderBottomWidth,
|
|
||||||
BorderLeftColor,
|
|
||||||
BorderLeftWidth,
|
|
||||||
BorderRightColor,
|
|
||||||
BorderRightWidth,
|
|
||||||
BorderSpacing,
|
|
||||||
BorderTopColor,
|
|
||||||
BorderTopWidth,
|
|
||||||
Bottom,
|
|
||||||
Color,
|
|
||||||
Clip,
|
|
||||||
FontSize,
|
|
||||||
FontWeight,
|
|
||||||
Height,
|
|
||||||
Left,
|
|
||||||
LetterSpacing,
|
|
||||||
LineHeight,
|
|
||||||
MarginBottom,
|
|
||||||
MarginLeft,
|
|
||||||
MarginRight,
|
|
||||||
MarginTop,
|
|
||||||
MaxHeight,
|
|
||||||
MaxWidth,
|
|
||||||
MinHeight,
|
|
||||||
MinWidth,
|
|
||||||
Opacity,
|
|
||||||
OutlineColor,
|
|
||||||
OutlineWidth,
|
|
||||||
PaddingBottom,
|
|
||||||
PaddingLeft,
|
|
||||||
PaddingRight,
|
|
||||||
PaddingTop,
|
|
||||||
Right,
|
|
||||||
TextIndent,
|
|
||||||
TextShadow,
|
|
||||||
Top,
|
|
||||||
Transform,
|
|
||||||
VerticalAlign,
|
|
||||||
Visibility,
|
|
||||||
Width,
|
|
||||||
WordSpacing,
|
|
||||||
ZIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub static ALL_TRANSITION_PROPERTIES: [TransitionProperty; 45] = [
|
|
||||||
TransitionProperty::BackgroundColor,
|
|
||||||
TransitionProperty::BackgroundPosition,
|
|
||||||
TransitionProperty::BorderBottomColor,
|
|
||||||
TransitionProperty::BorderBottomWidth,
|
|
||||||
TransitionProperty::BorderLeftColor,
|
|
||||||
TransitionProperty::BorderLeftWidth,
|
|
||||||
TransitionProperty::BorderRightColor,
|
|
||||||
TransitionProperty::BorderRightWidth,
|
|
||||||
TransitionProperty::BorderSpacing,
|
|
||||||
TransitionProperty::BorderTopColor,
|
|
||||||
TransitionProperty::BorderTopWidth,
|
|
||||||
TransitionProperty::Bottom,
|
|
||||||
TransitionProperty::Color,
|
|
||||||
TransitionProperty::Clip,
|
|
||||||
TransitionProperty::FontSize,
|
|
||||||
TransitionProperty::FontWeight,
|
|
||||||
TransitionProperty::Height,
|
|
||||||
TransitionProperty::Left,
|
|
||||||
TransitionProperty::LetterSpacing,
|
|
||||||
TransitionProperty::LineHeight,
|
|
||||||
TransitionProperty::MarginBottom,
|
|
||||||
TransitionProperty::MarginLeft,
|
|
||||||
TransitionProperty::MarginRight,
|
|
||||||
TransitionProperty::MarginTop,
|
|
||||||
TransitionProperty::MaxHeight,
|
|
||||||
TransitionProperty::MaxWidth,
|
|
||||||
TransitionProperty::MinHeight,
|
|
||||||
TransitionProperty::MinWidth,
|
|
||||||
TransitionProperty::Opacity,
|
|
||||||
TransitionProperty::OutlineColor,
|
|
||||||
TransitionProperty::OutlineWidth,
|
|
||||||
TransitionProperty::PaddingBottom,
|
|
||||||
TransitionProperty::PaddingLeft,
|
|
||||||
TransitionProperty::PaddingRight,
|
|
||||||
TransitionProperty::PaddingTop,
|
|
||||||
TransitionProperty::Right,
|
|
||||||
TransitionProperty::TextIndent,
|
|
||||||
TransitionProperty::TextShadow,
|
|
||||||
TransitionProperty::Top,
|
|
||||||
TransitionProperty::Transform,
|
|
||||||
TransitionProperty::VerticalAlign,
|
|
||||||
TransitionProperty::Visibility,
|
|
||||||
TransitionProperty::Width,
|
|
||||||
TransitionProperty::WordSpacing,
|
|
||||||
TransitionProperty::ZIndex,
|
|
||||||
];
|
|
||||||
|
|
||||||
impl ToCss for TransitionProperty {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
match *self {
|
|
||||||
TransitionProperty::All => dest.write_str("all"),
|
|
||||||
TransitionProperty::BackgroundColor => dest.write_str("background-color"),
|
|
||||||
TransitionProperty::BackgroundPosition => dest.write_str("background-position"),
|
|
||||||
TransitionProperty::BorderBottomColor => dest.write_str("border-bottom-color"),
|
|
||||||
TransitionProperty::BorderBottomWidth => dest.write_str("border-bottom-width"),
|
|
||||||
TransitionProperty::BorderLeftColor => dest.write_str("border-left-color"),
|
|
||||||
TransitionProperty::BorderLeftWidth => dest.write_str("border-left-width"),
|
|
||||||
TransitionProperty::BorderRightColor => dest.write_str("border-right-color"),
|
|
||||||
TransitionProperty::BorderRightWidth => dest.write_str("border-right-width"),
|
|
||||||
TransitionProperty::BorderSpacing => dest.write_str("border-spacing"),
|
|
||||||
TransitionProperty::BorderTopColor => dest.write_str("border-top-color"),
|
|
||||||
TransitionProperty::BorderTopWidth => dest.write_str("border-top-width"),
|
|
||||||
TransitionProperty::Bottom => dest.write_str("bottom"),
|
|
||||||
TransitionProperty::Color => dest.write_str("color"),
|
|
||||||
TransitionProperty::Clip => dest.write_str("clip"),
|
|
||||||
TransitionProperty::FontSize => dest.write_str("font-size"),
|
|
||||||
TransitionProperty::FontWeight => dest.write_str("font-weight"),
|
|
||||||
TransitionProperty::Height => dest.write_str("height"),
|
|
||||||
TransitionProperty::Left => dest.write_str("left"),
|
|
||||||
TransitionProperty::LetterSpacing => dest.write_str("letter-spacing"),
|
|
||||||
TransitionProperty::LineHeight => dest.write_str("line-height"),
|
|
||||||
TransitionProperty::MarginBottom => dest.write_str("margin-bottom"),
|
|
||||||
TransitionProperty::MarginLeft => dest.write_str("margin-left"),
|
|
||||||
TransitionProperty::MarginRight => dest.write_str("margin-right"),
|
|
||||||
TransitionProperty::MarginTop => dest.write_str("margin-top"),
|
|
||||||
TransitionProperty::MaxHeight => dest.write_str("max-height"),
|
|
||||||
TransitionProperty::MaxWidth => dest.write_str("max-width"),
|
|
||||||
TransitionProperty::MinHeight => dest.write_str("min-height"),
|
|
||||||
TransitionProperty::MinWidth => dest.write_str("min-width"),
|
|
||||||
TransitionProperty::Opacity => dest.write_str("opacity"),
|
|
||||||
TransitionProperty::OutlineColor => dest.write_str("outline-color"),
|
|
||||||
TransitionProperty::OutlineWidth => dest.write_str("outline-width"),
|
|
||||||
TransitionProperty::PaddingBottom => dest.write_str("padding-bottom"),
|
|
||||||
TransitionProperty::PaddingLeft => dest.write_str("padding-left"),
|
|
||||||
TransitionProperty::PaddingRight => dest.write_str("padding-right"),
|
|
||||||
TransitionProperty::PaddingTop => dest.write_str("padding-top"),
|
|
||||||
TransitionProperty::Right => dest.write_str("right"),
|
|
||||||
TransitionProperty::TextIndent => dest.write_str("text-indent"),
|
|
||||||
TransitionProperty::TextShadow => dest.write_str("text-shadow"),
|
|
||||||
TransitionProperty::Top => dest.write_str("top"),
|
|
||||||
TransitionProperty::Transform => dest.write_str("transform"),
|
|
||||||
TransitionProperty::VerticalAlign => dest.write_str("vertical-align"),
|
|
||||||
TransitionProperty::Visibility => dest.write_str("visibility"),
|
|
||||||
TransitionProperty::Width => dest.write_str("width"),
|
|
||||||
TransitionProperty::WordSpacing => dest.write_str("word-spacing"),
|
|
||||||
TransitionProperty::ZIndex => dest.write_str("z-index"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
@ -715,61 +562,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
|
||||||
computed_value::T(Vec::new())
|
computed_value::T(Vec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue,()> {
|
|
||||||
match_ignore_ascii_case! {
|
|
||||||
try!(input.expect_ident()),
|
|
||||||
"all" => Ok(TransitionProperty::All),
|
|
||||||
"background-color" => Ok(TransitionProperty::BackgroundColor),
|
|
||||||
"background-position" => Ok(TransitionProperty::BackgroundPosition),
|
|
||||||
"border-bottom-color" => Ok(TransitionProperty::BorderBottomColor),
|
|
||||||
"border-bottom-width" => Ok(TransitionProperty::BorderBottomWidth),
|
|
||||||
"border-left-color" => Ok(TransitionProperty::BorderLeftColor),
|
|
||||||
"border-left-width" => Ok(TransitionProperty::BorderLeftWidth),
|
|
||||||
"border-right-color" => Ok(TransitionProperty::BorderRightColor),
|
|
||||||
"border-right-width" => Ok(TransitionProperty::BorderRightWidth),
|
|
||||||
"border-spacing" => Ok(TransitionProperty::BorderSpacing),
|
|
||||||
"border-top-color" => Ok(TransitionProperty::BorderTopColor),
|
|
||||||
"border-top-width" => Ok(TransitionProperty::BorderTopWidth),
|
|
||||||
"bottom" => Ok(TransitionProperty::Bottom),
|
|
||||||
"color" => Ok(TransitionProperty::Color),
|
|
||||||
"clip" => Ok(TransitionProperty::Clip),
|
|
||||||
"font-size" => Ok(TransitionProperty::FontSize),
|
|
||||||
"font-weight" => Ok(TransitionProperty::FontWeight),
|
|
||||||
"height" => Ok(TransitionProperty::Height),
|
|
||||||
"left" => Ok(TransitionProperty::Left),
|
|
||||||
"letter-spacing" => Ok(TransitionProperty::LetterSpacing),
|
|
||||||
"line-height" => Ok(TransitionProperty::LineHeight),
|
|
||||||
"margin-bottom" => Ok(TransitionProperty::MarginBottom),
|
|
||||||
"margin-left" => Ok(TransitionProperty::MarginLeft),
|
|
||||||
"margin-right" => Ok(TransitionProperty::MarginRight),
|
|
||||||
"margin-top" => Ok(TransitionProperty::MarginTop),
|
|
||||||
"max-height" => Ok(TransitionProperty::MaxHeight),
|
|
||||||
"max-width" => Ok(TransitionProperty::MaxWidth),
|
|
||||||
"min-height" => Ok(TransitionProperty::MinHeight),
|
|
||||||
"min-width" => Ok(TransitionProperty::MinWidth),
|
|
||||||
"opacity" => Ok(TransitionProperty::Opacity),
|
|
||||||
"outline-color" => Ok(TransitionProperty::OutlineColor),
|
|
||||||
"outline-width" => Ok(TransitionProperty::OutlineWidth),
|
|
||||||
"padding-bottom" => Ok(TransitionProperty::PaddingBottom),
|
|
||||||
"padding-left" => Ok(TransitionProperty::PaddingLeft),
|
|
||||||
"padding-right" => Ok(TransitionProperty::PaddingRight),
|
|
||||||
"padding-top" => Ok(TransitionProperty::PaddingTop),
|
|
||||||
"right" => Ok(TransitionProperty::Right),
|
|
||||||
"text-indent" => Ok(TransitionProperty::TextIndent),
|
|
||||||
"text-shadow" => Ok(TransitionProperty::TextShadow),
|
|
||||||
"top" => Ok(TransitionProperty::Top),
|
|
||||||
"transform" => Ok(TransitionProperty::Transform),
|
|
||||||
"vertical-align" => Ok(TransitionProperty::VerticalAlign),
|
|
||||||
"visibility" => Ok(TransitionProperty::Visibility),
|
|
||||||
"width" => Ok(TransitionProperty::Width),
|
|
||||||
"word-spacing" => Ok(TransitionProperty::WordSpacing),
|
|
||||||
"z-index" => Ok(TransitionProperty::ZIndex),
|
|
||||||
_ => Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||||||
Ok(SpecifiedValue(try!(input.parse_comma_separated(parse_one))))
|
Ok(SpecifiedValue(try!(input.parse_comma_separated(SingleSpecifiedValue::parse))))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToComputedValue for SpecifiedValue {
|
impl ToComputedValue for SpecifiedValue {
|
||||||
|
@ -790,9 +584,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="animation-name" experimental="True">
|
<%helpers:longhand name="animation-name" experimental="True">
|
||||||
use cssparser::ToCss;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
pub mod computed_value {
|
pub mod computed_value {
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
|
@ -818,26 +610,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use Cows? Probably more codegen work would be needed, and this
|
pub use self::computed_value::T as SpecifiedValue;
|
||||||
// could not be that worth it (animations arent *that* used).
|
|
||||||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
|
||||||
pub struct SpecifiedValue(Vec<String>);
|
|
||||||
|
|
||||||
impl ToCss for SpecifiedValue {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
if self.0.is_empty() {
|
|
||||||
return dest.write_str("none")
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i, name) in self.0.iter().enumerate() {
|
|
||||||
if i != 0 {
|
|
||||||
try!(dest.write_str(", "));
|
|
||||||
}
|
|
||||||
try!(dest.write_str(&name));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_initial_value() -> computed_value::T {
|
pub fn get_initial_value() -> computed_value::T {
|
||||||
|
@ -845,27 +618,13 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||||||
|
use std::borrow::Cow;
|
||||||
Ok(SpecifiedValue(try!(input.parse_comma_separated(|input| {
|
Ok(SpecifiedValue(try!(input.parse_comma_separated(|input| {
|
||||||
input.expect_ident().map(Cow::into_owned)
|
input.expect_ident().map(Cow::into_owned)
|
||||||
}))))
|
}))))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToComputedValue for SpecifiedValue {
|
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||||||
type ComputedValue = computed_value::T;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
|
||||||
let mut ret = vec![];
|
|
||||||
if let Some(animations) = context.animations() {
|
|
||||||
for name in self.0.iter() {
|
|
||||||
if animations.contains_key(&**name) {
|
|
||||||
ret.push(name.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
computed_value::T(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="animation-duration" experimental="True">
|
<%helpers:longhand name="animation-duration" experimental="True">
|
||||||
|
|
|
@ -133,6 +133,10 @@ pub mod shorthands {
|
||||||
<%include file="/shorthand/text.mako.rs" />
|
<%include file="/shorthand/text.mako.rs" />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod animated_properties {
|
||||||
|
<%include file="/helpers/animated_properties.mako.rs" />
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO(SimonSapin): Convert this to a syntax extension rather than a Mako template.
|
// TODO(SimonSapin): Convert this to a syntax extension rather than a Mako template.
|
||||||
// Maybe submit for inclusion in libstd?
|
// Maybe submit for inclusion in libstd?
|
||||||
|
@ -1045,6 +1049,23 @@ impl PropertyDeclaration {
|
||||||
PropertyDeclaration::Custom(_, _) => &[]
|
PropertyDeclaration::Custom(_, _) => &[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if this property is one of the animable properties, false
|
||||||
|
/// otherwise.
|
||||||
|
pub fn is_animatable(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
% for property in data.longhands:
|
||||||
|
PropertyDeclaration::${property.camel_case}(_) => {
|
||||||
|
% if property.animatable:
|
||||||
|
true
|
||||||
|
% else:
|
||||||
|
false
|
||||||
|
% endif
|
||||||
|
}
|
||||||
|
% endfor
|
||||||
|
PropertyDeclaration::Custom(..) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod style_struct_traits {
|
pub mod style_struct_traits {
|
||||||
|
@ -1595,14 +1616,12 @@ fn cascade_with_cached_declarations<C: ComputedValues>(
|
||||||
parent_style: &C,
|
parent_style: &C,
|
||||||
cached_style: &C,
|
cached_style: &C,
|
||||||
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
|
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
|
||||||
animations: Option<<&HashMap<String, Vec<Keyframe>>>,
|
|
||||||
mut error_reporter: StdBox<ParseErrorReporter + Send>)
|
mut error_reporter: StdBox<ParseErrorReporter + Send>)
|
||||||
-> C {
|
-> C {
|
||||||
let mut context = computed::Context {
|
let mut context = computed::Context {
|
||||||
is_root_element: false,
|
is_root_element: false,
|
||||||
viewport_size: viewport_size,
|
viewport_size: viewport_size,
|
||||||
inherited_style: parent_style,
|
inherited_style: parent_style,
|
||||||
animations: animations,
|
|
||||||
style: C::new(
|
style: C::new(
|
||||||
custom_properties,
|
custom_properties,
|
||||||
shareable,
|
shareable,
|
||||||
|
@ -1742,7 +1761,6 @@ pub fn cascade<C: ComputedValues>(
|
||||||
shareable: bool,
|
shareable: bool,
|
||||||
parent_style: Option<<&C>,
|
parent_style: Option<<&C>,
|
||||||
cached_style: Option<<&C>,
|
cached_style: Option<<&C>,
|
||||||
animations: Option<<&HashMap<String, Vec<Keyframe>>>,
|
|
||||||
mut error_reporter: StdBox<ParseErrorReporter + Send>)
|
mut error_reporter: StdBox<ParseErrorReporter + Send>)
|
||||||
-> (C, bool) {
|
-> (C, bool) {
|
||||||
use properties::style_struct_traits::{Border, Box, Font, Outline};
|
use properties::style_struct_traits::{Border, Box, Font, Outline};
|
||||||
|
@ -1778,7 +1796,6 @@ pub fn cascade<C: ComputedValues>(
|
||||||
parent_style,
|
parent_style,
|
||||||
cached_style,
|
cached_style,
|
||||||
custom_properties,
|
custom_properties,
|
||||||
animations,
|
|
||||||
error_reporter);
|
error_reporter);
|
||||||
return (style, false)
|
return (style, false)
|
||||||
}
|
}
|
||||||
|
@ -1787,7 +1804,6 @@ pub fn cascade<C: ComputedValues>(
|
||||||
is_root_element: is_root_element,
|
is_root_element: is_root_element,
|
||||||
viewport_size: viewport_size,
|
viewport_size: viewport_size,
|
||||||
inherited_style: inherited_style,
|
inherited_style: inherited_style,
|
||||||
animations: animations,
|
|
||||||
style: C::new(
|
style: C::new(
|
||||||
custom_properties,
|
custom_properties,
|
||||||
shareable,
|
shareable,
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
let (mut timing_function, mut delay) = (None, None);
|
let (mut timing_function, mut delay) = (None, None);
|
||||||
loop {
|
loop {
|
||||||
if property.is_none() {
|
if property.is_none() {
|
||||||
if let Ok(value) = input.try(|input| transition_property::parse_one(input)) {
|
if let Ok(value) = input.try(transition_property::SingleSpecifiedValue::parse) {
|
||||||
property = Some(value);
|
property = Some(value);
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use dom::PresentationalHintsSynthetizer;
|
||||||
use element_state::*;
|
use element_state::*;
|
||||||
use error_reporting::StdoutErrorReporter;
|
use error_reporting::StdoutErrorReporter;
|
||||||
use keyframes::Keyframe;
|
use keyframes::Keyframe;
|
||||||
|
use keyframes::ComputedKeyframesAnimation;
|
||||||
use media_queries::{Device, MediaType};
|
use media_queries::{Device, MediaType};
|
||||||
use parser::ParserContextExtraData;
|
use parser::ParserContextExtraData;
|
||||||
use properties::{self, PropertyDeclaration, PropertyDeclarationBlock};
|
use properties::{self, PropertyDeclaration, PropertyDeclarationBlock};
|
||||||
|
@ -128,7 +129,7 @@ pub struct Stylist<Impl: SelectorImplExt> {
|
||||||
BuildHasherDefault<::fnv::FnvHasher>>,
|
BuildHasherDefault<::fnv::FnvHasher>>,
|
||||||
|
|
||||||
/// A map with all the animations indexed by name.
|
/// A map with all the animations indexed by name.
|
||||||
animations: HashMap<String, Vec<Keyframe>>,
|
animations: HashMap<String, ComputedKeyframesAnimation<Impl::ComputedValues>>,
|
||||||
|
|
||||||
/// Applicable declarations for a given non-eagerly cascaded pseudo-element.
|
/// Applicable declarations for a given non-eagerly cascaded pseudo-element.
|
||||||
/// These are eagerly computed once, and then used to resolve the new
|
/// These are eagerly computed once, and then used to resolve the new
|
||||||
|
@ -252,10 +253,10 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
||||||
self.rules_source_order = rules_source_order;
|
self.rules_source_order = rules_source_order;
|
||||||
}
|
}
|
||||||
CSSRule::Keyframes(ref keyframes_rule) => {
|
CSSRule::Keyframes(ref keyframes_rule) => {
|
||||||
// TODO: This *might* be optimised converting the
|
if let Some(computed) = ComputedKeyframesAnimation::from_keyframes(&keyframes_rule.keyframes) {
|
||||||
// Vec<Keyframe> into something like Arc<[Keyframe]>.
|
self.animations.insert(keyframes_rule.name.clone(),
|
||||||
self.animations.insert(keyframes_rule.name.clone(),
|
computed);
|
||||||
keyframes_rule.keyframes.clone());
|
}
|
||||||
}
|
}
|
||||||
// We don't care about any other rule.
|
// We don't care about any other rule.
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -289,7 +290,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
||||||
properties::cascade(self.device.au_viewport_size(),
|
properties::cascade(self.device.au_viewport_size(),
|
||||||
&declarations, false,
|
&declarations, false,
|
||||||
parent.map(|p| &**p),
|
parent.map(|p| &**p),
|
||||||
None, None,
|
None,
|
||||||
Box::new(StdoutErrorReporter));
|
Box::new(StdoutErrorReporter));
|
||||||
Some(Arc::new(computed))
|
Some(Arc::new(computed))
|
||||||
} else {
|
} else {
|
||||||
|
@ -322,7 +323,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
||||||
let (computed, _) =
|
let (computed, _) =
|
||||||
properties::cascade(self.device.au_viewport_size(),
|
properties::cascade(self.device.au_viewport_size(),
|
||||||
&declarations, false,
|
&declarations, false,
|
||||||
Some(&**parent), None, None,
|
Some(&**parent), None,
|
||||||
Box::new(StdoutErrorReporter));
|
Box::new(StdoutErrorReporter));
|
||||||
Some(Arc::new(computed))
|
Some(Arc::new(computed))
|
||||||
}
|
}
|
||||||
|
@ -458,7 +459,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn animations(&self) -> &HashMap<String, Vec<Keyframe>> {
|
pub fn animations(&self) -> &HashMap<String, ComputedKeyframesAnimation<Impl::ComputedValues>> {
|
||||||
&self.animations
|
&self.animations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1391,16 +1391,13 @@ pub mod specified {
|
||||||
pub fn parse_border_radius(input: &mut Parser) -> Result<BorderRadiusSize, ()> {
|
pub fn parse_border_radius(input: &mut Parser) -> Result<BorderRadiusSize, ()> {
|
||||||
input.try(BorderRadiusSize::parse).or_else(|()| {
|
input.try(BorderRadiusSize::parse).or_else(|()| {
|
||||||
match_ignore_ascii_case! { try!(input.expect_ident()),
|
match_ignore_ascii_case! { try!(input.expect_ident()),
|
||||||
"thin" =>
|
"thin" => Ok(BorderRadiusSize::circle(
|
||||||
Ok(BorderRadiusSize::circle(
|
LengthOrPercentage::Length(Length::from_px(1.)))),
|
||||||
LengthOrPercentage::Length(Length::from_px(1.)))),
|
"medium" => Ok(BorderRadiusSize::circle(
|
||||||
"medium" =>
|
LengthOrPercentage::Length(Length::from_px(3.)))),
|
||||||
Ok(BorderRadiusSize::circle(
|
"thick" => Ok(BorderRadiusSize::circle(
|
||||||
LengthOrPercentage::Length(Length::from_px(3.)))),
|
LengthOrPercentage::Length(Length::from_px(5.)))),
|
||||||
"thick" =>
|
_ => Err(())
|
||||||
Ok(BorderRadiusSize::circle(
|
|
||||||
LengthOrPercentage::Length(Length::from_px(5.)))),
|
|
||||||
_ => Err(())
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1561,10 +1558,8 @@ pub mod specified {
|
||||||
pub mod computed {
|
pub mod computed {
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use euclid::size::Size2D;
|
use euclid::size::Size2D;
|
||||||
use keyframes::Keyframe;
|
|
||||||
use properties::ComputedValues;
|
use properties::ComputedValues;
|
||||||
use properties::style_struct_traits::Font;
|
use properties::style_struct_traits::Font;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use super::LocalToCss;
|
use super::LocalToCss;
|
||||||
use super::specified::AngleOrCorner;
|
use super::specified::AngleOrCorner;
|
||||||
|
@ -1580,7 +1575,6 @@ pub mod computed {
|
||||||
fn inherited_style(&self) -> &Self::ConcreteComputedValues;
|
fn inherited_style(&self) -> &Self::ConcreteComputedValues;
|
||||||
fn style(&self) -> &Self::ConcreteComputedValues;
|
fn style(&self) -> &Self::ConcreteComputedValues;
|
||||||
fn mutate_style(&mut self) -> &mut Self::ConcreteComputedValues;
|
fn mutate_style(&mut self) -> &mut Self::ConcreteComputedValues;
|
||||||
fn animations(&self) -> Option<&HashMap<String, Vec<Keyframe>>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Context<'a, C: ComputedValues> {
|
pub struct Context<'a, C: ComputedValues> {
|
||||||
|
@ -1588,7 +1582,6 @@ pub mod computed {
|
||||||
pub viewport_size: Size2D<Au>,
|
pub viewport_size: Size2D<Au>,
|
||||||
pub inherited_style: &'a C,
|
pub inherited_style: &'a C,
|
||||||
|
|
||||||
pub animations: Option<&'a HashMap<String, Vec<Keyframe>>>,
|
|
||||||
/// Values access through this need to be in the properties "computed early":
|
/// Values access through this need to be in the properties "computed early":
|
||||||
/// color, text-decoration, font-size, display, position, float, border-*-style, outline-style
|
/// color, text-decoration, font-size, display, position, float, border-*-style, outline-style
|
||||||
pub style: C,
|
pub style: C,
|
||||||
|
@ -1601,7 +1594,6 @@ pub mod computed {
|
||||||
fn inherited_style(&self) -> &C { &self.inherited_style }
|
fn inherited_style(&self) -> &C { &self.inherited_style }
|
||||||
fn style(&self) -> &C { &self.style }
|
fn style(&self) -> &C { &self.style }
|
||||||
fn mutate_style(&mut self) -> &mut C { &mut self.style }
|
fn mutate_style(&mut self) -> &mut C { &mut self.style }
|
||||||
fn animations(&self) -> Option<&HashMap<String, Vec<Keyframe>>> { self.animations }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ToComputedValue {
|
pub trait ToComputedValue {
|
||||||
|
@ -1757,7 +1749,7 @@ pub mod computed {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>);
|
pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>);
|
||||||
|
|
||||||
|
|
|
@ -648,7 +648,6 @@ impl MaybeNew for ViewportConstraints {
|
||||||
viewport_size: initial_viewport,
|
viewport_size: initial_viewport,
|
||||||
inherited_style: ServoComputedValues::initial_values(),
|
inherited_style: ServoComputedValues::initial_values(),
|
||||||
style: ServoComputedValues::initial_values().clone(),
|
style: ServoComputedValues::initial_values().clone(),
|
||||||
animations: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// DEVICE-ADAPT § 9.3 Resolving 'extend-to-zoom'
|
// DEVICE-ADAPT § 9.3 Resolving 'extend-to-zoom'
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue