「TAURI」で「丸アートお絵描き」アプリを作ろう

2023年12月19日(火)
大西 武 (オオニシ タケシ)
第7回の今回は、前回に引き続きコーディングをしていき、「TAURI」で円を描画するだけでお絵描できるアプリを完成させていきます。

アンドゥの実装

ここまでで基本的な操作はほとんど実装しているのですが、さらに完成度を上げるために「アンドゥ」できるようにしたり、canvasの幅高さのサイズを変更できるようにしたり、ペンのプレビューを変更できるようにしたりします。

それでは「src」→「main.js」ファイルをコーディングしてアンドゥを実装し、「$ cargo-tauri dev」コマンドでデバッグビルドしてください。画面上部のアンドゥボタンでアンドゥできるようになりましたね。

また「リドゥ」ボタンも付けてリドゥ機能を実装しても良いでしょう。その場合はタイミングが大事で、アンドゥするときにアンドゥデータをcanvasにセットする直前のcanvasの幅と高さと画像データをリドゥデータの後ろに追加(push)します。

・「src」→「main.js」でアンドゥするサンプルコード
01const { invoke } = window.__TAURI__.tauri;
02const { open,save } = window.__TAURI__.dialog;
03 
04let _canvas = document.querySelector('canvas');
05let _context = _canvas.getContext('2d');
06//アンドゥのデータ
07let _undo = [];
08(中略)
09//円を描く
10function maru(event) {
11  let w = _canvas.width;
12  let h = _canvas.height;
13  let img = _context.getImageData(0, 0, w, h);
14  _undo.push([w,h,img]);
15  let pos = getPos(event);
16  let color = 'rgb('+2*_red.value/3+','+2*_green.value/3+','+2*_blue.value/3+')';
17  _context.beginPath();
18  _context.lineWidth = 1;
19  _context.strokeStyle = color;
20  color = 'rgb('+_red.value+','+_green.value+','+_blue.value+')';
21  _context.fillStyle = color;
22  _context.arc(pos[0],pos[1],_pen.value,0,Math.PI*2,true);
23  _context.fill();
24  _context.stroke();
25}
26//アンドゥ
27function undoImage() {
28  if ( _undo.length > 0 ) {
29    let undo = _undo[_undo.length-1];
30    _canvas.width = undo[0];
31    _canvas.height = undo[1];
32    _context.putImageData(undo[2],0,0);
33    _undo.pop();
34    _width.value = _canvas.width;
35    _height.value = _canvas.height;
36 }
37}
38(後略)

【サンプルコードの解説】
アンドゥデータは第0インデックスにcanvasの幅を、第1インデックスにcanvasの高さを、第2インデックスにcanvasの画像データを入れた配列を、さらに「_undo」配列の後ろに追加(pushメソッド)します。アンドゥデータを保存するタイミングはcanvasをクリックして円を描く直前です。
「index.html」のアンドゥボタンが押されたら「undoImage」関数が呼ばれます。_undo配列のlengthが0より大きければアンドゥデータが存在するので、_undo配列の最後の要素からcanvasの幅と高さと画像データを取得してセットし、_undo配列の最後の要素を削除(popメソッド)します。

canvasの幅高さを変更できるようにする

このままではcanvasの幅高さを指定したサイズに変更できないので、幅高さをUIで数値を入れて指定できるようにします。次のように「src」→「main.js」ファイルをコーディングして「$ cargo-tauri dev」コマンドを実行すると幅高さが反映されるようになりましたね。

また、canvasサイズの変更によりウィンドウサイズを超えてスクロールバーが出るので、「src-tauri」→「tauri.conf.json」ファイルでアプリ実行時のデフォルトのウィンドウの幅高さを変更してやります。

ところで、例えば図4のような塗り絵の輪郭線図があれば、誰でもそれなりの絵が描けるようになるでしょう。この画像を「名前を付けて保存」した後、今回のアプリで「開く」ボタンから読み込んで下書きにして、ヒマワリの丸アートを描いてみてください。

図4:ヒマワリの塗り絵図

・「src」→「main.js」でcanvasの幅高さを変更するサンプルコード
01(前略)
02//キャンバスの幅
03let _width = document.querySelector('#width');
04//キャンバスの高さ
05let _height = document.querySelector('#height');
06//HTMLの読み込みが終了したとき
07window.addEventListener("DOMContentLoaded", () => {
08  //キャンバスでマウスクリックが離されたとき
09  _canvas.addEventListener("mouseup", (e) => { maru(e); });
10  _canvas.width = 800;
11  _canvas.height = 600;
12  _width.value = _canvas.width;
13  _height.value = _canvas.height;
14});
15//キャンバスの幅を変更
16function changeWidth(text) {
17  let w = _canvas.width;
18  let h = _canvas.height;
19  let img = _context.getImageData(0, 0, w, h);
20  _canvas.width = text.value;
21  _context.putImageData(img,0,0);
22}
23//キャンバスの高さを変更
24function changeHeight(text) {
25  let w = _canvas.width;
26  let h = _canvas.height;
27  let img = _context.getImageData(0, 0, w, h);
28  _canvas.height = text.value;
29  _context.putImageData(img,0,0);
30}
31(後略)

【サンプルコードの解説】
canvasの幅の「#width」セレクタを「_width」変数に、canvasの高さの「#height」セレクタを「_height」変数に代入します。
最初にcanvasの幅を800に、高さを600にセットします。
canvasの幅のテキストボックスの値が変更されると「changeWidth」関数が呼ばれ、canvasの高さのテキストボックスの値が変更されると「changeHeight」関数が呼ばれ、それまでの画像データを「img」変数に取得してから、それぞれ幅と高さを変更してcanvasに画像データ「img」変数を描画します。
さらに改造するならテキストボックスの値が数値かどうか、1~2000ぐらいかどうか調べて正しければ、canvasサイズを変更すると良いでしょう。

・「src-tauri」→「tauri.conf.json」でウィンドウ幅高さを変更するサンプルコード
01(前略)
02    "windows": [
03      {
04        "fullscreen": false,
05        "resizable": true,
06        "title": "viewer",
07        "width": 1000,
08        "height": 800
09      }
10    ]
11  }
12}

【サンプルコードの解説】
"windows"の"width"(ウィンドウの幅)を1000に、"height"(ウィンドウの高さ)を800に変更します。

ペンのプレビュー

今までのところ、ペン(筆)の赤緑青の色や太さを変えても実際にcanvasに塗るまでペンの実物が分かりません。そこで「src」→「main.js」ファイルをコーディングすると、図5のようにペンのプレビューが表示できるようになります。

イベントリスナーの追加(「addEventListener」メソッド)は、index.htmlの全てのDOMが読み込まれたときに1回だけ追加します。何度もaddEventListenerメソッドを呼ぶと、その回数だけ「setColor」関数が何度も呼ばれてしまいます。

それほど重要なことではないですが、プレビューの#colorセレクタの円の輪郭線と、canvasの「arc」メソッドの円の輪郭線の太さをどちらも1にしているのに微妙に太さが違います。CSSとJavascriptで異なるからでしょうか。

図5:ペン(筆)のプレビュー

・「src」→「main.js」でペンをプレビューするサンプルコード
01(前略)
02//ペンの色
03let _color = document.querySelector('#color');
04//HTMLの読み込みが終了したとき
05window.addEventListener("DOMContentLoaded", () => {
06  //キャンバスでマウスクリックが離されたとき
07  _canvas.addEventListener("mouseup", (e) => { maru(e); });
08   //赤のスライダーが動いたとき
09  _red.addEventListener(`change`, () => { setColor(); });
10  //緑のスライダーが動いたとき
11  _green.addEventListener(`change`, () => { setColor(); });
12  //青のスライダーが動いたとき
13  _blue.addEventListener(`change`, () => { setColor(); });
14  //ペンの太さのスライダーが動いたとき
15  _pen.addEventListener('change', () => { setColor(); });
16  _canvas.width = 800;
17  _canvas.height = 600;
18  _width.value = _canvas.width;
19  _height.value = _canvas.height;
20});
21//ペンの色のセット
22function setColor() {
23    let color = 'rgb('+_red.value+','+_green.value+','+_blue.value+')';
24    _color.style.backgroundColor = color;
25    let diameter = parseInt(_pen.value) * 2;
26    _color.style.width = diameter+'px';
27    _color.style.height = diameter +'px';
28}
29(後略)

【サンプルコードの解説】
ペンのプレビューの「#color」セレクタを「_color」変数に代入します。
「#red」セレクタ、「#green」セレクタ、「#blue」セレクタ、「#pen」セレクタのスライダーが動いたら(change)、「setColor」関数を呼び出すイベントリスナーを最初に1度だけセットします。
「setColor」関数でCSSのスタイルシートでペンのプレビュー(_pen変数)の背景色(backgroundColor)を文字列で'rgb(255,160,80)'などと変更し、ペンの半径(_pen変数)の値(valueプロパティ)の2倍を'50px'などと文字列でCSSのスタイルシート(_color.style.widthと_color.style.height)に代入します。

おわりに

今回は、前回で作成した画像ビューアサンプルアプリに、丸アートのお絵描きアプリを付け足しました。例えばクリックだけで円を描くのではなく、マウスドラッグで線を引けるようにするなど、普通のお絵描きアプリに改造することもできます。色々とご自身で改造してみることで、技術習得の近道になるのではないでしょうか。

著者
大西 武 (オオニシ タケシ)
1975年香川県生まれ。大阪大学経済学部経営学科中退。プログラミング入門書など30冊以上を商業出版する作家。Microsoftで大賞やNTTドコモでグランプリなど20回以上全国区のコンテストに入賞するアーティスト。オリジナルの間違い探し「3Dクイズ」が全国放送のTVで約10回出題。
https://profile.vixar.jp

連載バックナンバー

開発言語技術解説
第15回

「TAURI」でデータベースを使ってみよう

2024/5/10
第15回の今回は「TAURI」でオープンソースのデータベース「SQLite3」を使用して、テーブル表に表示する解説をしていきます。
開発言語技術解説
第14回

「TAURI」で気象庁の「CSVデータ」を解析する

2024/5/1
第14回の今回は気象庁のWebサイトから指定した地域の1年間の気象データをダウンロードして「TAURI」で解析していきます。
開発言語技術解説
第13回

「TAURI」で「簡易RSSリーダー」を開発してみよう

2024/4/16
第13回の今回は「TAURI」で「RSSフィード」を読み込んでWebページに一覧表示し、リンクのページを開くための新規ウィンドウを作成するところまでを解説します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています