web-sys: A requestAnimationFrame Loop

View full source code or view the compiled example online

This is an example of a requestAnimationFrame loop using the web-sys crate! It renders a count of how many times a requestAnimationFrame callback has been invoked and then it breaks out of the requestAnimationFrame loop after 300 iterations.

Cargo.toml

You can see here how we depend on web-sys and activate associated features to enable all the various APIs:

[package] authors = ["The wasm-bindgen Developers"] edition = "2021" name = "request-animation-frame" publish = false version = "0.0.0" [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = { path = "../../" } [dependencies.web-sys] features = ['Document', 'Element', 'HtmlElement', 'Node', 'Window'] path = "../../crates/web-sys" [lints] workspace = true

src/lib.rs

use std::cell::RefCell; use std::rc::Rc; use wasm_bindgen::prelude::*; fn window() -> web_sys::Window { web_sys::window().expect("no global `window` exists") } fn request_animation_frame(f: &Closure<dyn FnMut()>) { window() .request_animation_frame(f.as_ref().unchecked_ref()) .expect("should register `requestAnimationFrame` OK"); } fn document() -> web_sys::Document { window() .document() .expect("should have a document on window") } fn body() -> web_sys::HtmlElement { document().body().expect("document should have a body") } // This function is automatically invoked after the Wasm module is instantiated. #[wasm_bindgen(start)] fn run() -> Result<(), JsValue> { // Here we want to call `requestAnimationFrame` in a loop, but only a fixed // number of times. After it's done we want all our resources cleaned up. To // achieve this we're using an `Rc`. The `Rc` will eventually store the // closure we want to execute on each frame, but to start out it contains // `None`. // // After the `Rc` is made we'll actually create the closure, and the closure // will reference one of the `Rc` instances. The other `Rc` reference is // used to store the closure, request the first frame, and then is dropped // by this function. // // Inside the closure we've got a persistent `Rc` reference, which we use // for all future iterations of the loop let f = Rc::new(RefCell::new(None)); let g = f.clone(); let mut i = 0; *g.borrow_mut() = Some(Closure::new(move || { if i > 300 { body().set_text_content(Some("All done!")); // Drop our handle to this closure so that it will get cleaned // up once we return. let _ = f.borrow_mut().take(); return; } // Set the body's text content to how many times this // requestAnimationFrame callback has fired. i += 1; let text = format!("requestAnimationFrame has been called {} times.", i); body().set_text_content(Some(&text)); // Schedule ourself for another requestAnimationFrame callback. request_animation_frame(f.borrow().as_ref().unwrap()); })); request_animation_frame(g.borrow().as_ref().unwrap()); Ok(()) }