LoginSignup
27

More than 3 years have passed since last update.

RustのWebAssembly で canvas に描画する

Last updated at Posted at 2019-10-13

RustのWebAssembly で canvas に描画する

rust の wasm-pack を使用して canvas に図形を描画する。

環境

  • Ubuntu 18.04
  • rustc 1.38.0

wasm-pack をインストールする

wasm-pack
https://github.com/rustwasm/wasm-pack

install にインストール方法が記載されている。

$ curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

wasm-pack の node.js のプロジェクトを作成する

ドキュメントの Getting Started のコマンドを実行してプロジェクトを作成する。

$ npm init rust-webpack my-app
npx: 18個のパッケージを1.555秒でインストールしました。
 🦀 Rust + 🕸 WebAssembly + Webpack = ❤️
 Installed dependencies ✅

生成された package.json を確認する。

{
  "author": "You <you@example.com>",
  "name": "rust-webpack-template",
  "version": "0.1.0",
  "scripts": {
    "build": "rimraf dist pkg && webpack",
    "start": "rimraf dist pkg && webpack-dev-server --open -d",
    "test": "cargo test && wasm-pack test --headless"
  },
  "devDependencies": {
    "@wasm-tool/wasm-pack-plugin": "^0.4.2",
    "copy-webpack-plugin": "^5.0.3",
    "webpack": "^4.33.0",
    "webpack-cli": "^3.3.3",
    "webpack-dev-server": "^3.7.1",
    "rimraf": "^2.6.3"
  }
}

src/lib.rs には console に Hello wrold! が出力されるコードが記述されている。

// This is like the `main` function, except for JavaScript.
#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
    // This provides better error messages in debug mode.
    // It's disabled in release mode so it doesn't bloat up the file size.
    #[cfg(debug_assertions)]
    console_error_panic_hook::set_once();


    // Your code goes here!
    console::log_1(&JsValue::from_str("Hello world!"));

    Ok(())
}

npm start を実行すると Web サーバが起動する。

$ cd my-app/
$ npm start
> rust-webpack-template@0.1.0 start /tmp/my-app
> rimraf dist pkg && webpack-dev-server --open -d

🧐  Checking for wasm-pack...

✅  wasm-pack is installed.

ℹ️  Compiling your crate in development mode...

ℹ 「wds」: Project is running at http://localhost:8080/
ℹ 「wds」: webpack output is served from /
(省略)
Version: webpack 4.41.1
Time: 121ms
Built at: 2019-10-12 21:27:07
 3 assets
Entrypoint index = index.js
[./pkg/index.js] 4.38 KiB {0} [built]
[./pkg/index_bg.wasm] 145 KiB {0} [built]
    + 33 hidden modules
ℹ 「wdm」: Compiled successfully.

http://localhost:8080/ を開くと console に Hello world! が出力される。
console.png

canvas に描画する

ドキュメントを参考にコードを実装する。
https://rustwasm.github.io/docs/wasm-bindgen/examples/2d-canvas.html

Cargo.tomlweb-sysの項目を以下のように変更する。

features に Rust から操作する要素の API を追加する。

# The `web-sys` crate allows you to interact with the various browser APIs,
# like the DOM.
[dependencies.web-sys]
version = "0.3.22"
features = [
  "console",
  'CanvasRenderingContext2d',
  'Document',
  'Element',
  'HtmlCanvasElement',
  'Window',
]

static/index.htmlcanvas を追加する。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>My Rust + Webpack project!</title>
  </head>
  <body>
    <script src="index.js"></script>
    <canvas id="canvas"></canvas>
  </body>
</html>

src/lib.rscanvas に描画するためのコードを追加する。

  1. ドキュメントの start 関数の #[wasm_bindgen(start)] を削除してコピペ。
  2. main_js 関数に start() を追加。
use std::f64;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::console;

// When the `wee_alloc` feature is enabled, this uses `wee_alloc` as the global
// allocator.
//
// If you don't want to use `wee_alloc`, you can safely delete this.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

// This is like the `main` function, except for JavaScript.
#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
    // This provides better error messages in debug mode.
    // It's disabled in release mode so it doesn't bloat up the file size.
    #[cfg(debug_assertions)]
    console_error_panic_hook::set_once();

    // Your code goes here!
    console::log_1(&JsValue::from_str("Hello world!"));

    start();

    Ok(())
}

pub fn start() {
    let document = web_sys::window().unwrap().document().unwrap();
    let canvas = document.get_element_by_id("canvas").unwrap();
    let canvas: web_sys::HtmlCanvasElement = canvas
        .dyn_into::<web_sys::HtmlCanvasElement>()
        .map_err(|_| ())
        .unwrap();

    let context = canvas
        .get_context("2d")
        .unwrap()
        .unwrap()
        .dyn_into::<web_sys::CanvasRenderingContext2d>()
        .unwrap();

    context.begin_path();

    // Draw the outer circle.
    context
        .arc(75.0, 75.0, 50.0, 0.0, f64::consts::PI * 2.0)
        .unwrap();

    // Draw the mouth.
    context.move_to(110.0, 75.0);
    context.arc(75.0, 75.0, 35.0, 0.0, f64::consts::PI).unwrap();

    // Draw the left eye.
    context.move_to(65.0, 65.0);
    context
        .arc(60.0, 65.0, 5.0, 0.0, f64::consts::PI * 2.0)
        .unwrap();

    // Draw the right eye.
    context.move_to(95.0, 65.0);
    context
        .arc(90.0, 65.0, 5.0, 0.0, f64::consts::PI * 2.0)
        .unwrap();

    context.stroke();
}

npm start で起動した http://localhost:8080/ を開くと canvas に絵が描画されている。
drawing-canvas.png

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27