1#![deny(missing_docs, missing_debug_implementations)]
5
6use std::cell::RefCell;
7use std::fmt;
8use std::rc::Rc;
9use wasm_bindgen::prelude::*;
10use wasm_bindgen::JsCast;
11
12#[derive(Debug)]
14pub struct AnimationFrame {
15 render_id: i32,
16 _closure: Closure<dyn Fn(JsValue)>,
17 callback_wrapper: Rc<RefCell<Option<CallbackWrapper>>>,
18}
19
20struct CallbackWrapper(Box<dyn FnOnce(f64) + 'static>);
21impl fmt::Debug for CallbackWrapper {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 f.write_str("CallbackWrapper")
24 }
25}
26
27impl Drop for AnimationFrame {
28 fn drop(&mut self) {
29 if self.callback_wrapper.borrow_mut().is_some() {
30 web_sys::window()
31 .unwrap_throw()
32 .cancel_animation_frame(self.render_id)
33 .unwrap_throw()
34 }
35 }
36}
37
38pub fn request_animation_frame<F>(callback_once: F) -> AnimationFrame
42where
43 F: FnOnce(f64) + 'static,
44{
45 let callback_wrapper = Rc::new(RefCell::new(Some(CallbackWrapper(Box::new(callback_once)))));
46 let callback: Closure<dyn Fn(JsValue)> = {
47 let callback_wrapper = Rc::clone(&callback_wrapper);
48 Closure::wrap(Box::new(move |v: JsValue| {
49 let time: f64 = v.as_f64().unwrap_or(0.0);
50 let callback = callback_wrapper.borrow_mut().take().unwrap().0;
51 callback(time);
52 }))
53 };
54
55 let render_id = web_sys::window()
56 .unwrap_throw()
57 .request_animation_frame(callback.as_ref().unchecked_ref())
58 .unwrap_throw();
59
60 AnimationFrame {
61 render_id,
62 _closure: callback,
63 callback_wrapper,
64 }
65}