wasm_repeated_animation_frame/
lib.rs

1use async_channel::{Receiver, Sender};
2use futures::{future::select_all, FutureExt};
3use js_sys::Function;
4use manual_future::ManualFuture;
5use wasm_bindgen::{closure::Closure, JsCast};
6use wasm_bindgen_futures::spawn_local;
7use web_sys::window;
8
9pub struct RafLoopCanceler {
10    sender: Sender<()>,
11}
12
13impl RafLoopCanceler {
14    pub async fn cancel(&self) {
15        self.sender.send(()).await.unwrap();
16    }
17}
18
19pub struct RafLoop {
20    handle: Option<i32>,
21    receiver: Receiver<()>,
22}
23
24impl RafLoop {
25    pub fn new() -> (RafLoop, RafLoopCanceler) {
26        let (sender, receiver) = async_channel::bounded::<()>(1);
27        (
28            RafLoop {
29                handle: None,
30                receiver,
31            },
32            RafLoopCanceler { sender },
33        )
34    }
35
36    pub fn cancel(&mut self) {
37        window()
38            .unwrap()
39            .cancel_animation_frame(self.handle.unwrap())
40            .unwrap();
41        self.handle = None;
42    }
43
44    pub async fn next(&mut self) -> bool {
45        let (future, completer) = ManualFuture::<()>::new();
46        let closure = Closure::once(Box::new(move || {
47            spawn_local(completer.complete(()));
48        }) as Box<dyn FnOnce()>);
49        let handle = window()
50            .unwrap()
51            .request_animation_frame(&(closure.into_js_value().unchecked_into::<Function>()))
52            .unwrap();
53        self.handle = Some(handle);
54        enum Event {
55            Stop,
56            Raf,
57        }
58        let (event, _, _) = select_all(vec![
59            async {
60                future.await;
61                Event::Raf
62            }
63            .boxed(),
64            async {
65                self.receiver.recv().await.unwrap();
66                Event::Stop
67            }
68            .boxed(),
69        ])
70        .await;
71        match event {
72            Event::Raf => true,
73            Event::Stop => false,
74        }
75    }
76}