dioxus_spring/
controller.rs1use futures::Future;
2use slotmap::{DefaultKey, SlotMap};
3use std::{
4 cell::RefCell,
5 pin::Pin,
6 rc::Rc,
7 task::{Context, Poll, Waker},
8};
9use wasm_bindgen::{prelude::Closure, JsCast};
10
11pub async fn request_animation_frame() {
12 RequestFuture { key: None }.await
13}
14
15#[derive(Default)]
16struct Control {
17 wakers: SlotMap<DefaultKey, Waker>,
18 pending: Option<Rc<Closure<dyn FnMut()>>>,
19}
20
21thread_local! {
22 static CONTROL: RefCell<Option<Control>> = RefCell::new(None);
23}
24
25struct RequestFuture {
26 key: Option<DefaultKey>,
27}
28
29impl Future for RequestFuture {
30 type Output = ();
31
32 fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
33 CONTROL
34 .try_with(|cell| {
35 if let Some(key) = self.key {
36 let maybe_controller = cell.borrow();
37 let controller = maybe_controller.as_ref().unwrap();
38 if controller.wakers.get(key).is_none() {
39 Poll::Ready(())
40 } else {
41 Poll::Pending
42 }
43 } else {
44 let mut maybe_controller = cell.borrow_mut();
45 if maybe_controller.is_none() {
46 *maybe_controller = Some(Control::default());
47 }
48 let controller = maybe_controller.as_mut().unwrap();
49
50 let key = controller.wakers.insert(cx.waker().clone());
51 self.key = Some(key);
52
53 if controller.pending.is_none() {
54 let f: Closure<dyn FnMut()> = Closure::new(move || {
55 CONTROL
56 .try_with(|cell| {
57 let mut maybe_controller = cell.borrow_mut();
58 let controller = maybe_controller.as_mut().unwrap();
59
60 for waker in controller.wakers.values() {
61 waker.wake_by_ref();
62 }
63 controller.wakers.clear();
64 controller.pending.take();
65 })
66 .unwrap();
67 });
68
69 web_sys::window()
70 .unwrap()
71 .request_animation_frame(f.as_ref().as_ref().unchecked_ref())
72 .unwrap();
73 controller.pending = Some(Rc::new(f));
74 }
75
76 Poll::Pending
77 }
78 })
79 .unwrap()
80 }
81}