зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1643201 - servo: Remove AnimatedProperty.
This removes an extra layer of abstraction and allows Servo to share more code with Gecko. In addition, we will need to handle raw `AnimationValue` structs soon in order to fully implement "faster reversing of interrupted transitions." Depends on D78192 Differential Revision: https://phabricator.services.mozilla.com/D78193
This commit is contained in:
Родитель
67b71f76f3
Коммит
fb83423da6
|
@ -11,7 +11,7 @@ use crate::bezier::Bezier;
|
|||
use crate::context::SharedStyleContext;
|
||||
use crate::dom::{OpaqueNode, TElement, TNode};
|
||||
use crate::font_metrics::FontMetricsProvider;
|
||||
use crate::properties::animated_properties::AnimatedProperty;
|
||||
use crate::properties::animated_properties::AnimationValue;
|
||||
use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
|
||||
use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
|
||||
use crate::properties::{self, CascadeMode, ComputedValues, LonghandId};
|
||||
|
@ -19,6 +19,7 @@ use crate::properties::{self, CascadeMode, ComputedValues, LonghandId};
|
|||
use crate::properties::LonghandIdSet;
|
||||
use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
|
||||
use crate::stylesheets::Origin;
|
||||
use crate::values::animated::{Animate, Procedure};
|
||||
use crate::values::computed::Time;
|
||||
use crate::values::computed::TimingFunction;
|
||||
use crate::values::generics::box_::AnimationIterationCount;
|
||||
|
@ -30,8 +31,11 @@ use std::fmt;
|
|||
/// Represents an animation for a given property.
|
||||
#[derive(Clone, Debug, MallocSizeOf)]
|
||||
pub struct PropertyAnimation {
|
||||
/// An `AnimatedProperty` that this `PropertyAnimation` corresponds to.
|
||||
property: AnimatedProperty,
|
||||
/// The value we are animating from.
|
||||
from: AnimationValue,
|
||||
|
||||
/// The value we are animating to.
|
||||
to: AnimationValue,
|
||||
|
||||
/// The timing function of this `PropertyAnimation`.
|
||||
timing_function: TimingFunction,
|
||||
|
@ -43,12 +47,8 @@ pub struct PropertyAnimation {
|
|||
impl PropertyAnimation {
|
||||
/// Returns the given property longhand id.
|
||||
pub fn property_id(&self) -> LonghandId {
|
||||
self.property.id()
|
||||
}
|
||||
|
||||
/// Returns the given property name.
|
||||
pub fn property_name(&self) -> &'static str {
|
||||
self.property.name()
|
||||
debug_assert_eq!(self.from.id(), self.to.id());
|
||||
self.from.id()
|
||||
}
|
||||
|
||||
fn from_longhand(
|
||||
|
@ -58,30 +58,33 @@ impl PropertyAnimation {
|
|||
old_style: &ComputedValues,
|
||||
new_style: &ComputedValues,
|
||||
) -> Option<PropertyAnimation> {
|
||||
let animated_property = AnimatedProperty::from_longhand(longhand, old_style, new_style)?;
|
||||
// FIXME(emilio): Handle the case where old_style and new_style's writing mode differ.
|
||||
let longhand = longhand.to_physical(new_style.writing_mode);
|
||||
let from = AnimationValue::from_computed_values(longhand, old_style)?;
|
||||
let to = AnimationValue::from_computed_values(longhand, new_style)?;
|
||||
let duration = duration.seconds() as f64;
|
||||
|
||||
let property_animation = PropertyAnimation {
|
||||
property: animated_property,
|
||||
timing_function,
|
||||
duration: duration.seconds() as f64,
|
||||
};
|
||||
|
||||
if property_animation.does_animate() {
|
||||
Some(property_animation)
|
||||
} else {
|
||||
None
|
||||
if from == to || duration == 0.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(PropertyAnimation {
|
||||
from,
|
||||
to,
|
||||
timing_function,
|
||||
duration,
|
||||
})
|
||||
}
|
||||
|
||||
/// Update the given animation at a given point of progress.
|
||||
pub fn update(&self, style: &mut ComputedValues, time: f64) {
|
||||
/// The output of the timing function given the progress ration of this animation.
|
||||
fn timing_function_output(&self, progress: f64) -> f64 {
|
||||
let epsilon = 1. / (200. * self.duration);
|
||||
let progress = match self.timing_function {
|
||||
match self.timing_function {
|
||||
GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => {
|
||||
Bezier::new(x1, y1, x2, y2).solve(time, epsilon)
|
||||
Bezier::new(x1, y1, x2, y2).solve(progress, epsilon)
|
||||
},
|
||||
GenericTimingFunction::Steps(steps, pos) => {
|
||||
let mut current_step = (time * (steps as f64)).floor() as i32;
|
||||
let mut current_step = (progress * (steps as f64)).floor() as i32;
|
||||
|
||||
if pos == StepPosition::Start ||
|
||||
pos == StepPosition::JumpStart ||
|
||||
|
@ -96,7 +99,7 @@ impl PropertyAnimation {
|
|||
// (i.e. Treat before_flag is unset,)
|
||||
// https://drafts.csswg.org/css-easing/#step-timing-function-algo
|
||||
|
||||
if time >= 0.0 && current_step < 0 {
|
||||
if progress >= 0.0 && current_step < 0 {
|
||||
current_step = 0;
|
||||
}
|
||||
|
||||
|
@ -109,7 +112,7 @@ impl PropertyAnimation {
|
|||
StepPosition::End => steps,
|
||||
};
|
||||
|
||||
if time <= 1.0 && current_step > jumps {
|
||||
if progress <= 1.0 && current_step > jumps {
|
||||
current_step = jumps;
|
||||
}
|
||||
|
||||
|
@ -117,22 +120,19 @@ impl PropertyAnimation {
|
|||
},
|
||||
GenericTimingFunction::Keyword(keyword) => {
|
||||
let (x1, x2, y1, y2) = keyword.to_bezier();
|
||||
Bezier::new(x1, x2, y1, y2).solve(time, epsilon)
|
||||
Bezier::new(x1, x2, y1, y2).solve(progress, epsilon)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the given animation at a given point of progress.
|
||||
fn update(&self, style: &mut ComputedValues, progress: f64) {
|
||||
let procedure = Procedure::Interpolate {
|
||||
progress: self.timing_function_output(progress),
|
||||
};
|
||||
|
||||
self.property.update(style, progress);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn does_animate(&self) -> bool {
|
||||
self.property.does_animate() && self.duration != 0.0
|
||||
}
|
||||
|
||||
/// Whether this animation has the same end value as another one.
|
||||
#[inline]
|
||||
pub fn has_the_same_end_value_as(&self, other: &Self) -> bool {
|
||||
self.property.has_the_same_end_value_as(&other.property)
|
||||
if let Ok(new_value) = self.from.animate(&self.to, procedure) {
|
||||
new_value.set_in_style_for_servo(style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -487,42 +487,23 @@ impl Animation {
|
|||
);
|
||||
|
||||
let mut new_style = (*style).clone();
|
||||
let mut update_style_for_longhand = |longhand| {
|
||||
let from = AnimationValue::from_computed_values(longhand, &from_style)?;
|
||||
let to = AnimationValue::from_computed_values(longhand, &target_style)?;
|
||||
PropertyAnimation {
|
||||
from,
|
||||
to,
|
||||
timing_function,
|
||||
duration: relative_duration as f64,
|
||||
}
|
||||
.update(&mut new_style, relative_progress);
|
||||
None::<()>
|
||||
};
|
||||
|
||||
for property in self.keyframes_animation.properties_changed.iter() {
|
||||
debug!(
|
||||
"Animation::update_style: scanning prop {:?} for animation \"{}\"",
|
||||
property, self.name
|
||||
);
|
||||
let animation = PropertyAnimation::from_longhand(
|
||||
property,
|
||||
timing_function,
|
||||
Time::from_seconds(relative_duration as f32),
|
||||
&from_style,
|
||||
&target_style,
|
||||
);
|
||||
|
||||
match animation {
|
||||
Some(property_animation) => {
|
||||
debug!(
|
||||
"Animation::update_style: got property animation for prop {:?}",
|
||||
property
|
||||
);
|
||||
debug!("Animation::update_style: {:?}", property_animation);
|
||||
property_animation.update(&mut new_style, relative_progress);
|
||||
},
|
||||
None => {
|
||||
debug!(
|
||||
"Animation::update_style: property animation {:?} not animating",
|
||||
property
|
||||
);
|
||||
},
|
||||
}
|
||||
update_style_for_longhand(property);
|
||||
}
|
||||
|
||||
debug!(
|
||||
"Animation::update_style: got style change in animation \"{}\"",
|
||||
self.name
|
||||
);
|
||||
*style = new_style;
|
||||
}
|
||||
}
|
||||
|
@ -574,12 +555,9 @@ impl Transition {
|
|||
|
||||
/// Whether this animation has the same end value as another one.
|
||||
#[inline]
|
||||
fn has_same_end_value(&self, other_animation: &PropertyAnimation) -> bool {
|
||||
if self.state == AnimationState::Canceled {
|
||||
return false;
|
||||
}
|
||||
self.property_animation
|
||||
.has_the_same_end_value_as(other_animation)
|
||||
fn progress(&self, now: f64) -> f64 {
|
||||
let progress = (now - self.start_time) / (self.property_animation.duration);
|
||||
progress.min(1.0)
|
||||
}
|
||||
|
||||
/// Update a style to the value specified by this `Transition` given a `SharedStyleContext`.
|
||||
|
@ -589,9 +567,7 @@ impl Transition {
|
|||
return;
|
||||
}
|
||||
|
||||
let now = context.current_time_for_animations;
|
||||
let progress = (now - self.start_time) / (self.property_animation.duration);
|
||||
let progress = progress.min(1.0);
|
||||
let progress = self.progress(context.current_time_for_animations);
|
||||
if progress >= 0.0 {
|
||||
self.property_animation.update(style, progress);
|
||||
}
|
||||
|
@ -783,7 +759,8 @@ pub fn start_transitions_if_applicable(
|
|||
) -> LonghandIdSet {
|
||||
// If the style of this element is display:none, then we don't start any transitions
|
||||
// and we cancel any currently running transitions by returning an empty LonghandIdSet.
|
||||
if new_style.get_box().clone_display().is_none() {
|
||||
let box_style = new_style.get_box();
|
||||
if box_style.clone_display().is_none() {
|
||||
return LonghandIdSet::new();
|
||||
}
|
||||
|
||||
|
@ -798,12 +775,8 @@ pub fn start_transitions_if_applicable(
|
|||
|
||||
let property_animation = match PropertyAnimation::from_longhand(
|
||||
transition.longhand_id,
|
||||
new_style
|
||||
.get_box()
|
||||
.transition_timing_function_mod(transition.index),
|
||||
new_style
|
||||
.get_box()
|
||||
.transition_duration_mod(transition.index),
|
||||
box_style.transition_timing_function_mod(transition.index),
|
||||
box_style.transition_duration_mod(transition.index),
|
||||
old_style,
|
||||
new_style,
|
||||
) {
|
||||
|
@ -813,12 +786,13 @@ pub fn start_transitions_if_applicable(
|
|||
|
||||
// Per [1], don't trigger a new transition if the end state for that
|
||||
// transition is the same as that of a transition that's running or
|
||||
// completed.
|
||||
// completed. We don't take into account any canceled animations.
|
||||
// [1]: https://drafts.csswg.org/css-transitions/#starting
|
||||
if animation_state
|
||||
.transitions
|
||||
.iter()
|
||||
.any(|transition| transition.has_same_end_value(&property_animation))
|
||||
.filter(|transition| transition.state != AnimationState::Canceled)
|
||||
.any(|transition| transition.property_animation.to == property_animation.to)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -57,132 +57,6 @@ impl From<nsCSSPropertyID> for TransitionProperty {
|
|||
}
|
||||
}
|
||||
|
||||
/// An animated property interpolation between two computed values for that
|
||||
/// property.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
||||
pub enum AnimatedProperty {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable and not prop.logical:
|
||||
<%
|
||||
value_type = "longhands::{}::computed_value::T".format(prop.ident)
|
||||
if not prop.is_animatable_with_computed_value:
|
||||
value_type = "<{} as ToAnimatedValue>::AnimatedValue".format(value_type)
|
||||
%>
|
||||
/// ${prop.name}
|
||||
${prop.camel_case}(${value_type}, ${value_type}),
|
||||
% endif
|
||||
% endfor
|
||||
}
|
||||
|
||||
impl AnimatedProperty {
|
||||
/// Get the id of the property we're animating.
|
||||
pub fn id(&self) -> LonghandId {
|
||||
match *self {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable and not prop.logical:
|
||||
AnimatedProperty::${prop.camel_case}(..) => LonghandId::${prop.camel_case},
|
||||
% endif
|
||||
% endfor
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the name of this property.
|
||||
pub fn name(&self) -> &'static str {
|
||||
self.id().name()
|
||||
}
|
||||
|
||||
/// Whether this interpolation does animate, that is, whether the start and
|
||||
/// end values are different.
|
||||
pub fn does_animate(&self) -> bool {
|
||||
match *self {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable and not prop.logical:
|
||||
AnimatedProperty::${prop.camel_case}(ref from, ref to) => from != to,
|
||||
% endif
|
||||
% endfor
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether an animated property has the same end value as another.
|
||||
pub fn has_the_same_end_value_as(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable and not prop.logical:
|
||||
(&AnimatedProperty::${prop.camel_case}(_, ref this_end_value),
|
||||
&AnimatedProperty::${prop.camel_case}(_, ref other_end_value)) => {
|
||||
this_end_value == other_end_value
|
||||
}
|
||||
% endif
|
||||
% endfor
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update `style` with the proper computed style corresponding to this
|
||||
/// animation at `progress`.
|
||||
#[cfg_attr(feature = "gecko", allow(unused))]
|
||||
pub fn update(&self, style: &mut ComputedValues, progress: f64) {
|
||||
#[cfg(feature = "servo")]
|
||||
{
|
||||
match *self {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable and not prop.logical:
|
||||
AnimatedProperty::${prop.camel_case}(ref from, ref to) => {
|
||||
// https://drafts.csswg.org/web-animations/#discrete-animation-type
|
||||
% if prop.animation_value_type == "discrete":
|
||||
let value = if progress < 0.5 { from.clone() } else { to.clone() };
|
||||
% else:
|
||||
let value = match from.animate(to, Procedure::Interpolate { progress }) {
|
||||
Ok(value) => value,
|
||||
Err(()) => return,
|
||||
};
|
||||
% endif
|
||||
% if not prop.is_animatable_with_computed_value:
|
||||
let value: longhands::${prop.ident}::computed_value::T =
|
||||
ToAnimatedValue::from_animated_value(value);
|
||||
% endif
|
||||
style.mutate_${prop.style_struct.name_lower}().set_${prop.ident}(value);
|
||||
}
|
||||
% endif
|
||||
% endfor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an animatable value from a transition-property, an old style, and a
|
||||
/// new style.
|
||||
pub fn from_longhand(
|
||||
property: LonghandId,
|
||||
old_style: &ComputedValues,
|
||||
new_style: &ComputedValues,
|
||||
) -> Option<AnimatedProperty> {
|
||||
// FIXME(emilio): Handle the case where old_style and new_style's
|
||||
// writing mode differ.
|
||||
let property = property.to_physical(new_style.writing_mode);
|
||||
Some(match property {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable and not prop.logical:
|
||||
LonghandId::${prop.camel_case} => {
|
||||
let old_computed = old_style.clone_${prop.ident}();
|
||||
let new_computed = new_style.clone_${prop.ident}();
|
||||
AnimatedProperty::${prop.camel_case}(
|
||||
% if prop.is_animatable_with_computed_value:
|
||||
old_computed,
|
||||
new_computed,
|
||||
% else:
|
||||
old_computed.to_animated_value(),
|
||||
new_computed.to_animated_value(),
|
||||
% endif
|
||||
)
|
||||
}
|
||||
% endif
|
||||
% endfor
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of AnimationValue that were composed on an element.
|
||||
/// This HashMap stores the values that are the last AnimationValue to be
|
||||
/// composed for each TransitionProperty.
|
||||
|
@ -192,11 +66,6 @@ pub type AnimationValueMap = FxHashMap<LonghandId, AnimationValue>;
|
|||
/// property in order to be interpolated with another one. When interpolating,
|
||||
/// both values need to belong to the same property.
|
||||
///
|
||||
/// This is different to AnimatedProperty in the sense that AnimatedProperty
|
||||
/// also knows the final value to be used during the animation.
|
||||
///
|
||||
/// This is to be used in Gecko integration code.
|
||||
///
|
||||
/// FIXME: We need to add a path for custom properties, but that's trivial after
|
||||
/// this (is a similar path to that of PropertyDeclaration).
|
||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
||||
|
@ -546,6 +415,30 @@ impl AnimationValue {
|
|||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Update `style` with the value of this `AnimationValue`.
|
||||
///
|
||||
/// SERVO ONLY: This doesn't properly handle things like updating 'em' units
|
||||
/// when animated font-size.
|
||||
pub fn set_in_style_for_servo(&self, style: &mut ComputedValues) {
|
||||
match self {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable and not prop.logical:
|
||||
AnimationValue::${prop.camel_case}(ref value) => {
|
||||
% if not prop.is_animatable_with_computed_value:
|
||||
let value: longhands::${prop.ident}::computed_value::T =
|
||||
ToAnimatedValue::from_animated_value(value.clone());
|
||||
style.mutate_${prop.style_struct.name_lower}().set_${prop.ident}(value);
|
||||
% else:
|
||||
style.mutate_${prop.style_struct.name_lower}().set_${prop.ident}(value.clone());
|
||||
% endif
|
||||
}
|
||||
% else:
|
||||
AnimationValue::${prop.camel_case}(..) => unreachable!(),
|
||||
% endif
|
||||
% endfor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn animate_discrete<T: Clone>(this: &T, other: &T, procedure: Procedure) -> Result<T, ()> {
|
||||
|
|
Загрузка…
Ссылка в новой задаче