web-sys: A Simple Paint Program - The `wasm-bindgen` Guide

# Paint Example

View full source code or view the compiled example online

A simple painting program.

## `Cargo.toml`

The `Cargo.toml` enables features necessary to work with the DOM, events and 2D canvas.

    [package]
    authors = ["The wasm-bindgen Developers"]
    edition = "2021"
    name = "wasm-bindgen-paint"
    publish = false
    version = "0.0.0"

    [lib]
    crate-type = ["cdylib"]

    [dependencies]
    js-sys = { path = "../../crates/js-sys" }
    wasm-bindgen = { path = "../../" }

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

    [lints]
    workspace = true

## `src/lib.rs`

Creates the `<canvas>` element, applies a CSS style to it, adds it to the document, get a 2D rendering context and adds listeners for mouse events.

    #![allow(unused)]
    fn main() {
    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(())
    }
    }