easings_rs/
lib.rs

1#![feature(clamp)]
2
3use std::f32::consts::PI;
4
5const C1: f32 = 1.70158;
6const C2: f32 = C1 * 1.525;
7const C3: f32 = C1 + 1.0;
8const C4: f32 = (2.0 * PI) / 3.0;
9const C5: f32 = (2.0 * PI) / 4.5;
10const N1: f32 = 7.5625;
11const D1: f32 = 2.75;
12
13#[derive(Clone, Copy, Debug)]
14pub enum Easing {
15    Linear,
16    SineIn,
17    SineOut,
18    SineInOut,
19    QuadIn,
20    QuadOut,
21    QuadInOut,
22    CubicIn,
23    CubicOut,
24    CubicInOut,
25    QuartIn,
26    QuartOut,
27    QuartInOut,
28    QuintIn,
29    QuintOut,
30    QuintInOut,
31    ExpoIn,
32    ExpoOut,
33    ExpoInOut,
34    CircIn,
35    CircOut,
36    CircInOut,
37    BackIn,
38    BackOut,
39    BackInOut,
40    ElasticIn,
41    ElasticOut,
42    ElasticInOut,
43    BounceIn,
44    BounceOut,
45    BounceInOut,
46}
47
48pub fn ease(easing: Easing, x: f32) -> f32 {
49    let x = x.clamp(0.0, 1.0);
50    match easing {
51        Easing::Linear => x,
52
53        // Sine
54        Easing::SineIn => 1.0 - (x * PI / 2.0).cos(),
55        Easing::SineOut => (x * PI / 2.0).sin(),
56        Easing::SineInOut => -((x * PI).cos() - 1.0) / 2.0,
57
58        // Quad
59        Easing::QuadIn => x.powf(2.0),
60        Easing::QuadOut => 1.0 - (1.0 - x).powf(2.0),
61        Easing::QuadInOut => {
62            if x < 0.5 {
63                2.0 * x.powf(2.0)
64            } else {
65                1.0 - (-2.0 * x + 2.0).powf(2.0) / 2.0
66            }
67        }
68
69        // Cubic
70        Easing::CubicIn => x.powf(3.0),
71        Easing::CubicOut => 1.0 - (1.0 - x).powf(3.0),
72        Easing::CubicInOut => {
73            if x < 0.5 {
74                4.0 * x.powf(3.0)
75            } else {
76                1.0 - (-2.0 * x + 2.0).powf(3.0) / 2.0
77            }
78        }
79
80        // Quart
81        Easing::QuartIn => x.powf(4.0),
82        Easing::QuartOut => 1.0 - (1.0 - x).powf(4.0),
83        Easing::QuartInOut => {
84            if x < 0.5 {
85                8.0 * x.powf(4.0)
86            } else {
87                1.0 - (-2.0 * x + 2.0).powf(4.0) / 2.0
88            }
89        }
90
91        // Quint
92        Easing::QuintIn => x.powf(5.0),
93        Easing::QuintOut => 1.0 - (1.0 - x).powf(5.0),
94        Easing::QuintInOut => {
95            if x < 0.5 {
96                16.0 * x.powf(5.0)
97            } else {
98                1.0 - (-2.0 * x + 2.0).powf(5.0) / 2.0
99            }
100        }
101
102        // Expo
103        Easing::ExpoIn => {
104            if x <= 0.0 {
105                0.0
106            } else {
107                2.0_f32.powf(10.0 * x - 10.0)
108            }
109        }
110        Easing::ExpoOut => {
111            if x >= 1.0 {
112                1.0
113            } else {
114                1.0 - 2.0_f32.powf(-10.0 * x)
115            }
116        }
117        Easing::ExpoInOut => {
118            if x <= 0.0 {
119                0.0
120            } else if x >= 1.0 {
121                1.0
122            } else if x < 0.5 {
123                2.0_f32.powf(20.0 * x - 10.0) / 2.0
124            } else {
125                (2.0 - 2.0_f32.powf(-20.0 * x + 10.0)) / 2.0
126            }
127        }
128
129        // Circ
130        Easing::CircIn => 1.0 - (1.0 - x.powf(2.0)).sqrt(),
131        Easing::CircOut => (1.0 - (x - 1.0).powf(2.0)).sqrt(),
132        Easing::CircInOut => {
133            if x < 0.5 {
134                (1.0 - (1.0 - (2.0 * x).powf(2.0)).sqrt()) / 2.0
135            } else {
136                ((1.0 - (-2.0 * x + 2.0).powf(2.0)).sqrt() + 1.0) / 2.0
137            }
138        }
139
140        // Back
141        Easing::BackIn => C3 * x.powf(3.0) - C1 * x.powf(2.0),
142        Easing::BackOut => 1.0 + C3 * (x - 1.0).powf(3.0) + C1 * (x - 1.0).powf(2.0),
143        Easing::BackInOut => {
144            if x < 0.5 {
145                (2.0 * x).powf(2.0) * ((C2 + 1.0) * 2.0 * x - 2.0) / 2.0
146            } else {
147                ((2.0 * x - 2.0).powf(2.0) * ((C2 + 1.0) * (x * 2.0 - 2.0) + C2) + 2.0) / 2.0
148            }
149        }
150
151        // Elastic
152        Easing::ElasticIn => {
153            if x <= 0.0 {
154                0.0
155            } else if x >= 1.0 {
156                1.0
157            } else {
158                -(2.0_f32.powf(10.0 * x - 10.0)) * (C4 * (x * 10.0 - 10.75)).sin()
159            }
160        }
161        Easing::ElasticOut => {
162            if x <= 0.0 {
163                0.0
164            } else if x >= 1.0 {
165                1.0
166            } else {
167                2.0_f32.powf(-10.0 * x) * (C4 * (x * 10.0 - 0.75)).sin() + 1.0
168            }
169        }
170        Easing::ElasticInOut => {
171            if x <= 0.0 {
172                0.0
173            } else if x >= 1.0 {
174                1.0
175            } else if x < 0.5 {
176                -(2.0_f32.powf(20.0 * x - 10.0) * (C5 * (20.0 * x - 11.125)).sin()) / 2.0
177            } else {
178                2.0_f32.powf(-20.0 * x + 10.0) * (C5 * (20.0 * x - 11.125)).sin() / 2.0 + 1.0
179            }
180        }
181
182        // Bounce
183        Easing::BounceIn => 1.0 - bounce_out(1.0 - x),
184        Easing::BounceOut => bounce_out(x),
185        Easing::BounceInOut => {
186            if x < 0.5 {
187                (1.0 - bounce_out(1.0 - 2.0 * x)) / 2.0
188            } else {
189                (1.0 + bounce_out(2.0 * x - 1.0)) / 2.0
190            }
191        }
192    }
193}
194
195fn bounce_out(x: f32) -> f32 {
196    if x < 1.0 / D1 {
197        N1 * x.powf(2.0)
198    } else if x < 2.0 / D1 {
199        let x = x - 1.5 / D1;
200        N1 * x.powf(2.0) + 0.75
201    } else if x < 2.5 / D1 {
202        let x = x - 2.25 / D1;
203        N1 * x.powf(2.0) + 0.9375
204    } else {
205        let x = x - 2.625 / D1;
206        N1 * x.powf(2.0) + 0.984_375
207    }
208}