Previous example: <-- web-sys: A requestAnimationFrame Loop

web-sys: A simple painting program.

A simple painting program.
_ wasm-bindgen Guide{target="_blank"}

web-sys: A simple painting program{target="_blank"}

This time we do everything from the Rust side.

setup the project

cargo new paint --lib
cd paint
mkdir -p www/js www/html
cargo add wasm-bindgen js-sys
  • Edit Cargo.toml
[lib]
crate-type = ["cdylib"]

[dependencies]
js-sys = "0.3.66"
wasm-bindgen = "0.2.89"

[dependencies.web-sys]
version = "0.3.66"
features = [
  'CanvasRenderingContext2d',
  'CssStyleDeclaration',
  'Document',
  'Element',
  'EventTarget',
  'HtmlCanvasElement',
  'HtmlElement',
  'MouseEvent',
  'Node',
  'Window',
]

The code

  • index.html
<!doctype html>
<html>
  <head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
    <title>Paint: nobundle</title>
  </head>
  <body>
    <script type="module" src="../js/index.js"></script>
  </body>
</html>
  • index.js
import init from '../pkg/paint.js';

const run =  async () => {
    await init().catch(console.error);
};

/*
async function run() {
    const wasm = await init().catch(console.error);

} 
*/

run();
  • Rust side
#![allow(unused)]
fn main() {
// src/lib.rs
use std::cell::Cell;
use std::rc::Rc;
use wasm_bindgen::prelude::*;

#[wasm_bindgen(start)]
fn start() -> Result<(), JsValue> {
    let document = web_sys::window().unwrap().document().unwrap();
    let canvas = document
        .create_element("canvas")?
        .dyn_into::<web_sys::HtmlCanvasElement>()?;
    document.body().unwrap().append_child(&canvas)?;
    canvas.set_width(640);
    canvas.set_height(480);
    canvas.style().set_property("border", "solid")?;
    let context = canvas
        .get_context("2d")?
        .unwrap()
        .dyn_into::<web_sys::CanvasRenderingContext2d>()?;
    let context = Rc::new(context);
    let pressed = Rc::new(Cell::new(false));
    {
        let context = context.clone();
        let pressed = pressed.clone();
        let closure = Closure::<dyn FnMut(_)>::new(move |event: web_sys::MouseEvent| {
            context.begin_path();
            context.move_to(event.offset_x() as f64, event.offset_y() as f64);
            pressed.set(true);
        });
        canvas.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())?;
        closure.forget();
    }
    {
        let context = context.clone();
        let pressed = pressed.clone();
        let closure = Closure::<dyn FnMut(_)>::new(move |event: web_sys::MouseEvent| {
            if pressed.get() {
                context.line_to(event.offset_x() as f64, event.offset_y() as f64);
                context.stroke();
                context.begin_path();
                context.move_to(event.offset_x() as f64, event.offset_y() as f64);
            }
        });
        canvas.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())?;
        closure.forget();
    }
    {
        let closure = Closure::<dyn FnMut(_)>::new(move |event: web_sys::MouseEvent| {
            pressed.set(false);
            context.line_to(event.offset_x() as f64, event.offset_y() as f64);
            context.stroke();
        });
        canvas.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref())?;
        closure.forget();
    }

    Ok(())
}
}

build and serve

wasm-pack build --target web --no-typescript --out-dir www/pkg

http www

open index.html

firefox http://localhost:8000/html/

What's next?

Next example: web-sys: Wasm in Web Worker -->