1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ViteでRust+WebAssembly入門

Posted at

Rust+WebAssemblyの入門記事です。

環境はこの記事を書いている最新版で、
vite: 5.4.11, node: v22.12.0, npm: 11.0.0, cargo: 1.83.0, wasm-pack: 0.13.1, macos: 15.3

以下のyoutube記事を参考にしています
https://www.youtube.com/watch?v=8zDYoprO358

✅ wasm-packが未インストールであればインストール:
$ cargo install wasm-pack

✅ wasm-packプロジェクトの生成(プロジェクト名は任意で、今回はwasm-mandelbrot):
$ wasm-pack new wasm-mandelbrot 

✅ 同一ディレクトリ内にViteプロジェクトを生成:
$ cd wasm-mandelbrot
$ npm init vite .  # 最後のピリオドに注意

今回は、メニューから以下を選択:
“Ignore existing files and continue>  Svelte > JavaScript

✅ node依存モジュールを追加 (これらが無いとブラウザーで派手なエラーが出る):
$ npm install -D vite-plugin-wasm vite-plugin-top-level-await

✅ vite.config.jsに以下を追加:
import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";

そしてpluginsに2項目を追加:
plugins: [svelte(), wasm(), topLevelAwait()],

✅ src/lib.rsを下記コードで置き換える(ChatGPT4oに書いてもらった)
lib.rs

mod utils;
use wasm_bindgen::prelude::*;
use web_sys::{ImageData, console};
use wasm_bindgen::Clamped;

#[wasm_bindgen]
pub fn generate_mandelbrot(width: u32, height: u32, color: &str) -> ImageData {
    let mut data = vec![0; (width * height * 4) as usize];
    let (r, g, b) = hex_to_rgb(color);

    for y in 0..height {
        for x in 0..width {
            let cx = x as f64 / width as f64 * 3.5 - 2.5;
            let cy = y as f64 / height as f64 * 2.0 - 1.0;
            let mut zx = 0.0;
            let mut zy = 0.0;
            let mut i = 0;
            while zx * zx + zy * zy < 4.0 && i < 255 {
                let tmp = zx * zx - zy * zy + cx;
                zy = 2.0 * zx * zy + cy;
                zx = tmp;
                i += 1;
            }
            let pixel_index = (y * width + x) as usize * 4;
            data[pixel_index] = (r * i as f64) as u8;
            data[pixel_index + 1] = (g * i as f64) as u8;
            data[pixel_index + 2] = (b * i as f64) as u8;
            data[pixel_index + 3] = 255;
        }
    }

    console::log_1(&"Mandelbrot generation complete".into());
    ImageData::new_with_u8_clamped_array_and_sh(Clamped(&data[..]), width, height).unwrap()
}

fn hex_to_rgb(hex: &str) -> (f64, f64, f64) {
    let hex = hex.trim_start_matches('#');
    let r = u8::from_str_radix(&hex[0..2], 16).unwrap() as f64 / 255.0;
    let g = u8::from_str_radix(&hex[2..4], 16).unwrap() as f64 / 255.0;
    let b = u8::from_str_radix(&hex[4..6], 16).unwrap() as f64 / 255.0;
    (r, g, b)
}


✅ クレートの追加:
$ cargo add web-sys
✅ Cargo.tomlの[dependences]に以下を追加:
web-sys = { version = "0.3.61", features = ["CanvasRenderingContext2d", "ImageData", "console",] }

✅ src/App.svelteを下記コードで置き換える
App.svelte
<script>
  import { onMount } from 'svelte';
  import { generate_mandelbrot } from '../pkg/wasm_mandelbrot';

  let canvas;
  let width = 1280;
  let height = 800;
  let color = '#ffff00';

  const drawMandelbrot = async () => {
    const ctx = canvas.getContext('2d');
    console.log("Canvas context:", ctx);
    const imageData = generate_mandelbrot(width, height, color);
    console.log("Generated ImageData:", imageData);
    ctx.putImageData(imageData, 0, 0);
  };

  onMount(drawMandelbrot);
</script>

<div>
  <label>
    Width:
    <input type="number" bind:value={width} min="1" />
  </label>
  <label>
    Height:
    <input type="number" bind:value={height} min="1" />
  </label>
  <label>
    Color:
    <input type="color" bind:value={color} />
  </label>
  <button on:click={drawMandelbrot}>Draw</button>
</div>

<canvas bind:this={canvas} width={width} height={height}></canvas>
✅ lib.rsをWebAssemblyにコンパイル:
$ wasm-pack build

✅ webサーバーを起動:
$ npm run dev

✅ ブラウザーで表示:
http://localhost:5173

以下のように表示される:スクリーンショット 2025-01-29 11.51.39.png

最後にプロジェクトのディレクトリ構造を示します(ゴミファイルはほとんど削除してます):

$ tree -L 2
├── Cargo.lock
├── Cargo.toml
├── LICENSE_APACHE
├── LICENSE_MIT
├── README.md
├── index.html
├── jsconfig.json
├── node_modules
│   ├── @ampproject
│   ~
│   └── zimmerframe
├── package-lock.json
├── package.json
├── pkg
│   ├── README.md
│   ├── package.json
│   ├── wasm_mandelbrot.d.ts
│   ├── wasm_mandelbrot.js
│   ├── wasm_mandelbrot_bg.js
│   ├── wasm_mandelbrot_bg.wasm
│   └── wasm_mandelbrot_bg.wasm.d.ts
├── public
├── src
│   ├── App.svelte
│   ├── lib.rs
│   ├── main.js
│   ├── utils.rs
│   └── vite-env.d.ts
├── svelte.config.js
├── target
│   ├── CACHEDIR.TAG
│   ├── debug
│   ├── release
│   ├── tmp
│   └── wasm32-unknown-unknown
├── tests
│   └── web.rs
└── vite.config.js

以上です。

1
1
0

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?