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]
name = "request-animation-frame"
version = "0.1.0"
authors = ["The wasm-bindgen Developers"]
edition = "2018"
rust-version = "1.57"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2.92"
[dependencies.web-sys]
version = "0.3.4"
features = [
'Document',
'Element',
'HtmlElement',
'Node',
'Window',
]
src/lib.rs
#![allow(unused)] fn main() { 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(()) } }