Help us understand the problem. What is going on with this article?

RustのWebAssembly で canvas に描画する

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした