armas_basic/animation/
staggered.rs1use super::{Animation, AnimationState, EasingFunction, Interpolate};
7
8#[derive(Debug, Clone)]
25pub struct StaggeredAnimation<T: Interpolate> {
26 pub base_delay: f32,
28 pub stagger_delay: f32,
30 pub duration: f32,
32 pub easing: EasingFunction,
34 elapsed: f32,
36 item_count: usize,
38 start: T,
40 end: T,
41}
42
43impl<T: Interpolate> StaggeredAnimation<T> {
44 pub const fn new(
46 start: T,
47 end: T,
48 item_count: usize,
49 stagger_delay: f32,
50 duration: f32,
51 ) -> Self {
52 Self {
53 base_delay: 0.0,
54 stagger_delay,
55 duration,
56 easing: EasingFunction::EaseOut,
57 elapsed: 0.0,
58 item_count,
59 start,
60 end,
61 }
62 }
63
64 #[must_use]
66 pub const fn base_delay(mut self, delay: f32) -> Self {
67 self.base_delay = delay;
68 self
69 }
70
71 #[must_use]
73 pub const fn easing(mut self, easing: EasingFunction) -> Self {
74 self.easing = easing;
75 self
76 }
77
78 pub fn update(&mut self, dt: f32) {
80 self.elapsed += dt;
81 }
82
83 pub const fn reset(&mut self) {
85 self.elapsed = 0.0;
86 }
87
88 pub fn value(&self, index: usize) -> T {
90 let item_start_time = self.base_delay + (index as f32 * self.stagger_delay);
91 let item_end_time = item_start_time + self.duration;
92
93 if self.elapsed < item_start_time {
94 return self.start.clone();
96 }
97
98 if self.elapsed >= item_end_time {
99 return self.end.clone();
101 }
102
103 let item_elapsed = self.elapsed - item_start_time;
105 let t = (item_elapsed / self.duration).clamp(0.0, 1.0);
106 let eased_t = self.easing.apply(t);
107
108 self.start.interpolate(&self.end, eased_t)
109 }
110
111 pub fn progress(&self, index: usize) -> f32 {
113 let item_start_time = self.base_delay + (index as f32 * self.stagger_delay);
114 let item_end_time = item_start_time + self.duration;
115
116 if self.elapsed < item_start_time {
117 0.0
118 } else if self.elapsed >= item_end_time {
119 1.0
120 } else {
121 ((self.elapsed - item_start_time) / self.duration).clamp(0.0, 1.0)
122 }
123 }
124
125 pub fn is_complete(&self) -> bool {
127 let last_item_end =
128 self.base_delay + ((self.item_count - 1) as f32 * self.stagger_delay) + self.duration;
129 self.elapsed >= last_item_end
130 }
131
132 pub fn opacity(&self, index: usize) -> f32 {
134 self.progress(index)
135 }
136
137 pub fn scale(&self, index: usize) -> f32 {
139 let t = self.progress(index);
140 0.8 + 0.2 * t }
142
143 pub fn y_offset(&self, index: usize, distance: f32) -> f32 {
145 let t = self.progress(index);
146 distance * (1.0 - t)
147 }
148}
149
150#[derive(Debug)]
162pub struct AnimationSequence<T: Interpolate> {
163 animations: Vec<SequenceStep<T>>,
164 current_step: usize,
165 elapsed: f32,
166}
167
168#[derive(Debug)]
169struct SequenceStep<T: Interpolate> {
170 delay: f32,
171 animation: Animation<T>,
172}
173
174impl<T: Interpolate> AnimationSequence<T> {
175 #[must_use]
177 pub const fn new() -> Self {
178 Self {
179 animations: Vec::new(),
180 current_step: 0,
181 elapsed: 0.0,
182 }
183 }
184
185 #[must_use]
187 pub fn then(mut self, animation: Animation<T>, delay: f32) -> Self {
188 self.animations.push(SequenceStep { delay, animation });
189 self
190 }
191
192 pub fn update(&mut self, dt: f32) {
194 if self.current_step >= self.animations.len() {
195 return;
196 }
197
198 self.elapsed += dt;
199 let step = &mut self.animations[self.current_step];
200
201 if self.elapsed < step.delay {
203 return;
204 }
205
206 let animation_dt = self.elapsed - step.delay;
208 step.animation.elapsed = animation_dt;
209
210 if animation_dt >= step.animation.duration {
211 self.current_step += 1;
213 self.elapsed = 0.0;
214 }
215 }
216
217 #[must_use]
219 pub fn value(&self) -> T {
220 if self.current_step >= self.animations.len() {
221 if let Some(last) = self.animations.last() {
223 return last.animation.end.clone();
224 }
225 }
226
227 self.animations.get(self.current_step).map_or_else(
228 || self.animations[0].animation.start.clone(),
229 |step| step.animation.value(),
230 )
231 }
232
233 #[must_use]
235 pub const fn is_complete(&self) -> bool {
236 self.current_step >= self.animations.len()
237 }
238
239 pub fn reset(&mut self) {
241 self.current_step = 0;
242 self.elapsed = 0.0;
243 for step in &mut self.animations {
244 step.animation.reset();
245 }
246 }
247}
248
249impl<T: Interpolate> Default for AnimationSequence<T> {
250 fn default() -> Self {
251 Self::new()
252 }
253}
254
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
257pub enum LoopMode {
258 Once,
260 Loop,
262 PingPong,
264}
265
266#[derive(Debug, Clone)]
278pub struct LoopingAnimation<T: Interpolate> {
279 animation: Animation<T>,
280 mode: LoopMode,
281 forward: bool,
282}
283
284impl<T: Interpolate> LoopingAnimation<T> {
285 pub const fn new(start: T, end: T, duration: f32, mode: LoopMode) -> Self {
287 Self {
288 animation: Animation::new(start, end, duration),
289 mode,
290 forward: true,
291 }
292 }
293
294 #[must_use]
296 pub const fn easing(mut self, easing: EasingFunction) -> Self {
297 self.animation.easing = easing;
298 self
299 }
300
301 pub fn update(&mut self, dt: f32) {
303 self.animation.update(dt);
304
305 if self.animation.is_complete() {
306 match self.mode {
307 LoopMode::Once => {
308 }
310 LoopMode::Loop => {
311 self.animation.reset();
313 self.animation.start();
314 }
315 LoopMode::PingPong => {
316 self.forward = !self.forward;
318 std::mem::swap(&mut self.animation.start, &mut self.animation.end);
319 self.animation.reset();
320 self.animation.start();
321 }
322 }
323 } else if self.animation.state == AnimationState::NotStarted {
324 self.animation.start();
325 }
326 }
327
328 pub fn value(&self) -> T {
330 self.animation.value()
331 }
332
333 pub fn progress(&self) -> f32 {
335 self.animation.progress()
336 }
337
338 pub const fn reset(&mut self) {
340 self.animation.reset();
341 self.forward = true;
342 }
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348
349 #[test]
350 fn test_staggered_animation() {
351 let mut anim = StaggeredAnimation::new(0.0_f32, 1.0, 3, 0.1, 0.3);
352
353 assert_eq!(anim.value(0), 0.0);
355 assert_eq!(anim.value(1), 0.0);
356 assert_eq!(anim.value(2), 0.0);
357
358 anim.elapsed = 0.15;
360 assert!(anim.progress(0) > 0.0);
361 assert!(anim.progress(1) < 0.5);
362 assert_eq!(anim.progress(2), 0.0);
363 }
364
365 #[test]
366 fn test_looping_animation() {
367 let mut anim = LoopingAnimation::new(0.0_f32, 1.0, 1.0, LoopMode::Loop);
368
369 anim.update(0.0);
371 anim.update(0.5);
372 assert!(anim.progress() > 0.0 && anim.progress() < 1.0);
373
374 anim.update(0.6); assert!(anim.progress() < 0.5);
376 }
377}