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    /// Get the last configured direction used to animation.
184    pub fn direction(&self) -> ReadRef<'static, AnimDirection> {
185        self.last_direction.read()
186    }
187
188    /// Runs the animation normally.
189    pub fn start(&mut self) {
190        self.run(AnimDirection::Forward)
191    }
192
193    /// Runs the animation in reverse direction.
194    pub fn reverse(&mut self) {
195        self.run(AnimDirection::Reverse)
196    }
197
198    /// Reset the animation with the initial state.
199    pub fn reset(&mut self) {
200        if let Some(task) = self.task.write().take() {
201            task.cancel();
202        }
203
204        self.animated_value
205            .write()
206            .prepare(*self.last_direction.peek());
207
208        *self.has_run_yet.write() = true;
209    }
210
211    /// Finish the animation with the final state.
212    pub fn finish(&mut self) {
213        if let Some(task) = self.task.write().take() {
214            task.cancel();
215        }
216
217        self.animated_value
218            .write()
219            .finish(*self.last_direction.peek());
220
221        *self.has_run_yet.write() = true;
222    }
223
224    pub fn is_running(&self) -> State<bool> {
225        self.is_running
226    }
227
228    pub fn has_run_yet(&self) -> State<bool> {
229        self.has_run_yet
230    }
231
232    /// Run the animation with a given [`AnimDirection`]
233    pub fn run(&self, mut direction: AnimDirection) {
234        let mut is_running = self.is_running;
235        let mut has_run_yet = self.has_run_yet;
236        let mut task = self.task;
237        let mut last_direction = self.last_direction;
238
239        let on_finish = self.config.peek().on_finish;
240        let mut animated_value = self.animated_value;
241
242        last_direction.set(direction);
243
244        // Cancel previous animations
245        if let Some(task) = task.write().take() {
246            task.cancel();
247        }
248
249        let peek_has_run_yet = *self.has_run_yet.peek();
250
251        let mut ticker = RenderingTicker::get();
252        let platform = Platform::get();
253        let animation_clock = AnimationClock::get();
254
255        let animation_task = spawn(async move {
256            platform.send(UserEvent::RequestRedraw);
257
258            let mut index = 0u128;
259            let mut prev_frame = Instant::now();
260
261            // Prepare the animations with the the proper direction
262            animated_value.write().prepare(direction);
263
264            if !peek_has_run_yet {
265                *has_run_yet.write() = true;
266            }
267            is_running.set(true);
268
269            loop {
270                // Wait for the event loop to tick
271                ticker.tick().await;
272
273                // Request another redraw to move the animation forward
274                platform.send(UserEvent::RequestRedraw);
275
276                let elapsed = animation_clock.correct_elapsed_duration(prev_frame.elapsed());
277
278                index += elapsed.as_millis();
279
280                let is_finished = {
281                    let mut animated_value = animated_value.write();
282                    let is_finished = animated_value.is_finished(index, direction);
283                    // Advance the animations
284                    animated_value.advance(index, direction);
285
286                    is_finished
287                };
288
289                if is_finished {
290                    let delay = match on_finish {
291                        OnFinish::Reverse { delay } => {
292                            // Toggle direction
293                            direction.toggle();
294                            delay
295                        }
296                        OnFinish::Restart { delay } => delay,
297                        OnFinish::Nothing => {
298                            // Stop if all the animations are finished
299                            break;
300                        }
301                    };
302
303                    if !delay.is_zero() {
304                        Timer::after(delay).await;
305                    }
306
307                    index = 0;
308
309                    // Restart/reverse the animation
310                    animated_value.write().prepare(direction);
311                }
312
313                prev_frame = Instant::now();
314            }
315
316            is_running.set(false);
317            task.write().take();
318        });
319
320        // Cancel previous animations
321        task.write().replace(animation_task);
322    }
323}
324/// Animate your UI easily.
325///
326/// [`use_animation`] takes a callback to initialize the animated values and related configuration.
327///
328/// To animate a group of values at once you can just return a tuple of them.
329///
330/// Currently supports animating:
331/// - Numeric values (e.g width): [AnimNum](crate::prelude::AnimNum)
332/// - Colors using [AnimColor](crate::prelude::AnimColor)
333/// - Sequential animations: [AnimSequential](crate::prelude::AnimSequential)
334/// - Anything as long as you implement the [AnimatedValue] trait.
335///
336/// For each animated value you will need to specify the duration, optionally an ease function or what type of easing you want.
337///
338/// # Example
339///
340/// Here is an example that animates a value from `0.0` to `100.0` in `50` milliseconds.
341///
342/// ```rust, no_run
343/// # use freya::prelude::*;
344/// # use freya::animation::*;
345/// fn app() -> impl IntoElement {
346///     let animation = use_animation(|conf| {
347///         conf.on_creation(OnCreation::Run);
348///         AnimNum::new(0., 100.).time(50)
349///     });
350///
351///     let width = animation.get().value();
352///
353///     rect()
354///         .width(Size::px(width))
355///         .height(Size::fill())
356///         .background(Color::BLUE)
357/// }
358/// ```
359///
360/// You are not limited to just one animation per call, you can have as many as you want.
361///
362/// ```rust, no_run
363/// # use freya::prelude::*;
364/// # use freya::animation::*;
365/// fn app() -> impl IntoElement {
366///     let animation = use_animation(|conf| {
367///         conf.on_creation(OnCreation::Run);
368///         (
369///             AnimNum::new(0., 100.).time(50),
370///             AnimColor::new(Color::RED, Color::BLUE).time(50),
371///         )
372///     });
373///
374///     let (width, color) = animation.get().value();
375///
376///     rect()
377///         .width(Size::px(width))
378///         .height(Size::fill())
379///         .background(color)
380/// }
381/// ```
382///
383/// You can also tweak what to do once the animation has finished with [`AnimConfiguration::on_finish`].
384///
385/// ```rust, no_run
386/// # use freya::prelude::*;
387/// # use freya::animation::*;
388/// fn app() -> impl IntoElement {
389///     let animation = use_animation(|conf| {
390///         conf.on_finish(OnFinish::restart());
391///         // ...
392///         # AnimNum::new(0., 1.)
393///     });
394///
395///     // ...
396///     # rect()
397/// }
398/// ```
399///
400/// You can subscribe your animation to reactive [state](freya_core::prelude::use_state) values, these are considered dependencies.
401///
402/// ```rust, no_run
403/// # use freya::prelude::*;
404/// # use freya::animation::*;
405/// fn app() -> impl IntoElement {
406///     let value = use_state(|| 100.);
407///
408///     let animation = use_animation(move |conf| {
409///         conf.on_change(OnChange::Rerun);
410///         AnimNum::new(0., value()).time(50)
411///     });
412///
413///     // ...
414///     # rect()
415/// }
416/// ```
417///
418/// You may also use [use_animation_with_dependencies] to pass non-reactive dependencies as well.
419pub fn use_animation<Animated: AnimatedValue>(
420    mut run: impl 'static + FnMut(&mut AnimConfiguration) -> Animated,
421) -> UseAnimation<Animated> {
422    use_hook(|| {
423        let mut config = State::create(AnimConfiguration::default());
424        let mut animated_value = State::create(Animated::default());
425        let is_running = State::create(false);
426        let has_run_yet = State::create(false);
427        let task = State::create(None);
428        let last_direction = State::create(AnimDirection::Forward);
429
430        let mut animation = UseAnimation {
431            animated_value,
432            config,
433            is_running,
434            has_run_yet,
435            task,
436            last_direction,
437        };
438
439        Effect::create_sync(move || {
440            let mut anim_conf = AnimConfiguration::default();
441            animated_value.set(run(&mut anim_conf));
442            *config.write() = anim_conf;
443        });
444
445        Effect::create_sync_with_gen(move |current_gen| match config.read().on_change {
446            OnChange::Finish if current_gen > 0 => {
447                animation.finish();
448            }
449            OnChange::Rerun if current_gen > 0 => {
450                let last_direction = *animation.last_direction.peek();
451                animation.run(last_direction);
452            }
453            OnChange::Reset if current_gen > 0 => {
454                animation.reset();
455            }
456            _ => {}
457        });
458
459        match config.peek().on_creation {
460            OnCreation::Run => {
461                animation.run(AnimDirection::Forward);
462            }
463            OnCreation::Finish => {
464                animation.finish();
465            }
466            _ => {}
467        }
468
469        animation
470    })
471}
472
473/// Like [use_animation] but supports passing manual dependencies.
474///
475/// ```rust, no_run
476/// # use freya::prelude::*;
477/// # use freya::animation::*;
478/// fn app() -> impl IntoElement {
479///     # let other_value = 1.;
480///     let animation = use_animation_with_dependencies(&other_value, |conf, other_value| {
481///         conf.on_change(OnChange::Rerun);
482///         AnimNum::new(0., *other_value).time(50)
483///     });
484///
485///     // ...
486///     # rect()
487/// }
488/// ```
489pub fn use_animation_with_dependencies<Animated: AnimatedValue, D: 'static + Clone + PartialEq>(
490    dependencies: &D,
491    mut run: impl 'static + FnMut(&mut AnimConfiguration, &D) -> Animated,
492) -> UseAnimation<Animated> {
493    let dependencies = use_reactive(dependencies);
494    use_hook(|| {
495        let mut config = State::create(AnimConfiguration::default());
496        let mut animated_value = State::create(Animated::default());
497        let is_running = State::create(false);
498        let has_run_yet = State::create(false);
499        let task = State::create(None);
500        let last_direction = State::create(AnimDirection::Forward);
501
502        let mut animation = UseAnimation {
503            animated_value,
504            config,
505            is_running,
506            has_run_yet,
507            task,
508            last_direction,
509        };
510
511        Effect::create_sync(move || {
512            let dependencies = dependencies.read();
513            let mut anim_conf = AnimConfiguration::default();
514            animated_value.set(run(&mut anim_conf, &dependencies));
515            *config.write() = anim_conf;
516        });
517
518        Effect::create_sync_with_gen(move |current_gen| match config.read().on_change {
519            OnChange::Finish if current_gen > 0 => {
520                animation.finish();
521            }
522            OnChange::Rerun if current_gen > 0 => {
523                let last_direction = *animation.last_direction.peek();
524                animation.run(last_direction);
525            }
526            OnChange::Reset if current_gen > 0 => {
527                animation.reset();
528            }
529            _ => {}
530        });
531
532        match config.peek().on_creation {
533            OnCreation::Run => {
534                animation.run(AnimDirection::Forward);
535            }
536            OnCreation::Finish => {
537                animation.finish();
538            }
539            _ => {}
540        }
541
542        animation
543    })
544}
545
546macro_rules! impl_tuple_call {
547    ($(($($type:ident),*)),*) => {
548        $(
549            impl<$($type,)*> AnimatedValue for ($($type,)*)
550            where
551                $($type: AnimatedValue,)*
552            {
553                fn prepare(&mut self, direction: AnimDirection) {
554                    #[allow(non_snake_case)]
555                    let ($($type,)*) = self;
556                    $(
557                        $type.prepare(direction);
558                    )*
559                }
560
561                fn is_finished(&self, index: u128, direction: AnimDirection) -> bool {
562                    #[allow(non_snake_case)]
563                    let ($($type,)*) = self;
564                    $(
565                        if !$type.is_finished(index, direction) {
566                            return false;
567                        }
568                    )*
569                    true
570                }
571
572                fn advance(&mut self, index: u128, direction: AnimDirection) {
573                    #[allow(non_snake_case)]
574                    let ($($type,)*) = self;
575                    $(
576                        $type.advance(index, direction);
577                    )*
578                }
579
580                fn finish(&mut self, direction: AnimDirection) {
581                    #[allow(non_snake_case)]
582                    let ($($type,)*) = self;
583                    $(
584                        $type.finish(direction);
585                    )*
586                }
587
588               fn into_reversed(self) -> Self {
589                    #[allow(non_snake_case)]
590                    let ($($type,)*) = self;
591                    (
592                        $(
593                            $type.into_reversed(),
594                        )*
595                    )
596                }
597            }
598            impl<$($type,)*> ReadAnimatedValue for  ($($type,)*)
599            where
600                $($type: ReadAnimatedValue,)*
601            {
602                type Output = (
603                    $(
604                        <$type as ReadAnimatedValue>::Output,
605                    )*
606                );
607                fn value(&self) -> Self::Output {
608                    #[allow(non_snake_case)]
609                    let ($($type,)*) = self;
610                    (
611                        $(
612                            $type.value(),
613                        )*
614                    )
615                }
616            }
617        )*
618    };
619}
620
621impl_tuple_call!(
622    (T1),
623    (T1, T2),
624    (T1, T2, T3),
625    (T1, T2, T3, T4),
626    (T1, T2, T3, T4, T5),
627    (T1, T2, T3, T4, T5, T6),
628    (T1, T2, T3, T4, T5, T6, T7),
629    (T1, T2, T3, T4, T5, T6, T7, T8),
630    (T1, T2, T3, T4, T5, T6, T7, T8, T9),
631    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10),
632    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11),
633    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
634);