awsm_web/tick/
raf.rs

1use wasm_bindgen::prelude::*;
2use wasm_bindgen::JsCast;
3use std::rc::Rc;
4use std::cell::RefCell;
5
6pub struct Raf {
7    state: Rc<RefCell<Option<RafState>>>,
8}
9
10struct RafState {
11    id: i32,
12    closure: Closure<dyn FnMut(f64)>,
13}
14
15impl Raf {
16    pub fn new<F>(mut callback: F) -> Self where F: FnMut(f64) + 'static {
17        let state: Rc<RefCell<Option<RafState>>> = Rc::new(RefCell::new(None));
18
19        fn schedule(callback: &Closure<dyn FnMut(f64)>) -> i32 {
20            web_sys::window()
21                .unwrap_throw()
22                .request_animation_frame(callback.as_ref().unchecked_ref())
23                .unwrap_throw()
24        }
25
26        let closure = {
27            let state = state.clone();
28
29            Closure::wrap(Box::new(move |time| {
30                {
31                    let mut state = state.borrow_mut();
32                    let state = state.as_mut().unwrap_throw();
33                    state.id = schedule(&state.closure);
34                }
35
36                callback(time);
37            }) as Box<dyn FnMut(f64)>)
38        };
39
40        *state.borrow_mut() = Some(RafState {
41            id: schedule(&closure),
42            closure
43        });
44
45        Self { state }
46    }
47}
48
49impl Drop for Raf {
50    fn drop(&mut self) {
51        // The take is necessary in order to prevent an Rc leak
52        let state = self.state.borrow_mut().take().unwrap_throw();
53
54        web_sys::window()
55            .unwrap_throw()
56            .cancel_animation_frame(state.id)
57            .unwrap_throw();
58    }
59}