mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
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:
parent
bc970596d6
commit
cb3da24f08
2 changed files with 68 additions and 16 deletions
|
@ -26,7 +26,44 @@ pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
|
||||||
pipeline_id: PipelineId) {
|
pipeline_id: PipelineId) {
|
||||||
let mut new_running_animations = vec![];
|
let mut new_running_animations = vec![];
|
||||||
while let Ok(animation) = new_animations_receiver.try_recv() {
|
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() {
|
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 += 1;
|
||||||
*current < *max
|
*current < *max
|
||||||
}
|
}
|
||||||
|
// Just tick it again.
|
||||||
KeyframesIterationState::Infinite => {
|
KeyframesIterationState::Infinite => {
|
||||||
state.started_at += state.duration;
|
state.started_at += state.duration + state.delay;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,10 +41,12 @@ pub enum KeyframesIterationState {
|
||||||
/// This structure represents the current keyframe animation state, i.e., the
|
/// This structure represents the current keyframe animation state, i.e., the
|
||||||
/// duration, the current and maximum iteration count, and the state (either
|
/// duration, the current and maximum iteration count, and the state (either
|
||||||
/// playing or paused).
|
/// playing or paused).
|
||||||
|
// TODO: unify the use of f32/f64 in this file.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct KeyframesAnimationState {
|
pub struct KeyframesAnimationState {
|
||||||
pub started_at: f64,
|
pub started_at: f64,
|
||||||
pub duration: f64,
|
pub duration: f64,
|
||||||
|
pub delay: f64,
|
||||||
pub iteration_state: KeyframesIterationState,
|
pub iteration_state: KeyframesIterationState,
|
||||||
pub paused: bool,
|
pub paused: bool,
|
||||||
}
|
}
|
||||||
|
@ -294,6 +296,7 @@ pub fn maybe_start_animations<Impl: SelectorImplExt>(context: &SharedStyleContex
|
||||||
.send(Animation::Keyframes(node, name.clone(), KeyframesAnimationState {
|
.send(Animation::Keyframes(node, name.clone(), KeyframesAnimationState {
|
||||||
started_at: animation_start,
|
started_at: animation_start,
|
||||||
duration: duration as f64,
|
duration: duration as f64,
|
||||||
|
delay: delay as f64,
|
||||||
iteration_state: iteration_state,
|
iteration_state: iteration_state,
|
||||||
paused: paused,
|
paused: paused,
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
|
@ -383,36 +386,46 @@ where Impl: SelectorImplExt,
|
||||||
let mut total_progress = (now - started_at) / total_duration;
|
let mut total_progress = (now - started_at) / total_duration;
|
||||||
if total_progress < 0. {
|
if total_progress < 0. {
|
||||||
warn!("Negative progress found for animation {:?}", name);
|
warn!("Negative progress found for animation {:?}", name);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if total_progress > 1. {
|
if total_progress > 1. {
|
||||||
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 last_keyframe_position = None;
|
||||||
let mut target_keyframe = None;
|
let mut target_keyframe_position = None;
|
||||||
|
|
||||||
// TODO: we could maybe binary-search this?
|
// TODO: we could maybe binary-search this? Also, find is probably a
|
||||||
for i in 1..animation.steps.len() {
|
// bit more idiomatic here?
|
||||||
|
for i in 0..animation.steps.len() {
|
||||||
if total_progress as f32 <= animation.steps[i].start_percentage.0 {
|
if total_progress as f32 <= animation.steps[i].start_percentage.0 {
|
||||||
// We might have found our current keyframe.
|
// We might have found our current keyframe.
|
||||||
last_keyframe = target_keyframe;
|
target_keyframe_position = Some(i);
|
||||||
target_keyframe = Some(&animation.steps[i]);
|
if i != 0 {
|
||||||
|
last_keyframe_position = Some(i - 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let target_keyframe = match target_keyframe {
|
debug!("update_style_for_animation: keyframe from {:?} to {:?}", last_keyframe_position, target_keyframe_position);
|
||||||
Some(current) => current,
|
|
||||||
|
let target_keyframe = match target_keyframe_position {
|
||||||
|
Some(target) => &animation.steps[target],
|
||||||
None => {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let last_keyframe = match last_keyframe {
|
let last_keyframe = match last_keyframe_position {
|
||||||
Some(last_keyframe) => last_keyframe,
|
Some(last) => &animation.steps[last],
|
||||||
None => {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -441,20 +454,21 @@ where Impl: SelectorImplExt,
|
||||||
let mut style_changed = false;
|
let mut style_changed = false;
|
||||||
|
|
||||||
for transition_property in &animation.properties_changed {
|
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,
|
if let Some(property_animation) = PropertyAnimation::from_transition_property(*transition_property,
|
||||||
timing_function,
|
timing_function,
|
||||||
Time(relative_duration as f32),
|
Time(relative_duration as f32),
|
||||||
&from_style,
|
&from_style,
|
||||||
&target_style) {
|
&target_style) {
|
||||||
debug!("update_style_for_animation: got property animation for prop {:?}", transition_property);
|
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);
|
property_animation.update(Arc::make_mut(&mut new_style), relative_progress);
|
||||||
style_changed = true;
|
style_changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if style_changed {
|
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 {
|
if let Some(damage) = damage {
|
||||||
*damage = *damage | Damage::compute(Some(style), &new_style);
|
*damage = *damage | Damage::compute(Some(style), &new_style);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue