freya_animation/
hook.rs

1use std::{
2    ops::Deref,
3    time::{
4        Duration,
5        Instant,
6    },
7};
8
9use async_io::Timer;
10use freya_core::prelude::*;
11
12#[derive(Default, PartialEq, Clone, Debug)]
13pub struct AnimConfiguration {
14    on_finish: OnFinish,
15    on_creation: OnCreation,
16    on_change: OnChange,
17}
18
19impl AnimConfiguration {
20    pub fn on_finish(&mut self, on_finish: OnFinish) -> &mut Self {
21        self.on_finish = on_finish;
22        self
23    }
24
25    pub fn on_creation(&mut self, on_creation: OnCreation) -> &mut Self {
26        self.on_creation = on_creation;
27        self
28    }
29
30    pub fn on_change(&mut self, on_change: OnChange) -> &mut Self {
31        self.on_change = on_change;
32        self
33    }
34}
35
36/// Controls the direction of the animation.
37#[derive(Clone, Copy, PartialEq)]
38pub enum AnimDirection {
39    Forward,
40    Reverse,
41}
42
43impl AnimDirection {
44    pub fn toggle(&mut self) {
45        match self {
46            Self::Forward => *self = Self::Reverse,
47            Self::Reverse => *self = Self::Forward,
48        }
49    }
50}
51
52/// What to do once the animation finishes.
53///
54/// By default it is [OnFinish::Nothing].
55#[derive(PartialEq, Clone, Copy, Default, Debug)]
56pub enum OnFinish {
57    /// Does nothing at all.
58    #[default]
59    Nothing,
60    /// Runs the animation in reverse direction.
61    Reverse {
62        /// Delay before reversing.
63        delay: Duration,
64    },
65    /// Runs the animation in the same direction again.
66    Restart {
67        /// Delay before restarting.
68        delay: Duration,
69    },
70}
71
72impl OnFinish {
73    /// Creates a new [OnFinish::Nothing] variant.
74    pub fn nothing() -> Self {
75        Self::Nothing
76    }
77
78    /// Creates a new [OnFinish::Reverse] variant with no delay.
79    pub fn reverse() -> Self {
80        Self::Reverse {
81            delay: Duration::ZERO,
82        }
83    }
84
85    /// Creates a new [OnFinish::Reverse] variant with a delay.
86    pub fn reverse_with_delay(delay: Duration) -> Self {
87        Self::Reverse { delay }
88    }
89
90    /// Creates a new [OnFinish::Restart] variant with no delay.
91    pub fn restart() -> Self {
92        Self::Restart {
93            delay: Duration::ZERO,
94        }
95    }
96
97    /// Creates a new [OnFinish::Restart] variant with a delay.
98    pub fn restart_with_delay(delay: Duration) -> Self {
99        Self::Restart { delay }
100    }
101}
102
103/// What to do once the animation gets created.
104///
105/// By default it is [OnCreation::Nothing]
106#[derive(PartialEq, Clone, Copy, Default, Debug)]
107pub enum OnCreation {
108    /// Does nothing at all.
109    #[default]
110    Nothing,
111    /// Runs the animation.
112    Run,
113    /// Set the values to the end of the animation. As if it had actually run.
114    Finish,
115}
116
117/// What to do once the animation dependencies change.
118///
119/// Defaults to [OnChange::Reset].
120#[derive(PartialEq, Clone, Copy, Default, Debug)]
121pub enum OnChange {
122    /// Reset to the initial state.
123    #[default]
124    Reset,
125    /// Set the values to the end of the animation.
126    Finish,
127    /// Reruns the animation.
128    Rerun,
129    /// Does nothing at all.
130    Nothing,
131}
132
133pub trait ReadAnimatedValue: Clone + 'static {
134    type Output;
135    fn value(&self) -> Self::Output;
136}
137
138pub trait AnimatedValue: Clone + Default + 'static {
139    fn prepare(&mut self, direction: AnimDirection);
140
141    fn is_finished(&self, index: u128, direction: AnimDirection) -> bool;
142
143    fn advance(&mut self, index: u128, direction: AnimDirection);
144
145    fn finish(&mut self, direction: AnimDirection);
146
147    fn into_reversed(self) -> Self;
148}
149
150#[derive(Default, Clone, Copy, PartialEq, Eq)]
151pub enum Ease {
152    In,
153    #[default]
154    Out,
155    InOut,
156}
157
158/// Animate your elements. Use [`use_animation`] to use this.
159#[derive(Clone, PartialEq)]
160pub struct UseAnimation<Animated: AnimatedValue> {
161    pub(crate) animated_value: State<Animated>,
162    pub(crate) config: State<AnimConfiguration>,
163    pub(crate) is_running: State<bool>,
164    pub(crate) has_run_yet: State<bool>,
165    pub(crate) task: State<Option<TaskHandle>>,
166    pub(crate) last_direction: State<AnimDirection>,
167}
168impl<T: AnimatedValue> Copy for UseAnimation<T> {}
169
170impl<Animated: AnimatedValue> Deref for UseAnimation<Animated> {
171    type Target = State<Animated>;
172    fn deref(&self) -> &Self::Target {
173        &self.animated_value
174    }
175}
176
177impl<Animated: AnimatedValue> UseAnimation<Animated> {
178    /// Get the animated value.
179    pub fn get(&self) -> ReadRef<'static, Animated> {
180        self.animated_value.read()
181    }
182
183    /// Runs the animation normally.
184    pub fn start(&mut self) {
185        self.run(AnimDirection::Forward)
186    }
187
188    /// Runs the animation in reverse direction.
189    pub fn reverse(&mut self) {
190        self.run(AnimDirection::Reverse)
191    }
192
193    /// Reset the animation with the initial state.
194    pub fn reset(&mut self) {
195        if let Some(task) = self.task.write().take() {
196            task.cancel();
197        }
198
199        self.animated_value
200            .write()
201            .prepare(*self.last_direction.peek());
202
203        *self.has_run_yet.write() = true;
204    }
205
206    /// Finish the animation with the final state.
207    pub fn finish(&mut self) {
208        if let Some(task) = self.task.write().take() {
209            task.cancel();
210        }
211
212        self.animated_value
213            .write()
214            .finish(*self.last_direction.peek());
215
216        *self.has_run_yet.write() = true;
217    }
218
219    pub fn is_running(&self) -> State<bool> {
220        self.is_running
221    }
222
223    pub fn has_run_yet(&self) -> State<bool> {
224        self.has_run_yet
225    }
226
227    /// Run the animation with a given [`AnimDirection`]
228    pub fn run(&self, mut direction: AnimDirection) {
229        let mut is_running = self.is_running;
230        let mut has_run_yet = self.has_run_yet;
231        let mut task = self.task;
232        let mut last_direction = self.last_direction;
233
234        let on_finish = self.config.peek().on_finish;
235        let mut animated_value = self.animated_value;
236
237        last_direction.set(direction);
238
239        // Cancel previous animations
240        if let Some(task) = task.write().take() {
241            task.cancel();
242        }
243
244        let peek_has_run_yet = *self.has_run_yet.peek();
245
246        let mut ticker = RenderingTicker::get();
247        let platform = Platform::get();
248        let animation_clock = AnimationClock::get();
249
250        let animation_task = spawn(async move {
251            platform.send(UserEvent::RequestRedraw);
252
253            let mut index = 0u128;
254            let mut prev_frame = Instant::now();
255
256            // Prepare the animations with the the proper direction
257            animated_value.write().prepare(direction);
258
259            if !peek_has_run_yet {
260                *has_run_yet.write() = true;
261            }
262            is_running.set(true);
263
264            loop {
265                // Wait for the event loop to tick
266                ticker.tick().await;
267
268                // Request another redraw to move the animation forward
269                platform.send(UserEvent::RequestRedraw);
270
271                let elapsed = animation_clock.correct_elapsed_duration(prev_frame.elapsed());
272
273                index += elapsed.as_millis();
274
275                let is_finished = {
276                    let mut animated_value = animated_value.write();
277                    let is_finished = animated_value.is_finished(index, direction);
278                    // Advance the animations
279                    animated_value.advance(index, direction);
280
281                    is_finished
282                };
283
284                if is_finished {
285                    let delay = match on_finish {
286                        OnFinish::Reverse { delay } => {
287                            // Toggle direction
288                            direction.toggle();
289                            delay
290                        }
291                        OnFinish::Restart { delay } => delay,
292                        OnFinish::Nothing => {
293                            // Stop if all the animations are finished
294                            break;
295                        }
296                    };
297
298                    if !delay.is_zero() {
299                        Timer::after(delay).await;
300                    }
301
302                    index = 0;
303
304                    // Restart/reverse the animation
305                    animated_value.write().prepare(direction);
306                }
307
308                prev_frame = Instant::now();
309            }
310
311            is_running.set(false);
312            task.write().take();
313        });
314
315        // Cancel previous animations
316        task.write().replace(animation_task);
317    }
318}
319/// Animate your UI easily.
320///
321/// [`use_animation`] takes a callback to initialize the animated values and related configuration.
322///
323/// To animate a group of values at once you can just return a tuple of them.
324///
325/// Currently supports animating:
326/// - Numeric values (e.g width): [AnimNum](crate::prelude::AnimNum)
327/// - Colors using [AnimColor](crate::prelude::AnimColor)
328/// - Sequential animations: [AnimSequential](crate::prelude::AnimSequential)
329/// - Anything as long as you implement the [AnimatedValue] trait.
330///
331/// For each animated value you will need to specify the duration, optionally an ease function or what type of easing you want.
332///
333/// # Example
334///
335/// Here is an example that animates a value from `0.0` to `100.0` in `50` milliseconds.
336///
337/// ```rust, no_run
338/// # use freya::prelude::*;
339/// # use freya::animation::*;
340/// fn app() -> impl IntoElement {
341///     let animation = use_animation(|conf| {
342///         conf.on_creation(OnCreation::Run);
343///         AnimNum::new(0., 100.).time(50)
344///     });
345///
346///     let width = animation.get().value();
347///
348///     rect()
349///         .width(Size::px(width))
350///         .height(Size::fill())
351///         .background(Color::BLUE)
352/// }
353/// ```
354///
355/// You are not limited to just one animation per call, you can have as many as you want.
356///
357/// ```rust, no_run
358/// # use freya::prelude::*;
359/// # use freya::animation::*;
360/// fn app() -> impl IntoElement {
361///     let animation = use_animation(|conf| {
362///         conf.on_creation(OnCreation::Run);
363///         (
364///             AnimNum::new(0., 100.).time(50),
365///             AnimColor::new(Color::RED, Color::BLUE).time(50),
366///         )
367///     });
368///
369///     let (width, color) = animation.get().value();
370///
371///     rect()
372///         .width(Size::px(width))
373///         .height(Size::fill())
374///         .background(color)
375/// }
376/// ```
377///
378/// You can also tweak what to do once the animation has finished with [`AnimConfiguration::on_finish`].
379///
380/// ```rust, no_run
381/// # use freya::prelude::*;
382/// # use freya::animation::*;
383/// fn app() -> impl IntoElement {
384///     let animation = use_animation(|conf| {
385///         conf.on_finish(OnFinish::restart());
386///         // ...
387///         # AnimNum::new(0., 1.)
388///     });
389///
390///     // ...
391///     # rect()
392/// }
393/// ```
394///
395/// You can subscribe your animation to reactive [state](freya_core::prelude::use_state) values, these are considered dependencies.
396///
397/// ```rust, no_run
398/// # use freya::prelude::*;
399/// # use freya::animation::*;
400/// fn app() -> impl IntoElement {
401///     let value = use_state(|| 100.);
402///
403///     let animation = use_animation(move |conf| {
404///         conf.on_change(OnChange::Rerun);
405///         AnimNum::new(0., value()).time(50)
406///     });
407///
408///     // ...
409///     # rect()
410/// }
411/// ```
412///
413/// You may also use [use_animation_with_dependencies] to pass non-reactive dependencies as well.
414pub fn use_animation<Animated: AnimatedValue>(
415    mut run: impl 'static + FnMut(&mut AnimConfiguration) -> Animated,
416) -> UseAnimation<Animated> {
417    use_hook(|| {
418        let mut config = State::create(AnimConfiguration::default());
419        let mut animated_value = State::create(Animated::default());
420        let is_running = State::create(false);
421        let has_run_yet = State::create(false);
422        let task = State::create(None);
423        let last_direction = State::create(AnimDirection::Forward);
424
425        let mut animation = UseAnimation {
426            animated_value,
427            config,
428            is_running,
429            has_run_yet,
430            task,
431            last_direction,
432        };
433
434        Effect::create_sync(move || {
435            let mut anim_conf = AnimConfiguration::default();
436            animated_value.set(run(&mut anim_conf));
437            *config.write() = anim_conf;
438        });
439
440        Effect::create_sync_with_gen(move |current_gen| match config.read().on_change {
441            OnChange::Finish if current_gen > 0 => {
442                animation.finish();
443            }
444            OnChange::Rerun if current_gen > 0 => {
445                let last_direction = *animation.last_direction.peek();
446                animation.run(last_direction);
447            }
448            OnChange::Reset if current_gen > 0 => {
449                animation.reset();
450            }
451            _ => {}
452        });
453
454        match config.peek().on_creation {
455            OnCreation::Run => {
456                animation.run(AnimDirection::Forward);
457            }
458            OnCreation::Finish => {
459                animation.finish();
460            }
461            _ => {}
462        }
463
464        animation
465    })
466}
467
468/// Like [use_animation] but supports passing manual dependencies.
469///
470/// ```rust, no_run
471/// # use freya::prelude::*;
472/// # use freya::animation::*;
473/// fn app() -> impl IntoElement {
474///     # let other_value = 1.;
475///     let animation = use_animation_with_dependencies(&other_value, |conf, other_value| {
476///         conf.on_change(OnChange::Rerun);
477///         AnimNum::new(0., *other_value).time(50)
478///     });
479///
480///     // ...
481///     # rect()
482/// }
483/// ```
484pub fn use_animation_with_dependencies<Animated: AnimatedValue, D: 'static + Clone + PartialEq>(
485    dependencies: &D,
486    mut run: impl 'static + FnMut(&mut AnimConfiguration, &D) -> Animated,
487) -> UseAnimation<Animated> {
488    let dependencies = use_reactive(dependencies);
489    use_hook(|| {
490        let mut config = State::create(AnimConfiguration::default());
491        let mut animated_value = State::create(Animated::default());
492        let is_running = State::create(false);
493        let has_run_yet = State::create(false);
494        let task = State::create(None);
495        let last_direction = State::create(AnimDirection::Forward);
496
497        let mut animation = UseAnimation {
498            animated_value,
499            config,
500            is_running,
501            has_run_yet,
502            task,
503            last_direction,
504        };
505
506        Effect::create_sync(move || {
507            let dependencies = dependencies.read();
508            let mut anim_conf = AnimConfiguration::default();
509            animated_value.set(run(&mut anim_conf, &dependencies));
510            *config.write() = anim_conf;
511        });
512
513        Effect::create_sync_with_gen(move |current_gen| match config.read().on_change {
514            OnChange::Finish if current_gen > 0 => {
515                animation.finish();
516            }
517            OnChange::Rerun if current_gen > 0 => {
518                let last_direction = *animation.last_direction.peek();
519                animation.run(last_direction);
520            }
521            OnChange::Reset if current_gen > 0 => {
522                animation.reset();
523            }
524            _ => {}
525        });
526
527        match config.peek().on_creation {
528            OnCreation::Run => {
529                animation.run(AnimDirection::Forward);
530            }
531            OnCreation::Finish => {
532                animation.finish();
533            }
534            _ => {}
535        }
536
537        animation
538    })
539}
540
541macro_rules! impl_tuple_call {
542    ($(($($type:ident),*)),*) => {
543        $(
544            impl<$($type,)*> AnimatedValue for ($($type,)*)
545            where
546                $($type: AnimatedValue,)*
547            {
548                fn prepare(&mut self, direction: AnimDirection) {
549                    #[allow(non_snake_case)]
550                    let ($($type,)*) = self;
551                    $(
552                        $type.prepare(direction);
553                    )*
554                }
555
556                fn is_finished(&self, index: u128, direction: AnimDirection) -> bool {
557                    #[allow(non_snake_case)]
558                    let ($($type,)*) = self;
559                    $(
560                        if !$type.is_finished(index, direction) {
561                            return false;
562                        }
563                    )*
564                    true
565                }
566
567                fn advance(&mut self, index: u128, direction: AnimDirection) {
568                    #[allow(non_snake_case)]
569                    let ($($type,)*) = self;
570                    $(
571                        $type.advance(index, direction);
572                    )*
573                }
574
575                fn finish(&mut self, direction: AnimDirection) {
576                    #[allow(non_snake_case)]
577                    let ($($type,)*) = self;
578                    $(
579                        $type.finish(direction);
580                    )*
581                }
582
583               fn into_reversed(self) -> Self {
584                    #[allow(non_snake_case)]
585                    let ($($type,)*) = self;
586                    (
587                        $(
588                            $type.into_reversed(),
589                        )*
590                    )
591                }
592            }
593            impl<$($type,)*> ReadAnimatedValue for  ($($type,)*)
594            where
595                $($type: ReadAnimatedValue,)*
596            {
597                type Output = (
598                    $(
599                        <$type as ReadAnimatedValue>::Output,
600                    )*
601                );
602                fn value(&self) -> Self::Output {
603                    #[allow(non_snake_case)]
604                    let ($($type,)*) = self;
605                    (
606                        $(
607                            $type.value(),
608                        )*
609                    )
610                }
611            }
612        )*
613    };
614}
615
616impl_tuple_call!(
617    (T1),
618    (T1, T2),
619    (T1, T2, T3),
620    (T1, T2, T3, T4),
621    (T1, T2, T3, T4, T5),
622    (T1, T2, T3, T4, T5, T6),
623    (T1, T2, T3, T4, T5, T6, T7),
624    (T1, T2, T3, T4, T5, T6, T7, T8),
625    (T1, T2, T3, T4, T5, T6, T7, T8, T9),
626    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10),
627    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11),
628    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
629);