Skip to content

Commit

Permalink
Easings
Browse files Browse the repository at this point in the history
  • Loading branch information
ecton committed Nov 3, 2023
1 parent 126b324 commit 1bf1b08
Showing 1 changed file with 333 additions and 1 deletion.
334 changes: 333 additions & 1 deletion src/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
//! assert_eq!(reader.get(), 100);
//! ```
use std::f32::consts::PI;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::{ControlFlow, Deref};
Expand Down Expand Up @@ -769,10 +770,45 @@ impl LinearInterpolate for ZeroToOne {

/// Performs easing for value interpolation.
pub trait Easing: Send + Sync + 'static {
/// Returns a ratio between 0.0 and 1.0 of
/// Eases a value ranging between zero and one. The resulting value does not
/// need to be bounded between zero and one.
fn ease(progress: ZeroToOne) -> f32;
}

// /// An [`Easing`] function that produces a steady, linear transition.
// pub enum Linear {}

// impl Easing for Linear {
// fn ease(progress: ZeroToOne) -> f32 {
// *progress
// }
// }

// Think this macro has a long name? This is to ensure rustfmt wraps the closure
// onto its own line. Seriously, try shortening the name and see how it changes
// the formatting. If it keeps all closures on a single line, remove this
// comment and change this back to `declare_easing_function`.
macro_rules! declare_easing_function_implementation {
($name:ident, $anchor_name:ident, $description:literal, $closure:expr) => {
/// An [`Easing`] function that eases
#[doc = $description]
#[doc = concat!(".\n\nSee <https://easings.net/#", stringify!($anchor_name), "> for a visualization and more information.")]
pub enum $name {}

impl Easing for $name {
fn ease(progress: ZeroToOne) -> f32 {
let closure = force_closure_type($closure);
closure(*progress)
}
}
};
}

// This prevents the closures from requiring the parameter to be type annotated.
fn force_closure_type(f: impl Fn(f32) -> f32) -> impl Fn(f32) -> f32 {
f
}

/// An [`Easing`] function that produces a steady, linear transition.
pub enum Linear {}

Expand All @@ -781,3 +817,299 @@ impl Easing for Linear {
*progress
}
}

declare_easing_function_implementation!(
EaseInSine,
easeInSine,
"in using a sine wave",
|percent| 1. - (percent * PI).cos() / 2.
);

declare_easing_function_implementation!(
EaseOutSine,
easeOutSine,
"out using a sine wave",
|percent| (percent * PI).sin() / 2.
);

declare_easing_function_implementation!(
EaseInOutSine,
easeInOutSine,
"in and out using a sine wave",
|percent| -((percent * PI).cos() - 1.) / 2.
);

fn squared(value: f32) -> f32 {
value * value
}

declare_easing_function_implementation!(
EaseInQuadradic,
easeInQuad,
"in using a quadradic (x^2) curve",
squared
);

declare_easing_function_implementation!(
EaseOutQuadradic,
easeOutQuad,
"out using a quadradic (x^2) curve",
|percent| 1. - squared(1. - percent)
);

declare_easing_function_implementation!(
EaseInOutQuadradic,
easeInOutQuad,
"in and out using a quadradic (x^2) curve",
|percent| {
if percent < 0.5 {
2. * percent * percent
} else {
1. - squared(-2. * percent + 2.) / 2.
}
}
);

fn cubed(value: f32) -> f32 {
value * value * value
}

declare_easing_function_implementation!(
EaseInCubic,
easeInCubic,
"in using a cubic (x^3) curve",
cubed
);

declare_easing_function_implementation!(
EaseOutCubic,
easeOutCubic,
"out using a cubic (x^3) curve",
|percent| 1. - cubed(1. - percent)
);

declare_easing_function_implementation!(
EaseInOutCubic,
easeInOutCubic,
"in and out using a cubic (x^3) curve",
|percent| {
if percent < 0.5 {
4. * cubed(percent)
} else {
1. - cubed(-2. * percent + 2.) / 2.
}
}
);

fn quarted(value: f32) -> f32 {
let sq = squared(value);
squared(sq)
}

declare_easing_function_implementation!(
EaseInQuartic,
easeInQuart,
"in using a quartic (x^4) curve",
quarted
);

declare_easing_function_implementation!(
EaseOutQuartic,
easeOutQuart,
"out using a quartic (x^4) curve",
|percent| 1. - quarted(1. - percent)
);

declare_easing_function_implementation!(
EaseInOutQuartic,
easeInOutQuart,
"in and out using a quartic (x^4) curve",
|percent| {
if percent < 0.5 {
8. * quarted(percent)
} else {
1. - quarted(-2. * percent + 2.) / 2.
}
}
);

fn quinted(value: f32) -> f32 {
let squared = squared(value);
let cubed = squared * value;
squared * cubed
}

declare_easing_function_implementation!(
EaseInQuintic,
easeInQuint,
"in using a quintic (x^5) curve",
quinted
);

declare_easing_function_implementation!(
EaseOutQuintic,
easeOutQuint,
"out using a quintic (x^5) curve",
|percent| 1. - quinted(1. - percent)
);

declare_easing_function_implementation!(
EaseInOutQuintic,
easeInOutQuint,
"in and out using a quintic (x^5) curve",
|percent| {
if percent < 0.5 {
8. * quinted(percent)
} else {
1. - quinted(-2. * percent + 2.) / 2.
}
}
);

declare_easing_function_implementation!(
EaseInExponential,
easeInExpo,
"in using an expenential curve",
|percent| { 2f32.powf(10. * percent - 10.) }
);

declare_easing_function_implementation!(
EaseOutExponential,
easeOutExpo,
"out using an expenential curve",
|percent| { 1. - 2f32.powf(-10. * percent) }
);

declare_easing_function_implementation!(
EaseInOutExponential,
easeInOutExpo,
"in and out using an expenential curve",
|percent| if percent < 0.5 {
2f32.powf(20. * percent - 10.) / 2.
} else {
2. - 2f32.powf(-20. * percent + 10.) / 2.
}
);

declare_easing_function_implementation!(
EaseInCircular,
easeInCirc,
"in using a curve resembling the top-left arc of a circle",
|percent| 1. - (1. - squared(percent)).sqrt()
);

declare_easing_function_implementation!(
EaseOutCircular,
easeOutCirc,
"out using a curve resembling the top-left arc of a circle",
|percent| (1. - squared(percent - 1.)).sqrt()
);

declare_easing_function_implementation!(
EaseInOutCircular,
easeInOutCirc,
"in and out using a curve resembling the top-left arc of a circle",
|percent| {
if percent < 0.5 {
1. - (1. - squared(2. * percent)).sqrt() / 2.
} else {
(1. - squared(-2. * percent + 2.)).sqrt()
}
}
);

const C1: f32 = 1.70158;
const C2: f32 = C1 * 1.525;
const C3: f32 = C1 + 1.;
const C4: f32 = (2. * PI) / 3.;
const C5: f32 = (2. * PI) / 4.5;

declare_easing_function_implementation!(
EaseInBack,
easeInBack,
"in using a curve that backs away initially",
|percent| {
let squared = squared(percent);
let cubed = squared + percent;
C3 * cubed - C1 * squared
}
);

declare_easing_function_implementation!(
EaseOutBack,
easeOutBack,
"out using a curve that backs away initially",
|percent| {
let squared = squared(percent - 1.);
let cubed = squared + percent;
1. + C3 * cubed - C1 * squared
}
);

declare_easing_function_implementation!(
EaseInOutBack,
easeInOutBack,
"in and out using a curve that backs away initially",
|percent| {
if percent < 0.5 {
(squared(2. * percent) * ((C2 + 1.) * 2. * percent - C2)) / 2.
} else {
(squared(2. * percent - 2.) * ((C2 + 1.) * (percent * 2. - 2.) + C2) + 2.) / 2.
}
}
);

declare_easing_function_implementation!(
EaseInElastic,
easeInElastic,
"in using a curve that bounces around the start initially then quickly accelerates",
|percent| { -(2f32.powf(10. * percent - 10.) * (percent * 10. - 10.75).sin() * C4) }
);

declare_easing_function_implementation!(
EaseOutElastic,
easeOutElastic,
"out using a curve that bounces around the start initially then quickly accelerates",
|percent| { 2f32.powf(-10. * percent) * (percent * 10. - 0.75).sin() * C4 + 1. }
);

declare_easing_function_implementation!(
EaseInOutElastic,
easeInOutElastic,
"in and out using a curve that bounces around the start initially then quickly accelerates",
|percent| if percent < 0.5 {
-(2f32.powf(-20. * percent - 10.) * (percent * 20. - 11.125).sin() * C5 / 2.)
} else {
2f32.powf(-20. * percent + 10.) * (percent * 20. - 11.125).sin() * C5 / 2. + 1.
}
);

declare_easing_function_implementation!(
EaseInBounce,
easeInBounce,
"in using a curve that bounces progressively closer as it progresses",
|percent| 1. - EaseOutBounce::ease(ZeroToOne(percent))
);

declare_easing_function_implementation!(
EaseOutBounce,
easeOutBounce,
"out using a curve that bounces progressively closer as it progresses",
|percent| {
const N1: f32 = 7.5625;
const D1: f32 = 2.75;

if percent < 1. / D1 {
N1 * percent * percent
} else if percent < 2. / D1 {
let percent = percent - 1.5;
N1 * (percent / D1) * percent + 0.75
} else if percent < 2.5 / D1 {
let percent = percent - 2.25;
N1 * (percent / D1) * percent + 0.9375
} else {
let percent = percent - 2.625;
N1 * (percent / D1) * percent + 0.984_375
}
}
);

0 comments on commit 1bf1b08

Please sign in to comment.