RustのWasmでDOM操作、Canvas操作、API操作のサンプルを作ってみた
alert処理
まずはjavascriptのalert関数を呼び出してみます。
受け取ったname引数をjavascript標準のalertを使って通知するだけの関数です。
#[wasm_bindgen]
pub fn js_alert(name: &str) {
alert(&format!("Hello, {}!", name));
}
下記のようなコマンドでビルド後、
$ wasm-pack build --release --target web -d ./dist/js
javascriptからは下記のように呼び出せます。
import init, { js_alert } from "../sample/dist/js/sample.js";
init().then(() => {
// js alertへのアクセス
js_alert("WebAssembly")
});
DOM操作
次に指定したIDのDOMに対してHtmlを挿入してみます。
#[wasm_bindgen]
pub fn insert_dom_message(param: &str) -> Result<(), JsValue> {
let window = web_sys::window().expect("no global `window` exists");
let document = window.document().expect("should have a document on window");
let element = document.get_element_by_id("set_message").unwrap();
let msg = format!("{}{}{}", "<p>Hello from Rust! ", param, "</p>");
element.insert_adjacent_html("afterbegin", &msg)?;
Ok(())
}
documentからIDを指定してelementを取得して、そのelementにhtmlをinsertしています。
こちらも先程と同じようにjavascript側から簡単に呼び出せます。
LocalStrage操作
LocalStrageも以下のようなコードでセット可能です。
pub fn set_local_storage(key: &str, value: &str) -> Result<(), JsValue> {
let window = web_sys::window().unwrap();
if let Ok(Some(local_storage)) = window.local_storage() {
local_storage.set_item(key, value).unwrap();
}
Ok(())
}
Canvas操作
Canvasも以下のようなコードで描画可能です。
#[wasm_bindgen]
pub fn draw_canvas() {
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();
}
ここではニコちゃんが描画されています。
rust wasmを学習するページではCanvasを使ったゲームの実装例が明示されています。
http get操作
以下はgithubのapiをコールしてブランチ情報を取得して返却しています。
#[wasm_bindgen]
pub async fn http_get() -> Result<JsValue, JsValue> {
let result = reqwest::Client::new()
.get("https://api.github.com/repos/rustwasm/wasm-bindgen/branches/master")
.header("Accept", "application/vnd.github.v3+json")
.send()
.await;
let res;
match result {
Ok(n) => res = n,
Err(e) => {
println!("failed to parse: {}", e);
return Err(JsValue::from_bool(false));
},
}
let result = res.text().await;
let text;
match result {
Ok(n) => text = n,
Err(e) => {
println!("failed to parse: {}", e);
return Err(JsValue::from_bool(false));
},
}
let branch_info: Branch = serde_json::from_str(&text).unwrap();
Ok(JsValue::from_serde(&branch_info).unwrap())
}
この辺りにweb requestの例などが掲載されています。
このhttp getの内部処理ですが、osリソースを使用したrustの通信処理が実行されているのではなくreqwest
の内部処理では下記のように、
あくまでjavascriptのfetch処理をrustがcallしているという仕組みのようです。
尚、セキュリティなどの関係もありwasmはOSリソースにはアクセスはできず、WebAssemblyとして許可されたリソースにしかアクセスできません。
fetch APIについては下記が詳しく掲載してくれています。
javascriptからの呼び出しは非同期になるので下記のようになります。
import init, { js_alert } from "../sample/dist/js/sample.js";
init().then(() => {
async function init_proc() {
// HTTP GET リクエスト
const resp_get = await http_get();
console.log(resp_get)
}
init_proc()
});
まとめ
rust wasmの交流の為のリポジトリも用意されているようです。
rust wasmに関する情報はまだ多くないので、不明点や議論したことがあれば、上記プルリクに乗っているdiscord serverなどに参加してみてもいいかもしれません。
wasmはjavascriptが担っていた操作のかなりの処理の代替も可能になっています。
rubyなどもwasmが使えるようになってきており、これから先もっとwasmの活用が活発になるかもしれません。
Discussion