Skip to main content

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