yew_hooks/hooks/
use_raf.rs

1use std::cmp::min_by;
2
3use gloo::render::request_animation_frame;
4use gloo::timers::callback::Timeout;
5use yew::prelude::*;
6
7/// An animation hook that forces component to re-render on each `requestAnimationFrame`,
8/// returns percentage of time elapsed. `millis` - milliseconds for how long to keep re-rendering component.
9/// `delay` — delay in milliseconds after which to start re-rendering component.
10///
11/// # Example
12///
13/// ```rust
14/// # use yew::prelude::*;
15/// #
16/// use yew_hooks::prelude::*;
17///
18/// #[function_component(UseRaf)]
19/// fn raf() -> Html {
20///     let elapsed = use_raf(5000, 1000);
21///     
22///     html! {
23///         <>
24///             { elapsed }
25///         </>
26///     }
27/// }
28/// ```
29#[hook]
30pub fn use_raf(millis: u32, delay: u32) -> f64 {
31    let elapsed = use_state(|| 0f64);
32    let start = use_mut_ref(|| 0f64);
33    let raf = use_mut_ref(|| None);
34    let on_frame = use_mut_ref(|| None);
35    let on_frame_clone = on_frame.clone();
36    let timer_stop = use_mut_ref(|| None);
37    let timer_delay = use_mut_ref(|| None);
38
39    {
40        let elapsed = elapsed.clone();
41        use_effect_with((millis, delay), move |(millis, delay)| {
42            let millis = *millis;
43            let delay = *delay;
44            *start.borrow_mut() = 0f64;
45
46            {
47                let raf = raf.clone();
48                let elapsed = elapsed.clone();
49                *on_frame_clone.borrow_mut() = Some(Box::new(move |time: f64| {
50                    let on_frame = on_frame.clone();
51                    if *start.borrow() <= 0f64 {
52                        *start.borrow_mut() = time;
53                    }
54                    let time = min_by(
55                        1f64,
56                        (time - *start.borrow()) / f64::from(millis),
57                        |x, y| x.partial_cmp(y).unwrap(),
58                    );
59                    elapsed.set(time);
60
61                    // Schedule ourself for another requestAnimationFrame callback.
62                    // Ref: https://github.com/rustwasm/wasm-bindgen/blob/main/examples/request-animation-frame/src/lib.rs
63                    *raf.borrow_mut() = Some(request_animation_frame(move |time| {
64                        let on_frame = on_frame.borrow();
65                        #[allow(clippy::borrowed_box)]
66                        let on_frame: &Box<dyn Fn(f64)> = on_frame.as_ref().unwrap();
67                        on_frame(time);
68                    }));
69                }) as Box<dyn Fn(f64)>);
70            }
71
72            {
73                let raf = raf.clone();
74                let timer_stop = timer_stop.clone();
75                *timer_delay.borrow_mut() = Some(Timeout::new(delay, move || {
76                    {
77                        let raf = raf.clone();
78                        *timer_stop.borrow_mut() = Some(Timeout::new(millis, move || {
79                            *raf.borrow_mut() = None;
80                            elapsed.set(1f64);
81                        }));
82                    }
83
84                    *raf.borrow_mut() = Some(request_animation_frame(move |time| {
85                        let on_frame_clone = on_frame_clone.borrow();
86                        #[allow(clippy::borrowed_box)]
87                        let on_frame_clone: &Box<dyn Fn(f64)> = on_frame_clone.as_ref().unwrap();
88                        on_frame_clone(time);
89                    }));
90                }));
91            }
92
93            move || {
94                *raf.borrow_mut() = None;
95                *timer_stop.borrow_mut() = None;
96                *timer_delay.borrow_mut() = None;
97            }
98        });
99    }
100
101    *elapsed
102}
OSZAR »