1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
use dioxus::prelude::RefCell;
use futures::StreamExt;
use std::rc::Rc;
use wasm_bindgen::{prelude::Closure, JsCast};

struct Controller {
    tx: async_channel::Sender<()>,
    rx: async_channel::Receiver<()>,
    pending: Option<Rc<Closure<dyn FnMut()>>>,
}

thread_local! {
    static CONTROLLER: RefCell<Option<Controller>> = RefCell::new(None);
}

pub async fn request_animation_frame() {
    let (request_cell, mut rx) = CONTROLLER
        .try_with(|cell| {
            let mut cx = cell.borrow_mut();
            let controller = if let Some(controller) = &mut *cx {
                controller
            } else {
                let (tx, rx) = async_channel::unbounded();
                *cx = Some(Controller {
                    tx,
                    rx,
                    pending: None,
                });
                cx.as_mut().unwrap()
            };

            let request_cell = if controller.pending.is_none() {
                let tx = controller.tx.clone();
                let f: Closure<dyn FnMut()> = Closure::new(move || {
                    tx.send_blocking(()).unwrap();

                    CONTROLLER
                        .try_with(|cell| {
                            let mut cx = cell.borrow_mut();
                            cx.as_mut().unwrap().pending.take();
                        })
                        .unwrap();
                });

                controller.pending = Some(Rc::new(f));
                controller.pending.clone()
            } else {
                None
            };
            (request_cell, controller.rx.clone())
        })
        .unwrap();

    if let Some(f) = request_cell {
        web_sys::window()
            .unwrap()
            .request_animation_frame(f.as_ref().as_ref().unchecked_ref())
            .unwrap();
    }

    rx.next().await;
}