← レッスン一覧に戻る レッスン 11 / 28
中級 graphicsdom
Canvas と 2Dグラフィックス
はじめに
HTML Canvasは、Rust/Wasmの最適なユースケースの一つです。Rustが計算処理(物理演算、ゲームロジック、プロシージャル生成)を担当し、Canvas APIがレンダリングを処理します。この分担により、重い処理部分でネイティブに近いパフォーマンスが得られます。
セットアップ
[dependencies.web-sys]
version = "0.3"
features = [
"Window",
"Document",
"HtmlCanvasElement",
"CanvasRenderingContext2d",
]Canvasコンテキストの取得
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
fn get_context(canvas_id: &str) -> Result<CanvasRenderingContext2d, JsValue> {
let document = web_sys::window().unwrap().document().unwrap();
let canvas = document
.get_element_by_id(canvas_id)
.unwrap()
.dyn_into::<HtmlCanvasElement>()?;
canvas
.get_context("2d")?
.unwrap()
.dyn_into::<CanvasRenderingContext2d>()
}基本図形の描画
// 矩形
ctx.set_fill_style_str("#654ff0");
ctx.fill_rect(10.0, 10.0, 100.0, 50.0);
// 円
ctx.begin_path();
ctx.arc(200.0, 200.0, 40.0, 0.0, std::f64::consts::PI * 2.0)?;
ctx.set_fill_style_str("#ce422b");
ctx.fill();
// 線
ctx.begin_path();
ctx.move_to(0.0, 0.0);
ctx.line_to(300.0, 150.0);
ctx.set_stroke_style_str("#34d399");
ctx.set_line_width(2.0);
ctx.stroke();
// テキスト
ctx.set_font("24px Inter");
ctx.set_fill_style_str("#f1f5f9");
ctx.fill_text("Hello Wasm!", 50.0, 50.0)?;アニメーションループ
アニメーションはJavaScript側でrequestAnimationFrameを使って実行し、各フレームでRustを呼び出します:
import init, { Canvas } from './pkg/my_app.js';
async function run() {
await init();
const canvas = new Canvas("game-canvas");
let x = 0;
function frame() {
canvas.clear();
canvas.draw_circle(x, 300, 20, "#654ff0");
x = (x + 2) % 800;
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
run();マウス入力
use web_sys::MouseEvent;
use wasm_bindgen::closure::Closure;
pub fn setup_mouse(canvas: &HtmlCanvasElement) -> Result<(), JsValue> {
let closure = Closure::wrap(Box::new(move |event: MouseEvent| {
let x = event.offset_x() as f64;
let y = event.offset_y() as f64;
// (x, y) でのクリックを処理
}) as Box<dyn FnMut(_)>);
canvas.add_event_listener_with_callback(
"click",
closure.as_ref().unchecked_ref(),
)?;
closure.forget();
Ok(())
}画像の描画
use web_sys::HtmlImageElement;
pub fn draw_image(ctx: &CanvasRenderingContext2d, src: &str, x: f64, y: f64)
-> Result<(), JsValue>
{
let img = HtmlImageElement::new()?;
img.set_src(src);
let ctx = ctx.clone();
let closure = Closure::once(move || {
ctx.draw_image_with_html_image_element(&img, x, y).unwrap();
});
img.set_onload(Some(closure.as_ref().unchecked_ref()));
closure.forget();
Ok(())
}パフォーマンス比較:Canvas vs WebGL
| 特徴 | Canvas 2D | WebGL |
|---|---|---|
| セットアップの複雑さ | 低い | 高い |
| 描画呼び出し | 約1,000回/フレーム | 約10,000回/フレーム |
| シェーダー | なし | あり |
| 3D | なし | あり |
| 最適な用途 | 2Dゲーム、チャート、UI | 3D、大量パーティクルシステム |
ほとんどの2D Rust/Wasmプロジェクトでは、Canvas 2Dで十分であり、はるかにシンプルです。
試してみよう
スターターコードは、基本的な描画メソッドを持つCanvasラッパー構造体を示しています。実際のプロジェクトでは、JavaScriptのアニメーションループからこれらのメソッドを呼び出します。
試してみる
チャプタークイズ
すべての問題に正解してレッスンを完了しましょう