Skip to main content

animato_timeline/
group.rs

1//! Animation groups built on top of [`Timeline`].
2
3use crate::{At, Sequence, Timeline};
4use alloc::format;
5use alloc::vec::Vec;
6use animato_core::{Playable, Update};
7use animato_tween::StaggerPattern;
8use core::fmt;
9
10/// A group of animations controlled as a single playable unit.
11pub struct AnimationGroup {
12    inner: Timeline,
13}
14
15impl fmt::Debug for AnimationGroup {
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        f.debug_struct("AnimationGroup")
18            .field("inner", &self.inner)
19            .finish()
20    }
21}
22
23impl AnimationGroup {
24    /// Create a group from an existing timeline.
25    pub fn from_timeline(timeline: Timeline) -> Self {
26        Self { inner: timeline }
27    }
28
29    /// Play all animations at the same time.
30    pub fn parallel<A>(animations: Vec<A>) -> Self
31    where
32        A: Playable + Send + 'static,
33    {
34        let mut timeline = Timeline::new();
35        for (index, animation) in animations.into_iter().enumerate() {
36            timeline = timeline.add(format!("item_{index}"), animation, At::Start);
37        }
38        Self::from_timeline(timeline)
39    }
40
41    /// Play animations one after another.
42    pub fn sequence<A>(animations: Vec<A>) -> Self
43    where
44        A: Playable + Send + 'static,
45    {
46        let mut sequence = Sequence::new();
47        for (index, animation) in animations.into_iter().enumerate() {
48            sequence = sequence.then(format!("item_{index}"), animation);
49        }
50        Self::from_timeline(sequence.build())
51    }
52
53    /// Play animations with start delays from a [`StaggerPattern`].
54    pub fn stagger<A>(animations: Vec<A>, pattern: StaggerPattern) -> Self
55    where
56        A: Playable + Send + 'static,
57    {
58        let total = animations.len();
59        let mut timeline = Timeline::new();
60        for (index, animation) in animations.into_iter().enumerate() {
61            timeline = timeline.add(
62                format!("item_{index}"),
63                animation,
64                At::Absolute(pattern.delay(index, total)),
65            );
66        }
67        Self::from_timeline(timeline)
68    }
69
70    /// Access the wrapped timeline.
71    pub fn timeline(&self) -> &Timeline {
72        &self.inner
73    }
74
75    /// Access the wrapped timeline mutably.
76    pub fn timeline_mut(&mut self) -> &mut Timeline {
77        &mut self.inner
78    }
79
80    /// Begin group playback.
81    pub fn play(&mut self) {
82        self.inner.play();
83    }
84
85    /// Pause group playback.
86    pub fn pause(&mut self) {
87        self.inner.pause();
88    }
89
90    /// Resume group playback.
91    pub fn resume(&mut self) {
92        self.inner.resume();
93    }
94
95    /// Reset group playback.
96    pub fn reset(&mut self) {
97        self.inner.reset();
98    }
99
100    /// Seek by normalized progress.
101    pub fn seek(&mut self, progress: f32) {
102        self.inner.seek(progress);
103    }
104
105    /// Seek to the mirrored progress position.
106    pub fn reverse(&mut self) {
107        let progress = self.inner.progress();
108        self.inner.seek(1.0 - progress);
109    }
110
111    /// Set time scale for the entire group.
112    pub fn set_time_scale(&mut self, scale: f32) {
113        self.inner.set_time_scale(scale);
114    }
115
116    /// Group duration in seconds.
117    pub fn duration(&self) -> f32 {
118        self.inner.duration()
119    }
120
121    /// Normalized progress.
122    pub fn progress(&self) -> f32 {
123        self.inner.progress()
124    }
125
126    /// `true` when the group has completed.
127    pub fn is_complete(&self) -> bool {
128        self.inner.is_complete()
129    }
130
131    /// Register a callback fired once when the group completes.
132    #[cfg(feature = "std")]
133    pub fn on_complete(mut self, f: impl FnMut() + Send + 'static) -> Self {
134        self.inner = self.inner.on_complete(f);
135        self
136    }
137}
138
139impl Update for AnimationGroup {
140    fn update(&mut self, dt: f32) -> bool {
141        self.inner.update(dt)
142    }
143}
144
145impl Playable for AnimationGroup {
146    fn duration(&self) -> f32 {
147        self.inner.duration()
148    }
149
150    fn reset(&mut self) {
151        AnimationGroup::reset(self);
152    }
153
154    fn seek_to(&mut self, progress: f32) {
155        self.seek(progress);
156    }
157
158    fn is_complete(&self) -> bool {
159        AnimationGroup::is_complete(self)
160    }
161
162    fn as_any(&self) -> &dyn core::any::Any {
163        self
164    }
165
166    fn as_any_mut(&mut self) -> &mut dyn core::any::Any {
167        self
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use super::*;
174    use animato_tween::Tween;
175
176    #[test]
177    fn parallel_completes_when_last_child_finishes() {
178        let mut group = AnimationGroup::parallel(alloc::vec![
179            Tween::new(0.0_f32, 1.0).duration(0.5).build(),
180            Tween::new(0.0_f32, 1.0).duration(1.0).build(),
181        ]);
182        group.play();
183        assert!(group.update(0.75));
184        assert!(!group.update(0.25));
185    }
186
187    #[test]
188    fn sequence_orders_children() {
189        let mut group = AnimationGroup::sequence(alloc::vec![
190            Tween::new(0.0_f32, 10.0).duration(1.0).build(),
191            Tween::new(0.0_f32, 20.0).duration(1.0).build(),
192        ]);
193        group.play();
194        group.update(1.5);
195        assert_eq!(
196            group
197                .timeline()
198                .get::<Tween<f32>>("item_0")
199                .expect("first")
200                .value(),
201            10.0
202        );
203        assert_eq!(
204            group
205                .timeline()
206                .get::<Tween<f32>>("item_1")
207                .expect("second")
208                .value(),
209            10.0
210        );
211    }
212}