freya_animation/
hook.rs

1use std::{
2    ops::Deref,
3    time::Instant,
4};
5
6use freya_core::prelude::*;
7
8#[derive(Default, PartialEq, Clone, Debug)]
9pub struct AnimConfiguration {
10    on_finish: OnFinish,
11    on_creation: OnCreation,
12    on_change: OnChange,
13}
14
15impl AnimConfiguration {
16    pub fn on_finish(&mut self, on_finish: OnFinish) -> &mut Self {
17        self.on_finish = on_finish;
18        self
19    }
20
21    pub fn on_creation(&mut self, on_creation: OnCreation) -> &mut Self {
22        self.on_creation = on_creation;
23        self
24    }
25
26    pub fn on_change(&mut self, on_change: OnChange) -> &mut Self {
27        self.on_change = on_change;
28        self
29    }
30}
31
32/// Controls the direction of the animation.
33#[derive(Clone, Copy, PartialEq)]
34pub enum AnimDirection {
35    Forward,
36    Reverse,
37}
38
39impl AnimDirection {
40    pub fn toggle(&mut self) {
41        match self {
42            Self::Forward => *self = Self::Reverse,
43            Self::Reverse => *self = Self::Forward,
44        }
45    }
46}
47
48/// What to do once the animation finishes.
49///
50/// By default it is [OnFinish::Nothing].
51#[derive(PartialEq, Clone, Copy, Default, Debug)]
52pub enum OnFinish {
53    /// Does nothing at all.
54    #[default]
55    Nothing,
56    /// Runs the animation in reverse direction.
57    Reverse,
58    /// Runs the animation in the same direction again.
59    Restart,
60}
61
62/// What to do once the animation gets created.
63///
64/// By default it is [OnCreation::Nothing]
65#[derive(PartialEq, Clone, Copy, Default, Debug)]
66pub enum OnCreation {
67    /// Does nothing at all.
68    #[default]
69    Nothing,
70    /// Runs the animation.
71    Run,
72    /// Set the values to the end of the animation. As if it had actually run.
73    Finish,
74}
75
76/// What to do once the animation dependencies change.
77///
78/// Defaults to [OnChange::Reset].
79#[derive(PartialEq, Clone, Copy, Default, Debug)]
80pub enum OnChange {
81    /// Reset to the initial state.
82    #[default]
83    Reset,
84    /// Set the values to the end of the animation.
85    Finish,
86    /// Reruns the animation.
87    Rerun,
88}
89
90pub trait ReadAnimatedValue: Clone + 'static {
91    type Output;
92    fn value(&self) -> Self::Output;
93}
94
95pub trait AnimatedValue: Clone + Default + 'static {
96    fn prepare(&mut self, direction: AnimDirection);
97
98    fn is_finished(&self, index: u128, direction: AnimDirection) -> bool;
99
100    fn advance(&mut self, index: u128, direction: AnimDirection);
101
102    fn finish(&mut self, direction: AnimDirection);
103
104    fn into_reversed(self) -> Self;
105}
106
107#[derive(Default, Clone, Copy, PartialEq, Eq)]
108pub enum Ease {
109    In,
110    #[default]
111    Out,
112    InOut,
113}
114
115/// Animate your elements. Use [`use_animation`] to use this.
116#[derive(Clone, PartialEq)]
117pub struct UseAnimation<Animated: AnimatedValue> {
118    pub(crate) animated_value: State<Animated>,
119    pub(crate) config: State<AnimConfiguration>,
120    pub(crate) is_running: State<bool>,
121    pub(crate) has_run_yet: State<bool>,
122    pub(crate) task: State<Option<TaskHandle>>,
123    pub(crate) last_direction: State<AnimDirection>,
124}
125impl<T: AnimatedValue> Copy for UseAnimation<T> {}
126
127impl<Animated: AnimatedValue> Deref for UseAnimation<Animated> {
128    type Target = State<Animated>;
129    fn deref(&self) -> &Self::Target {
130        &self.animated_value
131    }
132}
133
134impl<Animated: AnimatedValue> UseAnimation<Animated> {
135    /// Get the animated value.
136    pub fn get(&self) -> ReadRef<'static, Animated> {
137        self.animated_value.read()
138    }
139
140    /// Runs the animation normally.
141    pub fn start(&mut self) {
142        self.run(AnimDirection::Forward)
143    }
144
145    /// Runs the animation in reverse direction.
146    pub fn reverse(&mut self) {
147        self.run(AnimDirection::Reverse)
148    }
149
150    /// Finish the animation with the final state.
151    pub fn finish(&mut self) {
152        if let Some(task) = self.task.write().take() {
153            task.cancel();
154        }
155
156        self.animated_value
157            .write()
158            .finish(*self.last_direction.peek());
159
160        *self.has_run_yet.write() = true;
161    }
162
163    pub fn is_running(&self) -> State<bool> {
164        self.is_running
165    }
166
167    pub fn has_run_yet(&self) -> State<bool> {
168        self.has_run_yet
169    }
170
171    /// Run the animation with a given [`AnimDirection`]
172    pub fn run(&self, mut direction: AnimDirection) {
173        let mut is_running = self.is_running;
174        let mut has_run_yet = self.has_run_yet;
175        let mut task = self.task;
176        let mut last_direction = self.last_direction;
177
178        let on_finish = self.config.peek().on_finish;
179        let mut animated_value = self.animated_value;
180
181        last_direction.set(direction);
182
183        // Cancel previous animations
184        if let Some(task) = task.write().take() {
185            task.cancel();
186        }
187
188        let peek_has_run_yet = *self.has_run_yet.peek();
189
190        let mut ticker = RenderingTicker::get();
191        let platform = Platform::get();
192        let animation_clock = AnimationClock::get();
193
194        let animation_task = spawn(async move {
195            platform.send(UserEvent::RequestRedraw);
196
197            let mut index = 0u128;
198            let mut prev_frame = Instant::now();
199
200            // Prepare the animations with the the proper direction
201            animated_value.write().prepare(direction);
202
203            if !peek_has_run_yet {
204                *has_run_yet.write() = true;
205            }
206            is_running.set(true);
207
208            loop {
209                // Wait for the event loop to tick
210                ticker.tick().await;
211
212                // Request another redraw to move the animation forward
213                platform.send(UserEvent::RequestRedraw);
214
215                let elapsed = animation_clock.correct_elapsed_duration(prev_frame.elapsed());
216
217                index += elapsed.as_millis();
218
219                let mut animated_value = animated_value.write();
220
221                let is_finished = animated_value.is_finished(index, direction);
222
223                // Advance the animations
224                animated_value.advance(index, direction);
225
226                prev_frame = Instant::now();
227
228                if is_finished {
229                    if OnFinish::Reverse == on_finish {
230                        // Toggle direction
231                        direction.toggle();
232                    }
233                    match on_finish {
234                        OnFinish::Restart | OnFinish::Reverse => {
235                            index = 0;
236
237                            // Restart the animation
238                            animated_value.prepare(direction);
239                        }
240                        OnFinish::Nothing => {
241                            // Stop if all the animations are finished
242                            break;
243                        }
244                    }
245                }
246            }
247
248            is_running.set(false);
249            task.write().take();
250        });
251
252        // Cancel previous animations
253        task.write().replace(animation_task);
254    }
255}
256
257pub fn use_animation<Animated: AnimatedValue>(
258    mut run: impl 'static + FnMut(&mut AnimConfiguration) -> Animated,
259) -> UseAnimation<Animated> {
260    use_hook(|| {
261        let mut config = State::create(AnimConfiguration::default());
262        let mut animated_value = State::create(Animated::default());
263        let is_running = State::create(false);
264        let has_run_yet = State::create(false);
265        let task = State::create(None);
266        let last_direction = State::create(AnimDirection::Forward);
267
268        let mut animation = UseAnimation {
269            animated_value,
270            config,
271            is_running,
272            has_run_yet,
273            task,
274            last_direction,
275        };
276
277        Effect::create_sync(move || {
278            let mut anim_conf = AnimConfiguration::default();
279            animated_value.set(run(&mut anim_conf));
280            *config.write() = anim_conf;
281        });
282
283        Effect::create_sync_with_gen(move |current_gen| match config.read().on_change {
284            OnChange::Finish if current_gen > 0 => {
285                animation.finish();
286            }
287            OnChange::Rerun if current_gen > 0 => {
288                let last_direction = *animation.last_direction.peek();
289                animation.run(last_direction);
290            }
291            _ => {}
292        });
293
294        match config.peek().on_creation {
295            OnCreation::Run => {
296                animation.run(AnimDirection::Forward);
297            }
298            OnCreation::Finish => {
299                animation.finish();
300            }
301            _ => {}
302        }
303
304        animation
305    })
306}
307
308pub fn use_animation_with_dependencies<Animated: AnimatedValue, D: 'static + Clone + PartialEq>(
309    dependencies: &D,
310    mut run: impl 'static + FnMut(&mut AnimConfiguration, &D) -> Animated,
311) -> UseAnimation<Animated> {
312    let dependencies = use_reactive(dependencies);
313    use_hook(|| {
314        let mut config = State::create(AnimConfiguration::default());
315        let mut animated_value = State::create(Animated::default());
316        let is_running = State::create(false);
317        let has_run_yet = State::create(false);
318        let task = State::create(None);
319        let last_direction = State::create(AnimDirection::Forward);
320
321        let mut animation = UseAnimation {
322            animated_value,
323            config,
324            is_running,
325            has_run_yet,
326            task,
327            last_direction,
328        };
329
330        Effect::create_sync(move || {
331            let dependencies = dependencies.read();
332            let mut anim_conf = AnimConfiguration::default();
333            animated_value.set(run(&mut anim_conf, &dependencies));
334            *config.write() = anim_conf;
335        });
336
337        Effect::create_sync_with_gen(move |current_gen| match config.read().on_change {
338            OnChange::Finish if current_gen > 0 => {
339                animation.finish();
340            }
341            OnChange::Rerun if current_gen > 0 => {
342                let last_direction = *animation.last_direction.peek();
343                animation.run(last_direction);
344            }
345            _ => {}
346        });
347
348        match config.peek().on_creation {
349            OnCreation::Run => {
350                animation.run(AnimDirection::Forward);
351            }
352            OnCreation::Finish => {
353                animation.finish();
354            }
355            _ => {}
356        }
357
358        animation
359    })
360}
361
362macro_rules! impl_tuple_call {
363    ($(($($type:ident),*)),*) => {
364        $(
365            impl<$($type,)*> AnimatedValue for ($($type,)*)
366            where
367                $($type: AnimatedValue,)*
368            {
369                fn prepare(&mut self, direction: AnimDirection) {
370                    #[allow(non_snake_case)]
371                    let ($($type,)*) = self;
372                    $(
373                        $type.prepare(direction);
374                    )*
375                }
376
377                fn is_finished(&self, index: u128, direction: AnimDirection) -> bool {
378                    #[allow(non_snake_case)]
379                    let ($($type,)*) = self;
380                    $(
381                        if !$type.is_finished(index, direction) {
382                            return false;
383                        }
384                    )*
385                    true
386                }
387
388                fn advance(&mut self, index: u128, direction: AnimDirection) {
389                    #[allow(non_snake_case)]
390                    let ($($type,)*) = self;
391                    $(
392                        $type.advance(index, direction);
393                    )*
394                }
395
396                fn finish(&mut self, direction: AnimDirection) {
397                    #[allow(non_snake_case)]
398                    let ($($type,)*) = self;
399                    $(
400                        $type.finish(direction);
401                    )*
402                }
403
404               fn into_reversed(self) -> Self {
405                    #[allow(non_snake_case)]
406                    let ($($type,)*) = self;
407                    (
408                        $(
409                            $type.into_reversed(),
410                        )*
411                    )
412                }
413            }
414            impl<$($type,)*> ReadAnimatedValue for  ($($type,)*)
415            where
416                $($type: ReadAnimatedValue,)*
417            {
418                type Output = (
419                    $(
420                        <$type as ReadAnimatedValue>::Output,
421                    )*
422                );
423                fn value(&self) -> Self::Output {
424                    #[allow(non_snake_case)]
425                    let ($($type,)*) = self;
426                    (
427                        $(
428                            $type.value(),
429                        )*
430                    )
431                }
432            }
433        )*
434    };
435}
436
437impl_tuple_call!(
438    (T1),
439    (T1, T2),
440    (T1, T2, T3),
441    (T1, T2, T3, T4),
442    (T1, T2, T3, T4, T5),
443    (T1, T2, T3, T4, T5, T6),
444    (T1, T2, T3, T4, T5, T6, T7),
445    (T1, T2, T3, T4, T5, T6, T7, T8),
446    (T1, T2, T3, T4, T5, T6, T7, T8, T9),
447    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10),
448    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11),
449    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
450);