Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

animations improvements #663

Merged
merged 1 commit into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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