use std::cell::UnsafeCell; use std::rc::Rc; use log::error; use wasm_bindgen::prelude::*; /// use `window.requestAnimationFrame()` to schedule calling a /// function as long as the function returns true. pub struct RenderLoop { inner: Rc>>, } impl RenderLoop { fn request_animation_frame(f: &Closure) -> Result<(), JsValue> { web_sys::window() .ok_or("no window")? .request_animation_frame(f.as_ref().unchecked_ref())?; Ok(()) } /// `fun` takes a timestamp in the same space as the document /// timeline and returns a flag specifying whether we should /// schedule another frame.. pub fn new bool + 'static>(mut fun: T) -> Self { // init with stub closure because rust wants that, then change // it later once we have our rc clone. let inner = Rc::new(UnsafeCell::new(Closure::new(|_| {}))); let rloop = inner.clone(); let f = unsafe { &mut *inner.get() }; *f = Closure::new(move |t| { if fun(t) { let cl = unsafe { &*rloop.get() }; if let Err(e) = Self::request_animation_frame(cl) { error!("couldn't request animation frame: {:?}", e); } } }); Self { inner } } /// start animating. pub fn start(&self) -> Result<(), JsValue> { let cl = unsafe { &*self.inner.get() }; Self::request_animation_frame(cl)?; Ok(()) } } impl Clone for RenderLoop { fn clone(&self) -> Self { Self { inner: self.inner.clone(), } } }