diff options
author | Martin Robinson <mrobinson@igalia.com> | 2020-05-26 20:34:58 +0200 |
---|---|---|
committer | Martin Robinson <mrobinson@igalia.com> | 2020-05-26 20:34:58 +0200 |
commit | 77aa3721c5221eea9acb064b8c9fd559ee2a6c14 (patch) | |
tree | 8d819866535b0c2edfdd876f4385c970de76a1de /components/script/animations.rs | |
parent | 47642e0eee1db349d5b933c0cafc4088818db9a0 (diff) | |
download | servo-77aa3721c5221eea9acb064b8c9fd559ee2a6c14.tar.gz servo-77aa3721c5221eea9acb064b8c9fd559ee2a6c14.zip |
Add support for remaining animation and transition events
Fixes #21564.
Diffstat (limited to 'components/script/animations.rs')
-rw-r--r-- | components/script/animations.rs | 97 |
1 files changed, 91 insertions, 6 deletions
diff --git a/components/script/animations.rs b/components/script/animations.rs index f9de0fc0a32..d0bfcef6f79 100644 --- a/components/script/animations.rs +++ b/components/script/animations.rs @@ -78,12 +78,15 @@ impl Animations { let mut sets = self.sets.write(); for set in sets.values_mut() { + self.start_pending_animations(set, now, pipeline_id); + // When necessary, iterate our running animations to the next iteration. for animation in set.animations.iter_mut() { if animation.iterate_if_necessary(now) { self.add_animation_event( animation, TransitionOrAnimationEventType::AnimationIteration, + now, pipeline_id, ); } @@ -97,7 +100,6 @@ impl Animations { /// Processes any new animations that were discovered after reflow. Collect messages /// that trigger events for any animations that changed state. - /// TODO(mrobinson): The specification dictates that this should happen before reflow. pub(crate) fn do_post_reflow_update(&self, window: &Window, now: f64) { let pipeline_id = window.pipeline_id(); let mut sets = self.sets.write(); @@ -140,6 +142,39 @@ impl Animations { .sum() } + /// Walk through the list of pending animations and start all of the ones that + /// have left the delay phase. + fn start_pending_animations( + &self, + set: &mut ElementAnimationSet, + now: f64, + pipeline_id: PipelineId, + ) { + for animation in set.animations.iter_mut() { + if animation.state == AnimationState::Pending && animation.started_at <= now { + animation.state = AnimationState::Running; + self.add_animation_event( + animation, + TransitionOrAnimationEventType::AnimationStart, + now, + pipeline_id, + ); + } + } + + for transition in set.transitions.iter_mut() { + if transition.state == AnimationState::Pending && transition.start_time <= now { + transition.state = AnimationState::Running; + self.add_transition_event( + transition, + TransitionOrAnimationEventType::TransitionStart, + now, + pipeline_id, + ); + } + } + } + /// Walk through the list of running animations and remove all of the ones that /// have ended. fn finish_running_animations( @@ -154,6 +189,7 @@ impl Animations { self.add_animation_event( animation, TransitionOrAnimationEventType::AnimationEnd, + now, pipeline_id, ); } @@ -192,7 +228,17 @@ impl Animations { } } - // TODO(mrobinson): We need to send animationcancel events. + for animation in &set.animations { + if animation.state == AnimationState::Canceled { + self.add_animation_event( + animation, + TransitionOrAnimationEventType::AnimationCancel, + now, + pipeline_id, + ); + } + } + set.clear_canceled_animations(); } @@ -263,14 +309,21 @@ impl Animations { now: f64, pipeline_id: PipelineId, ) { + // Calculate the `elapsed-time` property of the event and take the absolute + // value to prevent -0 values. let elapsed_time = match event_type { TransitionOrAnimationEventType::TransitionRun | + TransitionOrAnimationEventType::TransitionStart => transition + .property_animation + .duration + .min((-transition.delay).max(0.)), TransitionOrAnimationEventType::TransitionEnd => transition.property_animation.duration, TransitionOrAnimationEventType::TransitionCancel => { (now - transition.start_time).max(0.) }, _ => unreachable!(), - }; + } + .abs(); self.pending_events .borrow_mut() @@ -291,6 +344,7 @@ impl Animations { &self, animation: &Animation, event_type: TransitionOrAnimationEventType, + now: f64, pipeline_id: PipelineId, ) { let num_iterations = match animation.iteration_state { @@ -298,11 +352,25 @@ impl Animations { KeyframesIterationState::Infinite(current) => current, }; + let active_duration = match animation.iteration_state { + KeyframesIterationState::Finite(_, max) => max * animation.duration, + KeyframesIterationState::Infinite(_) => std::f64::MAX, + }; + + // Calculate the `elapsed-time` property of the event and take the absolute + // value to prevent -0 values. let elapsed_time = match event_type { + TransitionOrAnimationEventType::AnimationStart => { + (-animation.delay).max(0.).min(active_duration) + }, TransitionOrAnimationEventType::AnimationIteration | TransitionOrAnimationEventType::AnimationEnd => num_iterations * animation.duration, + TransitionOrAnimationEventType::AnimationCancel => { + (num_iterations * animation.duration) + (now - animation.started_at).max(0.) + }, _ => unreachable!(), - }; + } + .abs(); self.pending_events .borrow_mut() @@ -333,10 +401,13 @@ impl Animations { let event_atom = match event.event_type { TransitionOrAnimationEventType::AnimationEnd => atom!("animationend"), + TransitionOrAnimationEventType::AnimationStart => atom!("animationstart"), + TransitionOrAnimationEventType::AnimationCancel => atom!("animationcancel"), TransitionOrAnimationEventType::AnimationIteration => atom!("animationiteration"), TransitionOrAnimationEventType::TransitionCancel => atom!("transitioncancel"), TransitionOrAnimationEventType::TransitionEnd => atom!("transitionend"), TransitionOrAnimationEventType::TransitionRun => atom!("transitionrun"), + TransitionOrAnimationEventType::TransitionStart => atom!("transitionstart"), }; let parent = EventInit { bubbles: true, @@ -381,25 +452,39 @@ pub enum TransitionOrAnimationEventType { /// "The transitionrun event occurs when a transition is created (i.e., when it /// is added to the set of running transitions)." TransitionRun, + /// "The transitionstart event occurs when a transition’s delay phase ends." + TransitionStart, /// "The transitionend event occurs at the completion of the transition. In the /// case where a transition is removed before completion, such as if the /// transition-property is removed, then the event will not fire." TransitionEnd, /// "The transitioncancel event occurs when a transition is canceled." TransitionCancel, + /// "The animationstart event occurs at the start of the animation. If there is + /// an animation-delay then this event will fire once the delay period has expired." + AnimationStart, /// "The animationiteration event occurs at the end of each iteration of an /// animation, except when an animationend event would fire at the same time." AnimationIteration, /// "The animationend event occurs when the animation finishes" AnimationEnd, + /// "The animationcancel event occurs when the animation stops running in a way + /// that does not fire an animationend event..." + AnimationCancel, } impl TransitionOrAnimationEventType { /// Whether or not this event is a transition-related event. pub fn is_transition_event(&self) -> bool { match *self { - Self::TransitionRun | Self::TransitionEnd | Self::TransitionCancel => true, - Self::AnimationEnd | Self::AnimationIteration => false, + Self::TransitionRun | + Self::TransitionEnd | + Self::TransitionCancel | + Self::TransitionStart => true, + Self::AnimationEnd | + Self::AnimationIteration | + Self::AnimationStart | + Self::AnimationCancel => false, } } } |