はじめに
下記でAsciiアート生成ツールを作成した際は、Fabric.jsを利用してCanvas操作していたが、
今回はweb-sysというRustからJavaScriptのWeb APIにアクセスするクレートを利用して
Canvas操作しつつ写真ペイントツールを作成してみようと思う。🧑🎨
(Wasmから直接DOM操作はできないため、
JavaScriptのWeb APIで仲介しているだけなのでRustでやる意味はないかもしれません。🤔)
web-sysについて
プロジェクトの作成
下記に既にCanvasのペイント処理のExampleがあったのでこれを参考にしながら
スマホのTouch操作の処理を実装と、写真のCanvas表示を実装してみる。
取り込んだ写真をCanvasに表示するように実装、
web-sysを使ってstyleの設定とかもできた。
// 省略
let document = web_sys::window().unwrap().document().unwrap();
let canvas = document.create_element("canvas").unwrap()
.dyn_into::<web_sys::HtmlCanvasElement>().unwrap();
canvas.set_id("myCanvas");
document.body().unwrap().append_child(&canvas).unwrap();
canvas.set_width(img.width());
canvas.set_height(img.height());
canvas.style().set_property("border", "solid").unwrap();
let context =
canvas.get_context("2d").unwrap().unwrap()
.dyn_into::<web_sys::CanvasRenderingContext2d>().unwrap();
let context = Rc::new(context);
// image crate の画像データを RGBA のベクタに変換
let raw_pixels: Vec<u8> = match img {
DynamicImage::ImageRgba8(ref img_buffer) => img_buffer.clone().into_raw(),
_ => {
img.to_rgba8().into_raw()
}
};
let image_data = ImageData::new_with_u8_clamped_array_and_sh(
wasm_bindgen::Clamped(&mut raw_pixels.clone()),
img.width(),
img.height(),
).unwrap();
context.put_image_data(&image_data, 0.0, 0.0).unwrap();
// 省略
次にスマホのTouch操作の処理を実装する。
雑だが、touchmove中の座標をtouchend時に繋げるような感じで実装
// 省略
let closure = Closure::<dyn FnMut(_)>::new(move |event: web_sys::TouchEvent| {
// Canvas要素の位置を取得
let rect = canvas_clone.get_bounding_client_rect();
// タッチ座標をCanvas座標系に変換
let canvas_x = event.touches().get(0).unwrap().client_x() as f64 - rect.left();
let canvas_y = event.touches().get(0).unwrap().client_y() as f64 - rect.top();
// 現在の位置に矩形を描画
context.fill_rect(canvas_x, canvas_y, 1f64, 1f64);
my_xy_clone.borrow_mut().push(Point{x:canvas_x, y:canvas_y});
});
canvas.add_event_listener_with_callback("touchmove", closure.as_ref().unchecked_ref()).unwrap();
closure.forget();
// 省略
let closure = Closure::<dyn FnMut(_)>::new(move |_event: web_sys::TouchEvent| {
for i in 0..my_xy_clone.borrow().len(){
if i > 0 && i < my_xy_clone.borrow().len() - 1 {
context.begin_path();
context.move_to(my_xy_clone.borrow().get(i-1).unwrap().x,my_xy_clone.borrow().get(i-1).unwrap().y);
context.line_to(my_xy_clone.borrow().get(i).unwrap().x,my_xy_clone.borrow().get(i).unwrap().y);
context.stroke();
}
}
my_xy_clone.borrow_mut().clear();
});
canvas.add_event_listener_with_callback("touchend", closure.as_ref().unchecked_ref()).unwrap();
closure.forget();
// 省略
今回の成果物
デモURL
デモ画像
元画像
お絵描き後の画像
ソース
まとめ
Javascriptで直接操作したほうが、楽なような気がするが、
Rust初学者なため、こういったクレートを使うことも学習促進になるような気がした💪
まだまだ、理解できていないところも多いがコツコツと勉強していたい。
上記、画像が大きい場合に、Windowサイズを大きくはみ出てる課題があったため、
下記で修正版の記事を書きました。💪
※間違い等ありましたら、ご指摘いただけると助かります。