Skip to content

Commit

Permalink
Animation fixes (#599)
Browse files Browse the repository at this point in the history
* refactor(animation): remove unused modules and code in animation.rs

* fix(animations): fix a few behaviors and move animation hide state change

The animation state progression is now driven from the style pass instead of layout which makes more sense and fixes a few issues.

There are still a few bugs. I think that animations should track the on_create as well as on_remove. The state transitions will be better

* fix clippy
  • Loading branch information
jrmoulton authored Sep 30, 2024
1 parent b3d7ac6 commit 1f1e279
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 153 deletions.
2 changes: 1 addition & 1 deletion examples/animations/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fn app_view() -> impl IntoView {
let animation = RwSignal::new(
Animation::new()
.duration(5.seconds())
.keyframe(0, |kf| kf.computed())
.keyframe(0, |kf| kf.computed_style())
.keyframe(50, |kf| {
kf.style(|s| s.background(Color::BLACK).size(30, 30))
.ease_in()
Expand Down
187 changes: 158 additions & 29 deletions src/animate/animation.rs → src/animate.rs

Large diffs are not rendered by default.

52 changes: 0 additions & 52 deletions src/animate/anim_state.rs

This file was deleted.

8 changes: 0 additions & 8 deletions src/animate/mod.rs

This file was deleted.

102 changes: 65 additions & 37 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use web_time::{Duration, Instant};

use taffy::prelude::NodeId;

use crate::animate::RepeatMode;
use crate::animate::{AnimStateKind, RepeatMode};
use crate::style::DisplayProp;
use crate::view_state::IsHiddenState;
use crate::{
Expand Down Expand Up @@ -634,6 +634,36 @@ impl<'a> StyleCx<'a> {

view.borrow_mut().style_pass(self);

let mut is_hidden_state = view_state.borrow().is_hidden_state;
let computed_display = view_state.borrow().combined_style.get(DisplayProp);
is_hidden_state.transition(
computed_display,
|| {
let count = animations_on_remove(view_id, Scope::current());
view_state.borrow_mut().num_waiting_animations = count;
count > 0
},
|| {
animations_on_create(view_id);
},
|| {
stop_reset_remove_animations(view_id);
},
|| view_state.borrow().num_waiting_animations,
);
// if request_layout {
// view_id.request_layout();
// }

view_state.borrow_mut().is_hidden_state = is_hidden_state;
let modified = view_state
.borrow()
.combined_style
.clone()
.apply_opt(is_hidden_state.get_display(), Style::display);

view_state.borrow_mut().combined_style = modified;

self.restore();
}

Expand Down Expand Up @@ -741,38 +771,11 @@ impl<'a> ComputeLayoutCx<'a> {
pub fn compute_view_layout(&mut self, id: ViewId) -> Option<Rect> {
let view_state = id.state();

let mut is_hidden_state = view_state.borrow().is_hidden_state;
let display = view_state.borrow().combined_style.get(DisplayProp);
let request_layout = is_hidden_state.transition(
display,
|| {
let count = animations_recursive_on_remove(id, Scope::current());
view_state.borrow_mut().num_waiting_animations = count;
count > 0
},
|| {
animations_recursive_on_create(id);
},
|| view_state.borrow().num_waiting_animations,
);
if request_layout {
id.request_layout();
}

view_state.borrow_mut().is_hidden_state = is_hidden_state;
if is_hidden_state == IsHiddenState::Hidden {
if view_state.borrow().is_hidden_state == IsHiddenState::Hidden {
view_state.borrow_mut().layout_rect = Rect::ZERO;
return None;
}

let modified = view_state
.borrow()
.combined_style
.clone()
.apply_opt(is_hidden_state.get_display(), Style::display);

view_state.borrow_mut().combined_style = modified;

self.save();

let layout = id.get_layout().unwrap_or_default();
Expand Down Expand Up @@ -1300,10 +1303,11 @@ impl DerefMut for PaintCx<'_> {
}
}

fn animations_recursive_on_remove(id: ViewId, scope: Scope) -> u16 {
fn animations_on_remove(id: ViewId, scope: Scope) -> u16 {
let mut wait_for = 0;
let state = id.state();
let mut state = state.borrow_mut();
state.num_waiting_animations = 0;
let animations = &mut state.animations.stack;
let mut request_style = false;
for anim in animations {
Expand All @@ -1326,18 +1330,21 @@ fn animations_recursive_on_remove(id: ViewId, scope: Scope) -> u16 {
id.request_style();
}

id.children().into_iter().fold(wait_for, |acc, id| {
acc + animations_recursive_on_remove(id, scope)
})
id.children()
.into_iter()
.fold(wait_for, |acc, id| acc + animations_on_remove(id, scope))
}

fn animations_recursive_on_create(id: ViewId) {
fn stop_reset_remove_animations(id: ViewId) {
let state = id.state();
let mut state = state.borrow_mut();
let animations = &mut state.animations.stack;
let mut request_style = false;
for anim in animations {
if anim.run_on_create && !matches!(anim.repeat_mode, RepeatMode::LoopForever) {
if anim.run_on_remove
&& anim.state_kind() == AnimStateKind::PassInProgress
&& !matches!(anim.repeat_mode, RepeatMode::LoopForever)
{
anim.reverse_once.set(false);
anim.start_mut();
request_style = true;
}
Expand All @@ -1349,5 +1356,26 @@ fn animations_recursive_on_create(id: ViewId) {

id.children()
.into_iter()
.for_each(animations_recursive_on_create);
.for_each(stop_reset_remove_animations)
}

fn animations_on_create(id: ViewId) {
let state = id.state();
let mut state = state.borrow_mut();
state.num_waiting_animations = 0;
let animations = &mut state.animations.stack;
let mut request_style = false;
for anim in animations {
if anim.run_on_create && !matches!(anim.repeat_mode, RepeatMode::LoopForever) {
anim.reverse_once.set(false);
anim.start_mut();
request_style = true;
}
}
drop(state);
if request_style {
id.request_style();
}

id.children().into_iter().for_each(animations_on_create);
}
2 changes: 2 additions & 0 deletions src/animate/easing.rs → src/easing.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! The Easing trait and the built-in easing functions.

use peniko::kurbo::{ParamCurve, Point};

pub trait Easing: std::fmt::Debug {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ pub(crate) mod app_state;
mod clipboard;
pub mod context;
pub mod dropped_file;
pub mod easing;
pub mod event;
pub mod ext_event;
pub mod file;
Expand Down
2 changes: 1 addition & 1 deletion src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ use taffy::{
},
};

use crate::animate::{Bezier, Easing, Linear, Spring};
use crate::context::InteractionState;
use crate::easing::*;
use crate::responsive::{ScreenSize, ScreenSizeBp};
use crate::unit::{Px, PxPct, PxPctAuto, UnitExt};
use crate::view::{IntoView, View};
Expand Down
56 changes: 31 additions & 25 deletions src/view_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,46 +99,48 @@ impl IsHiddenState {
}
}

// returns true if the view should request layout
pub(crate) fn transition(
&mut self,
display: taffy::Display,
computed_display: taffy::Display,
remove_animations: impl FnOnce() -> bool,
add_animations: impl FnOnce(),
stop_reset_animations: impl FnOnce(),
num_waiting_anim: impl FnOnce() -> u16,
) -> bool {
let hide = display == taffy::Display::None;
let mut request_layout = false;
) {
let computed_has_hide = computed_display == taffy::Display::None;
*self = match self {
Self::None if hide => Self::Hidden,
Self::None if !hide => Self::Visible(display),
Self::Visible(dis) if !hide => Self::Visible(*dis),
Self::Visible(dis) if hide => {
// initial states (makes it so that the animations aren't run on intial app/view load)
Self::None if computed_has_hide => Self::Hidden,
Self::None if !computed_has_hide => Self::Visible(computed_display),
// do nothing
Self::Visible(dis) if !computed_has_hide => Self::Visible(*dis),
// transition to hidden
Self::Visible(dis) if computed_has_hide => {
let active_animations = remove_animations();
if active_animations {
// request_layout = true;
Self::AnimatingOut(*dis)
} else {
Self::Hidden
}
}
Self::AnimatingOut(_) if !hide => Self::Visible(display),
Self::AnimatingOut(dis) if hide => {
Self::AnimatingOut(_) if !computed_has_hide => {
stop_reset_animations();
Self::Visible(computed_display)
}
Self::AnimatingOut(dis) if computed_has_hide => {
if num_waiting_anim() == 0 {
request_layout = true;
Self::Hidden
} else {
Self::AnimatingOut(*dis)
}
}
Self::Hidden if hide => Self::Hidden,
Self::Hidden if !hide => {
Self::Hidden if computed_has_hide => Self::Hidden,
Self::Hidden if !computed_has_hide => {
add_animations();
Self::Visible(display)
Self::Visible(computed_display)
}
_ => unreachable!(),
};
request_layout
}
}

Expand Down Expand Up @@ -225,24 +227,28 @@ impl ViewState {
.apply_classes_from_context(&self.classes, context)
.apply(self.style());

self.has_style_selectors = computed_style.selectors();

computed_style.apply_interact_state(&interact_state, screen_size_bp);

for animation in self
.animations
.stack
.iter_mut()
.filter(|anim| anim.can_advance())
.filter(|anim| anim.can_advance() || anim.should_apply_folded())
{
new_frame = true;
if animation.can_advance() {
new_frame = true;

animation.animate_into(&mut computed_style);
animation.animate_into(&mut computed_style);

animation.advance();
animation.advance();
} else {
animation.apply_folded(&mut computed_style)
}
debug_assert!(!animation.is_idle());
}

self.has_style_selectors = computed_style.selectors();

computed_style.apply_interact_state(&interact_state, screen_size_bp);

self.combined_style = computed_style;

new_frame
Expand Down

0 comments on commit 1f1e279

Please sign in to comment.