Skip to content

Commit

Permalink
Add support for remaining animation and transition events
Browse files Browse the repository at this point in the history
Fixes #21564.
  • Loading branch information
mrobinson committed May 26, 2020
1 parent 47642e0 commit 77aa372
Show file tree
Hide file tree
Showing 28 changed files with 492 additions and 67 deletions.
2 changes: 2 additions & 0 deletions components/atoms/static_atoms.txt
Expand Up @@ -2,6 +2,7 @@ DOMContentLoaded
abort
activate
addtrack
animationcancel
animationend
animationiteration
animationstart
Expand Down Expand Up @@ -132,6 +133,7 @@ track
transitioncancel
transitionend
transitionrun
transitionstart
unhandledrejection
unload
url
Expand Down
97 changes: 91 additions & 6 deletions components/script/animations.rs
Expand Up @@ -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,
);
}
Expand All @@ -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();
Expand Down Expand Up @@ -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(
Expand All @@ -154,6 +189,7 @@ impl Animations {
self.add_animation_event(
animation,
TransitionOrAnimationEventType::AnimationEnd,
now,
pipeline_id,
);
}
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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()
Expand All @@ -291,18 +344,33 @@ impl Animations {
&self,
animation: &Animation,
event_type: TransitionOrAnimationEventType,
now: f64,
pipeline_id: PipelineId,
) {
let num_iterations = match animation.iteration_state {
KeyframesIterationState::Finite(current, _) |
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()
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
}
}
}
Expand Down

0 comments on commit 77aa372

Please sign in to comment.