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!
が出力される。
canvas に描画する
ドキュメントを参考にコードを実装する。
https://rustwasm.github.io/docs/wasm-bindgen/examples/2d-canvas.html
Cargo.toml
の web-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.html
に canvas
を追加する。
<!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.rs
に canvas
に描画するためのコードを追加する。
- ドキュメントの
start
関数の#[wasm_bindgen(start)]
を削除してコピペ。 -
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
に絵が描画されている。