アンドゥの実装
ここまでで基本的な操作はほとんど実装しているのですが、さらに完成度を上げるために「アンドゥ」できるようにしたり、canvasの幅高さのサイズを変更できるようにしたり、ペンのプレビューを変更できるようにしたりします。
それでは「src」→「main.js」ファイルをコーディングしてアンドゥを実装し、「$ cargo-tauri dev」コマンドでデバッグビルドしてください。画面上部のアンドゥボタンでアンドゥできるようになりましたね。
また「リドゥ」ボタンも付けてリドゥ機能を実装しても良いでしょう。その場合はタイミングが大事で、アンドゥするときにアンドゥデータをcanvasにセットする直前のcanvasの幅と高さと画像データをリドゥデータの後ろに追加(push)します。
・「src」→「main.js」でアンドゥするサンプルコード
01 | const { invoke } = window.__TAURI__.tauri; |
02 | const { open,save } = window.__TAURI__.dialog; |
04 | let _canvas = document.querySelector('canvas'); |
05 | let _context = _canvas.getContext('2d'); |
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+')'; |
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); |
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); |
34 | _width.value = _canvas.width; |
35 | _height.value = _canvas.height; |
【サンプルコードの解説】
アンドゥデータは第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の幅高さを変更するサンプルコード
03 | let _width = document.querySelector('#width'); |
05 | let _height = document.querySelector('#height'); |
07 | window.addEventListener("DOMContentLoaded", () => { |
08 | //キャンバスでマウスクリックが離されたとき |
09 | _canvas.addEventListener("mouseup", (e) => { maru(e); }); |
12 | _width.value = _canvas.width; |
13 | _height.value = _canvas.height; |
16 | function 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); |
24 | function 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); |
【サンプルコードの解説】
canvasの幅の「#width」セレクタを「_width」変数に、canvasの高さの「#height」セレクタを「_height」変数に代入します。
最初にcanvasの幅を800に、高さを600にセットします。
canvasの幅のテキストボックスの値が変更されると「changeWidth」関数が呼ばれ、canvasの高さのテキストボックスの値が変更されると「changeHeight」関数が呼ばれ、それまでの画像データを「img」変数に取得してから、それぞれ幅と高さを変更してcanvasに画像データ「img」変数を描画します。
さらに改造するならテキストボックスの値が数値かどうか、1~2000ぐらいかどうか調べて正しければ、canvasサイズを変更すると良いでしょう。
・「src-tauri」→「tauri.conf.json」でウィンドウ幅高さを変更するサンプルコード
【サンプルコードの解説】
"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」でペンをプレビューするサンプルコード
03 | let _color = document.querySelector('#color'); |
05 | window.addEventListener("DOMContentLoaded", () => { |
06 | //キャンバスでマウスクリックが離されたとき |
07 | _canvas.addEventListener("mouseup", (e) => { maru(e); }); |
09 | _red.addEventListener(`change`, () => { setColor(); }); |
11 | _green.addEventListener(`change`, () => { setColor(); }); |
13 | _blue.addEventListener(`change`, () => { setColor(); }); |
15 | _pen.addEventListener('change', () => { setColor(); }); |
18 | _width.value = _canvas.width; |
19 | _height.value = _canvas.height; |
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'; |
【サンプルコードの解説】
ペンのプレビューの「#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)に代入します。
おわりに
今回は、前回で作成した画像ビューアサンプルアプリに、丸アートのお絵描きアプリを付け足しました。例えばクリックだけで円を描くのではなく、マウスドラッグで線を引けるようにするなど、普通のお絵描きアプリに改造することもできます。色々とご自身で改造してみることで、技術習得の近道になるのではないでしょうか。