gloo_render/
lib.rs

1//! Crate that provides wrapper for
2//! [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame)
3
4#![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/// Handle for [`request_animation_frame`].
13#[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
38/// Calls browser's `requestAnimationFrame`. It is cancelled when the handler is dropped.
39///
40/// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame)
41pub 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}