style: layout: Get actual keyframes working!

There are a few shortcomings, for example:

 * We don't do the same as other browsers when some properties are not specified
   in some of the keyframes, though this is easy to work out (though a bit more
   expensive in the sense that we should apply all the previous keyframes' style
   instead of just the previous and the next.

 * To trigger the initial animation, a restyle is necessary. Should be easy to
   do an initial restyle off-hand or something like that, but for now this is
   worked-around adding a :hover rule to the node.

   Also, the animation is resetted when the node is hovered. That's a bug, but
   is probably not so difficult to test.

 * A few things, mainly animation-direction, are not supported yet, but
   shouldn't be that hard to support.

Still a lot of work to do, but I think this approach might be ok.
This commit is contained in:
Emilio Cobos Álvarez 2016-06-21 00:02:25 +02:00
parent bc970596d6
commit cb3da24f08
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
2 changed files with 68 additions and 16 deletions

View file

@ -26,7 +26,44 @@ pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
pipeline_id: PipelineId) {
let mut new_running_animations = vec![];
while let Ok(animation) = new_animations_receiver.try_recv() {
new_running_animations.push(animation)
let should_push = match animation {
Animation::Transition(..) => true,
Animation::Keyframes(ref node, ref name, ref state) => {
// If the animation was already present in the list for the
// node, just update its state, else push the new animation to
// run.
if let Some(ref mut animations) = running_animations.get_mut(node) {
// TODO: This being linear is probably not optimal.
match animations.iter_mut().find(|anim| match **anim {
Animation::Keyframes(_, ref anim_name, _) => *name == *anim_name,
Animation::Transition(..) => false,
}) {
Some(mut anim) => {
debug!("update_animation_state: Found other animation {}", name);
match *anim {
Animation::Keyframes(_, _, ref mut anim_state) => {
// NB: The important part is not touching
// the started_at field.
anim_state.duration = state.duration;
anim_state.iteration_state = state.iteration_state.clone();
anim_state.paused = state.paused;
anim_state.delay = state.delay;
false
}
_ => unreachable!(),
}
}
None => true,
}
} else {
true
}
}
};
if should_push {
new_running_animations.push(animation);
}
}
if running_animations.is_empty() && new_running_animations.is_empty() {
@ -54,8 +91,9 @@ pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
*current += 1;
*current < *max
}
// Just tick it again.
KeyframesIterationState::Infinite => {
state.started_at += state.duration;
state.started_at += state.duration + state.delay;
true
}
}

View file

@ -41,10 +41,12 @@ pub enum KeyframesIterationState {
/// This structure represents the current keyframe animation state, i.e., the
/// duration, the current and maximum iteration count, and the state (either
/// playing or paused).
// TODO: unify the use of f32/f64 in this file.
#[derive(Debug, Clone)]
pub struct KeyframesAnimationState {
pub started_at: f64,
pub duration: f64,
pub delay: f64,
pub iteration_state: KeyframesIterationState,
pub paused: bool,
}
@ -294,6 +296,7 @@ pub fn maybe_start_animations<Impl: SelectorImplExt>(context: &SharedStyleContex
.send(Animation::Keyframes(node, name.clone(), KeyframesAnimationState {
started_at: animation_start,
duration: duration as f64,
delay: delay as f64,
iteration_state: iteration_state,
paused: paused,
})).unwrap();
@ -383,36 +386,46 @@ where Impl: SelectorImplExt,
let mut total_progress = (now - started_at) / total_duration;
if total_progress < 0. {
warn!("Negative progress found for animation {:?}", name);
return;
}
if total_progress > 1. {
total_progress = 1.;
}
debug!("update_style_for_animation: anim \"{}\", steps: {:?}, state: {:?}, progress: {}", name, animation.steps, state, total_progress);
let mut last_keyframe = None;
let mut target_keyframe = None;
let mut last_keyframe_position = None;
let mut target_keyframe_position = None;
// TODO: we could maybe binary-search this?
for i in 1..animation.steps.len() {
// TODO: we could maybe binary-search this? Also, find is probably a
// bit more idiomatic here?
for i in 0..animation.steps.len() {
if total_progress as f32 <= animation.steps[i].start_percentage.0 {
// We might have found our current keyframe.
last_keyframe = target_keyframe;
target_keyframe = Some(&animation.steps[i]);
target_keyframe_position = Some(i);
if i != 0 {
last_keyframe_position = Some(i - 1);
}
break;
}
}
let target_keyframe = match target_keyframe {
Some(current) => current,
debug!("update_style_for_animation: keyframe from {:?} to {:?}", last_keyframe_position, target_keyframe_position);
let target_keyframe = match target_keyframe_position {
Some(target) => &animation.steps[target],
None => {
warn!("update_style_for_animation: No current keyframe found for animation {:?} at progress {}", name, total_progress);
// TODO: The 0. case falls here, maybe we should just resort
// to the first keyframe instead.
warn!("update_style_for_animation: No current keyframe found for animation \"{}\" at progress {}", name, total_progress);
return;
}
};
let last_keyframe = match last_keyframe {
Some(last_keyframe) => last_keyframe,
let last_keyframe = match last_keyframe_position {
Some(last) => &animation.steps[last],
None => {
warn!("update_style_for_animation: No last keyframe found for animation {:?} at progress {}", name, total_progress);
warn!("update_style_for_animation: No last keyframe found for animation \"{}\" at progress {}", name, total_progress);
return;
}
};
@ -441,20 +454,21 @@ where Impl: SelectorImplExt,
let mut style_changed = false;
for transition_property in &animation.properties_changed {
debug!("update_style_for_animation: scanning prop {:?} for animation {}", transition_property, name);
debug!("update_style_for_animation: scanning prop {:?} for animation \"{}\"", transition_property, name);
if let Some(property_animation) = PropertyAnimation::from_transition_property(*transition_property,
timing_function,
Time(relative_duration as f32),
&from_style,
&target_style) {
debug!("update_style_for_animation: got property animation for prop {:?}", transition_property);
debug!("update_style_for_animation: {:?}", property_animation);
property_animation.update(Arc::make_mut(&mut new_style), relative_progress);
style_changed = true;
}
}
if style_changed {
debug!("update_style_for_animation: got style change in animation {:?}", name);
debug!("update_style_for_animation: got style change in animation \"{}\"", name);
if let Some(damage) = damage {
*damage = *damage | Damage::compute(Some(style), &new_style);
}