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 has_run_yet(&self) -> State<bool> {
164        self.has_run_yet
165    }
166
167    /// Run the animation with a given [`AnimDirection`]
168    pub fn run(&self, mut direction: AnimDirection) {
169        let mut is_running = self.is_running;
170        let mut has_run_yet = self.has_run_yet;
171        let mut task = self.task;
172        let mut last_direction = self.last_direction;
173
174        let on_finish = self.config.peek().on_finish;
175        let mut animated_value = self.animated_value;
176
177        last_direction.set(direction);
178
179        // Cancel previous animations
180        if let Some(task) = task.write().take() {
181            task.cancel();
182        }
183
184        let peek_has_run_yet = *self.has_run_yet.peek();
185
186        let mut ticker = RenderingTicker::get();
187        let platform = Platform::get();
188        let animation_clock = AnimationClock::get();
189
190        let animation_task = spawn(async move {
191            platform.send(UserEvent::RequestRedraw);
192
193            let mut index = 0u128;
194            let mut prev_frame = Instant::now();
195
196            // Prepare the animations with the the proper direction
197            animated_value.write().prepare(direction);
198
199            if !peek_has_run_yet {
200                *has_run_yet.write() = true;
201            }
202            is_running.set(true);
203
204            loop {
205                // Wait for the event loop to tick
206                ticker.tick().await;
207
208                // Request another redraw to move the animation forward
209                platform.send(UserEvent::RequestRedraw);
210
211                let elapsed = animation_clock.correct_elapsed_duration(prev_frame.elapsed());
212
213                index += elapsed.as_millis();
214
215                let mut animated_value = animated_value.write();
216
217                let is_finished = animated_value.is_finished(index, direction);
218
219                // Advance the animations
220                animated_value.advance(index, direction);
221
222                prev_frame = Instant::now();
223
224                if is_finished {
225                    if OnFinish::Reverse == on_finish {
226                        // Toggle direction
227                        direction.toggle();
228                    }
229                    match on_finish {
230                        OnFinish::Restart | OnFinish::Reverse => {
231                            index = 0;
232
233                            // Restart the animation
234                            animated_value.prepare(direction);
235                        }
236                        OnFinish::Nothing => {
237                            // Stop if all the animations are finished
238                            break;
239                        }
240                    }
241                }
242            }
243
244            is_running.set(false);
245            task.write().take();
246        });
247
248        // Cancel previous animations
249        task.write().replace(animation_task);
250    }
251}
252
253pub fn use_animation<Animated: AnimatedValue>(
254    mut run: impl 'static + FnMut(&mut AnimConfiguration) -> Animated,
255) -> UseAnimation<Animated> {
256    use_hook(|| {
257        let mut config = State::create(AnimConfiguration::default());
258        let mut animated_value = State::create(Animated::default());
259        let is_running = State::create(false);
260        let has_run_yet = State::create(false);
261        let task = State::create(None);
262        let last_direction = State::create(AnimDirection::Forward);
263
264        let mut animation = UseAnimation {
265            animated_value,
266            config,
267            is_running,
268            has_run_yet,
269            task,
270            last_direction,
271        };
272
273        Effect::create_sync(move || {
274            let mut anim_conf = AnimConfiguration::default();
275            animated_value.set(run(&mut anim_conf));
276            *config.write() = anim_conf;
277        });
278
279        Effect::create_with_gen(move |current_gen| match config.read().on_change {
280            OnChange::Finish if current_gen > 0 => {
281                animation.finish();
282            }
283            OnChange::Rerun if current_gen > 0 => {
284                let last_direction = *animation.last_direction.peek();
285                animation.run(last_direction);
286            }
287            _ => {}
288        });
289
290        match config.peek().on_creation {
291            OnCreation::Run => {
292                animation.run(AnimDirection::Forward);
293            }
294            OnCreation::Finish => {
295                animation.finish();
296            }
297            _ => {}
298        }
299
300        animation
301    })
302}
303
304pub fn use_animation_with_dependencies<Animated: AnimatedValue, D: 'static + Clone + PartialEq>(
305    dependencies: &D,
306    mut run: impl 'static + FnMut(&mut AnimConfiguration, &D) -> Animated,
307) -> UseAnimation<Animated> {
308    let dependencies = use_reactive(dependencies);
309    use_hook(|| {
310        let mut config = State::create(AnimConfiguration::default());
311        let mut animated_value = State::create(Animated::default());
312        let is_running = State::create(false);
313        let has_run_yet = State::create(false);
314        let task = State::create(None);
315        let last_direction = State::create(AnimDirection::Forward);
316
317        let mut animation = UseAnimation {
318            animated_value,
319            config,
320            is_running,
321            has_run_yet,
322            task,
323            last_direction,
324        };
325
326        Effect::create_sync(move || {
327            let dependencies = dependencies.read();
328            let mut anim_conf = AnimConfiguration::default();
329            animated_value.set(run(&mut anim_conf, &dependencies));
330            *config.write() = anim_conf;
331        });
332
333        Effect::create_with_gen(move |current_gen| match config.read().on_change {
334            OnChange::Finish if current_gen > 0 => {
335                animation.finish();
336            }
337            OnChange::Rerun if current_gen > 0 => {
338                let last_direction = *animation.last_direction.peek();
339                animation.run(last_direction);
340            }
341            _ => {}
342        });
343
344        match config.peek().on_creation {
345            OnCreation::Run => {
346                animation.run(AnimDirection::Forward);
347            }
348            OnCreation::Finish => {
349                animation.finish();
350            }
351            _ => {}
352        }
353
354        animation
355    })
356}
357
358macro_rules! impl_tuple_call {
359    ($(($($type:ident),*)),*) => {
360        $(
361            impl<$($type,)*> AnimatedValue for ($($type,)*)
362            where
363                $($type: AnimatedValue,)*
364            {
365                fn prepare(&mut self, direction: AnimDirection) {
366                    #[allow(non_snake_case)]
367                    let ($($type,)*) = self;
368                    $(
369                        $type.prepare(direction);
370                    )*
371                }
372
373                fn is_finished(&self, index: u128, direction: AnimDirection) -> bool {
374                    #[allow(non_snake_case)]
375                    let ($($type,)*) = self;
376                    $(
377                        if !$type.is_finished(index, direction) {
378                            return false;
379                        }
380                    )*
381                    true
382                }
383
384                fn advance(&mut self, index: u128, direction: AnimDirection) {
385                    #[allow(non_snake_case)]
386                    let ($($type,)*) = self;
387                    $(
388                        $type.advance(index, direction);
389                    )*
390                }
391
392                fn finish(&mut self, direction: AnimDirection) {
393                    #[allow(non_snake_case)]
394                    let ($($type,)*) = self;
395                    $(
396                        $type.finish(direction);
397                    )*
398                }
399
400               fn into_reversed(self) -> Self {
401                    #[allow(non_snake_case)]
402                    let ($($type,)*) = self;
403                    (
404                        $(
405                            $type.into_reversed(),
406                        )*
407                    )
408                }
409            }
410            impl<$($type,)*> ReadAnimatedValue for  ($($type,)*)
411            where
412                $($type: ReadAnimatedValue,)*
413            {
414                type Output = (
415                    $(
416                        <$type as ReadAnimatedValue>::Output,
417                    )*
418                );
419                fn value(&self) -> Self::Output {
420                    #[allow(non_snake_case)]
421                    let ($($type,)*) = self;
422                    (
423                        $(
424                            $type.value(),
425                        )*
426                    )
427                }
428            }
429        )*
430    };
431}
432
433impl_tuple_call!(
434    (T1),
435    (T1, T2),
436    (T1, T2, T3),
437    (T1, T2, T3, T4),
438    (T1, T2, T3, T4, T5),
439    (T1, T2, T3, T4, T5, T6),
440    (T1, T2, T3, T4, T5, T6, T7),
441    (T1, T2, T3, T4, T5, T6, T7, T8),
442    (T1, T2, T3, T4, T5, T6, T7, T8, T9),
443    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10),
444    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11),
445    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
446);