Skip to content

Commit

Permalink
animations improvements (#663)
Browse files Browse the repository at this point in the history
  • Loading branch information
jrmoulton authored Nov 5, 2024
1 parent e14eca8 commit fdfb03c
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 66 deletions.
20 changes: 13 additions & 7 deletions examples/animations/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,21 @@ fn app_view() -> impl IntoView {
.style(|s| s.background(Color::RED).size(500, 100))
.animation(move |_| animation.get().duration(10.seconds())),
empty()
.style(|s| s.background(Color::BLUE).size(50, 100))
.style(|s| {
s.background(Color::BLUE)
.size(50, 100)
.border(5.)
.border_color(Color::GREEN)
})
.animation(move |_| animation.get())
.animation(move |a| {
a.keyframe(100, |f| {
f.style(|s| s.border(5.).border_color(Color::PURPLE))
})
.duration(5.seconds())
.repeat(true)
.auto_reverse(true)
a.keyframe(0, |f| f.computed_style())
.keyframe(100, |f| {
f.style(|s| s.border(5.).border_color(Color::PURPLE))
})
.duration(5.seconds())
.repeat(true)
.auto_reverse(true)
}),
empty()
.style(|s| s.background(Color::GREEN).size(100, 300))
Expand Down
48 changes: 24 additions & 24 deletions src/animate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ enum PropFrameKind {
Computed(u16),
}
impl PropFrameKind {
fn inner(self) -> u16 {
const fn inner(self) -> u16 {
match self {
PropFrameKind::Normal(val) => val,
PropFrameKind::Computed(val) => val,
Self::Normal(val) => val,
Self::Computed(val) => val,
}
}
}
Expand Down Expand Up @@ -261,10 +261,10 @@ impl ReverseOnce {
}

/// return true if the animation should be reversing
pub fn is_rev(self) -> bool {
pub const fn is_rev(self) -> bool {
match self {
ReverseOnce::Never => false,
ReverseOnce::Val(v) => v,
Self::Never => false,
Self::Val(v) => v,
}
}
}
Expand Down Expand Up @@ -383,7 +383,7 @@ pub struct Animation {
}
impl Default for Animation {
fn default() -> Self {
Animation {
Self {
state: AnimState::Idle,
effect_states: SmallVec::new(),
auto_reverse: false,
Expand Down Expand Up @@ -526,7 +526,7 @@ impl Animation {
///
/// The total duration of an animation will run until all animating props return `finished`.
/// This is useful for spring animations which don't conform well to strict ending times.
pub fn duration(mut self, duration: Duration) -> Self {
pub const fn duration(mut self, duration: Duration) -> Self {
self.duration = duration;
self
}
Expand Down Expand Up @@ -567,47 +567,47 @@ impl Animation {
/// Set whether this animation should run when being created.
///
/// I.e when being created by a dyn container or when being shown after being hidden.
pub fn run_on_create(mut self, run_on_create: bool) -> Self {
pub const fn run_on_create(mut self, run_on_create: bool) -> Self {
self.run_on_create = run_on_create;
self
}

/// Set whether this animation should run when being created and not when being removed.
pub fn only_on_create(mut self) -> Self {
pub const fn only_on_create(mut self) -> Self {
self.run_on_remove = false;
self.run_on_create = true;
self
}

/// Set whether this animation should run when being removed.
/// I.e when being removed by a dyn container or when being hidden.
pub fn run_on_remove(mut self, run_on_remove: bool) -> Self {
pub const fn run_on_remove(mut self, run_on_remove: bool) -> Self {
self.run_on_remove = run_on_remove;
self
}

/// Set whether this animation should run when being removed and not when being created.
pub fn only_on_remove(mut self) -> Self {
pub const fn only_on_remove(mut self) -> Self {
self.run_on_remove = true;
self.run_on_create = false;
self
}

/// Set whether the properties from the final keyframe of this animation should be applied even when the animation is finished.
pub fn apply_when_finished(mut self, apply: bool) -> Self {
pub const fn apply_when_finished(mut self, apply: bool) -> Self {
self.apply_when_finished = apply;
self
}

/// Sets if this animation should auto reverse.
/// If true, the animation will reach the final key frame twice as fast and then animate backwards
pub fn auto_reverse(mut self, auto_rev: bool) -> Self {
pub const fn auto_reverse(mut self, auto_rev: bool) -> Self {
self.auto_reverse = auto_rev;
self
}

/// Sets if this animation should be allowed to be reversed when the view is being removed or hidden.
pub fn reverse_on_exit(mut self, allow: bool) -> Self {
pub const fn reverse_on_exit(mut self, allow: bool) -> Self {
if allow {
self.reverse_once = ReverseOnce::Val(false);
} else {
Expand All @@ -617,13 +617,13 @@ impl Animation {
}

/// Sets a delay for how long the animation should wait before starting.
pub fn delay(mut self, delay: Duration) -> Self {
pub const fn delay(mut self, delay: Duration) -> Self {
self.delay = delay;
self
}

/// Sets if the animation should the repeat forever.
pub fn repeat(mut self, repeat: bool) -> Self {
pub const fn repeat(mut self, repeat: bool) -> Self {
self.repeat_mode = if repeat {
RepeatMode::LoopForever
} else {
Expand All @@ -633,7 +633,7 @@ impl Animation {
}

/// Sets the number of times the animation should repeat.
pub fn repeat_times(mut self, times: usize) -> Self {
pub const fn repeat_times(mut self, times: usize) -> Self {
self.repeat_mode = RepeatMode::Times(times);
self
}
Expand All @@ -645,7 +645,7 @@ impl Animation {
/// If you need more than 100 keyframes, increase this number, but be aware, the keyframe numbers will then be as a percentage of the maximum.
///
/// *This does not move existing keyframes.*
pub fn max_key_frame(mut self, max: u16) -> Self {
pub const fn max_key_frame(mut self, max: u16) -> Self {
self.max_key_frame_num = max;
self
}
Expand Down Expand Up @@ -752,7 +752,7 @@ impl Animation {
}

/// Matches the current state of the animation and returns the kind of state it is in.
pub fn state_kind(&self) -> AnimStateKind {
pub const fn state_kind(&self) -> AnimStateKind {
match self.state {
AnimState::Idle => AnimStateKind::Idle,
AnimState::Stopped => AnimStateKind::Stopped,
Expand Down Expand Up @@ -1026,7 +1026,7 @@ impl Animation {
}
}

pub(crate) fn apply_folded(&mut self, computed_style: &mut Style) {
pub(crate) fn apply_folded(&self, computed_style: &mut Style) {
computed_style.apply_mut(self.folded_style.clone());
}

Expand Down Expand Up @@ -1157,7 +1157,7 @@ impl Animation {
}

/// returns true if the animation can advance, which either means the animation will transition states, or properties can be animated and updated
pub fn can_advance(&self) -> bool {
pub const fn can_advance(&self) -> bool {
match self.state_kind() {
AnimStateKind::PassFinished | AnimStateKind::PassInProgress | AnimStateKind::Idle => {
true
Expand All @@ -1167,14 +1167,14 @@ impl Animation {
}

/// returns true if the animation should auto reverse
pub fn is_auto_reverse(&self) -> bool {
pub const fn is_auto_reverse(&self) -> bool {
self.auto_reverse
}

/// returns true if the internal folded style of the animation should be applied.
///
/// This is used when the animation cannot advance but the folded style should still be applied.
pub(crate) fn should_apply_folded(&self) -> bool {
pub(crate) const fn should_apply_folded(&self) -> bool {
self.apply_when_finished
|| match self.state_kind() {
AnimStateKind::Paused => true,
Expand Down
75 changes: 40 additions & 35 deletions src/easing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ impl Step {
step_position: StepPosition::End,
};

pub fn new(num_steps: usize, step_position: StepPosition) -> Self {
Step {
pub const fn new(num_steps: usize, step_position: StepPosition) -> Self {
Self {
num_steps,
step_position,
}
}

pub fn new_end(num_steps: usize) -> Self {
pub const fn new_end(num_steps: usize) -> Self {
Self {
num_steps,
step_position: StepPosition::End,
Expand All @@ -86,7 +86,10 @@ impl Easing for Step {
}
StepPosition::None => {
let step_size = 1.0 / self.num_steps as f64;
((time / step_size).floor() * step_size + step_size / 2.0).min(1.0)
(time / step_size)
.floor()
.mul_add(step_size, step_size / 2.0)
.min(1.0)
}
StepPosition::Both => {
let step_size = 1.0 / (self.num_steps - 1) as f64;
Expand All @@ -100,20 +103,20 @@ impl Easing for Step {
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct Bezier(pub f64, pub f64, pub f64, pub f64);
impl Bezier {
const EASE: Self = Bezier(0.25, 0.1, 0.25, 1.);
const EASE_IN: Self = Bezier(0.42, 0., 1., 1.);
const EASE_OUT: Self = Bezier(0., 0., 0.58, 1.);
const EASE_IN_OUT: Self = Bezier(0.42, 0., 0.58, 1.);
pub fn ease() -> Self {
const EASE: Self = Self(0.25, 0.1, 0.25, 1.);
const EASE_IN: Self = Self(0.42, 0., 1., 1.);
const EASE_OUT: Self = Self(0., 0., 0.58, 1.);
const EASE_IN_OUT: Self = Self(0.42, 0., 0.58, 1.);
pub const fn ease() -> Self {
Self::EASE
}
pub fn ease_in() -> Self {
pub const fn ease_in() -> Self {
Self::EASE_IN
}
pub fn ease_out() -> Self {
pub const fn ease_out() -> Self {
Self::EASE_OUT
}
pub fn ease_in_out() -> Self {
pub const fn ease_in_out() -> Self {
Self::EASE_IN_OUT
}

Expand Down Expand Up @@ -142,8 +145,8 @@ pub struct Spring {
}

impl Spring {
pub fn new(mass: f64, stiffness: f64, damping: f64, initial_velocity: f64) -> Self {
Spring {
pub const fn new(mass: f64, stiffness: f64, damping: f64, initial_velocity: f64) -> Self {
Self {
mass,
stiffness,
damping,
Expand All @@ -153,17 +156,17 @@ impl Spring {
// TODO: figure out if these are reasonable values.

/// Slower, smoother motion
pub fn gentle() -> Self {
pub const fn gentle() -> Self {
Self::new(1., 50.0, 8.0, 0.0)
}

/// More overshoot, longer settling time
pub fn bouncy() -> Self {
pub const fn bouncy() -> Self {
Self::new(1., 150.0, 5.0, 0.0)
}

/// Quick response, minimal overshoot
pub fn snappy() -> Self {
pub const fn snappy() -> Self {
Self::new(1., 200.0, 20.0, 0.0)
}

Expand All @@ -182,35 +185,35 @@ impl Spring {

if zeta < 1.0 {
// Underdamped
let omega_d = omega * (1.0 - zeta * zeta).sqrt();
let omega_d = omega * zeta.mul_add(-zeta, 1.0).sqrt();
let e = (-zeta * omega * time).exp();
let cos_term = (omega_d * time).cos();
let sin_term = (omega_d * time).sin();

let a = 1.0;
let b = (v0 + zeta * omega * a) / omega_d;
let b = (zeta * omega).mul_add(a, v0) / omega_d;

1.0 - e * (a * cos_term + b * sin_term)
e.mul_add(-a.mul_add(cos_term, b * sin_term), 1.0)
} else if zeta > 1.0 {
// Overdamped
let r1 = -omega * (zeta - (zeta * zeta - 1.0).sqrt());
let r2 = -omega * (zeta + (zeta * zeta - 1.0).sqrt());
let r1 = -omega * (zeta - zeta.mul_add(zeta, -1.0).sqrt());
let r2 = -omega * (zeta + zeta.mul_add(zeta, -1.0).sqrt());

let a = (v0 - r2) / (r1 - r2);
let b = 1.0 - a;

1.0 - a * (r1 * time).exp() - b * (r2 * time).exp()
b.mul_add(-(r2 * time).exp(), a.mul_add(-(r1 * time).exp(), 1.0))
} else {
// Critically damped
let e = (-omega * time).exp();
let a = 1.0;
let b = v0 + omega * a;
let b = omega.mul_add(a, v0);

1.0 - e * (a + b * time)
e.mul_add(-b.mul_add(time, a), 1.0)
}
}

const THRESHOLD: f64 = 0.003;
const THRESHOLD: f64 = 0.005;
pub fn finished(&self, time: f64) -> bool {
let position = self.eval(time);
let velocity = self.velocity(time);
Expand All @@ -233,32 +236,34 @@ impl Spring {

if zeta < 1.0 {
// Underdamped
let omega_d = omega * (1.0 - zeta * zeta).sqrt();
let omega_d = omega * zeta.mul_add(-zeta, 1.0).sqrt();
let e = (-zeta * omega * time).exp();
let cos_term = (omega_d * time).cos();
let sin_term = (omega_d * time).sin();

let a = 1.0;
let b = (v0 + zeta * omega * a) / omega_d;
let b = (zeta * omega).mul_add(a, v0) / omega_d;

e * ((zeta * omega * (a * cos_term + b * sin_term))
+ (a * -omega_d * sin_term + b * omega_d * cos_term))
e * (zeta * omega).mul_add(
a.mul_add(cos_term, b * sin_term),
(a * -omega_d).mul_add(sin_term, b * omega_d * cos_term),
)
} else if zeta > 1.0 {
// Overdamped
let r1 = -omega * (zeta - (zeta * zeta - 1.0).sqrt());
let r2 = -omega * (zeta + (zeta * zeta - 1.0).sqrt());
let r1 = -omega * (zeta - zeta.mul_add(zeta, -1.0).sqrt());
let r2 = -omega * (zeta + zeta.mul_add(zeta, -1.0).sqrt());

let a = (v0 - r2) / (r1 - r2);
let b = 1.0 - a;

-a * r1 * (r1 * time).exp() - b * r2 * (r2 * time).exp()
(-a * r1).mul_add((r1 * time).exp(), -(b * r2 * (r2 * time).exp()))
} else {
// Critically damped
let e = (-omega * time).exp();
let a = 1.0;
let b = v0 + omega * a;
let b = omega.mul_add(a, v0);

e * (b - omega * (a + b * time))
e * omega.mul_add(-b.mul_add(time, a), b)
}
}
}
Expand Down

0 comments on commit fdfb03c

Please sign in to comment.