use std::cell::Cell; use std::rc::Rc; use std::time::Duration; use wasm_bindgen::closure::Closure; use wasm_bindgen::JsCast; #[derive(Debug)] pub struct Timeout { handle: i32, _closure: Closure, } impl Timeout { pub fn new(f: F, duration: Duration) -> Timeout where F: 'static + FnMut(), { let window = web_sys::window().expect("Failed to obtain window"); let closure = Closure::wrap(Box::new(f) as Box); let handle = window .set_timeout_with_callback_and_timeout_and_arguments_0( closure.as_ref().unchecked_ref(), duration.as_millis() as i32, ) .expect("Failed to set timeout"); Timeout { handle, _closure: closure, } } } impl Drop for Timeout { fn drop(&mut self) { let window = web_sys::window().expect("Failed to obtain window"); window.clear_timeout_with_handle(self.handle); } } #[derive(Debug)] pub struct AnimationFrameRequest { handle: i32, // track callback state, because `cancelAnimationFrame` is slow fired: Rc>, _closure: Closure, } impl AnimationFrameRequest { pub fn new(mut f: F) -> AnimationFrameRequest where F: 'static + FnMut(), { let window = web_sys::window().expect("Failed to obtain window"); let fired = Rc::new(Cell::new(false)); let c_fired = fired.clone(); let closure = Closure::wrap(Box::new(move || { (*c_fired).set(true); f(); }) as Box); let handle = window .request_animation_frame(closure.as_ref().unchecked_ref()) .expect("Failed to request animation frame"); AnimationFrameRequest { handle, fired, _closure: closure, } } } impl Drop for AnimationFrameRequest { fn drop(&mut self) { if !(*self.fired).get() { let window = web_sys::window().expect("Failed to obtain window"); window .cancel_animation_frame(self.handle) .expect("Failed to cancel animation frame"); } } }