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

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

はじめに

今回は、前回で作ったプログラムにさらに肉付けしていき、丸シールでお絵描きするアート作品風に円をたくさん描画するだけでお絵描きできるアプリを開発します。そのためには、まず前回のプログラムをコーディングする必要があります。

図1のように色の付いた正円を好きな太さで何個も判子のように押していけば、例えばヒマワリの丸シールアートが描けます。よく見ていただければ分かるように、塗り絵のように線画の下絵を読み込んでから描いています。あまり上手な絵は描けませんが、これなら誰でも簡単に描けてヘタウマな感じがしませんか。

今回は「HTML5+JavaScript+CSS」だけコーディングし、「canvas」タグに円を描けるようにしたり、円の赤緑青の色をスライダーで変更させられるようにしたり、円のペン(筆)の太さを変更させられるようにしたり、canvasタグの幅高さの大きさを変更できるようにしたりします。「Rust」については前回のまま同じコードで、画像を読み込んだり保存したりするだけです。

図1:丸アートお絵描きアプリの完成図

タグを配置して円を描けるようにする

「index.html」「main.js」「styles.css」をコーディングすると、図2のように赤緑青の色を変更したりペンの太さを変更したりして円を描けるようになります。後はペンの色と太さに合わせてプレビューの筆を反映したり、canvasの幅と高さも反映したりすれば完成です。

図2:円を描けるようにしたところ

HTMLファイルにタグを配置する

ここで「src」→「index.html」ファイルにタグを配置するだけで、ほとんどお絵描きアプリの完成図に近いものができます。ただしHTMLファイルでは見た目だけで、実際に円を描くにはJavaScriptでプログラミングしなければなりません。

ペンの円の色や太さを決める「input」タグの「type」属性が"range"のスライダーです。最小値を「min」属性で、最大値を「max」属性でセットします。canvas画面の幅高さをセットするinputタグのtype属性が"text"のテキストボックスで、テキストボックスの値が変更されるたびに「onBlur」属性で指定した「changeWidth」関数や「changeHeight」関数が呼ばれます。

・「src」→「index.html」のサンプルコード
(前略)
<body>
    <header>
      <h1>MaruArt</h1>
      <button onClick="newImage()">新規</button>
      <button onClick="openImage()">開く</button>
      <button onClick="saveImage()">保存</button>
      <button onClick="undoImage()">アンドゥ</button>
    </header>
    <div id="canvas">
      <canvas></canvas>
    </div>
    <div id="ui">
      <p>
        赤 <input id="red" type="range" min="0" max="255" value="0" step="1"><br />
        緑 <input id="green" type="range" min="0" max="255" value="0" step="1"><br />
        青 <input id="blue" type="range" min="0" max="255" value="0" step="1"><br />
      </p>
      <p>
        <div id="color"></div>
      </p>
      <p>
        幅  <input onBlur="changeWidth(this)" type="text" id="width" name="width" required minlength="1" maxlength="4" size="11"><br />
        高さ <input onBlur="changeHeight(this)" type="text" id="height" name="height" required minlength="1" maxlength="4" size="11"><br />
      </p>
      <p>
        筆 <input id="pen" type="range" min="1" max="75" value="25" step="1"><br />
      </p>
    </div>
    <script src="main.js"></script>
  </body>
</html>

【サンプルコードの解説】
「アンドゥ」ボタンを押すと「undoImage」関数が呼ばれるようにbuttonタグを配置します。
「#ui」セレクタに「赤」と「緑」と「青」を0~255以下まで選べるスライダー(inputタグのrangeタイプ)を配置します。
「#color」セレクタに実際のペンの色と大きさの実物大をプレビューします。まだここでは変更が反映されません。
canvasの「幅」と「高さ」のサイズを変更できるテキストボックス(inputタグのtextタイプ)を配置します。まだここでは変更が反映されません。
「筆」(ペン)の太さを1~75以下まで選べるスライダー(inputタグのrangeタイプ)を配置します。

canvasタグにJavaScriptで円を描けるようにする

「src」→「main.js」ファイルでindex.htmlの各セレクタを制御します。canvasタグ上をクリックしたら、黒っぽい輪郭で囲まれた円を描きます。輪郭の円周の色は、円の色の赤緑青の値をそれぞれ2/3倍した値です。

「querySelector」メソッドでペンの色を「#red」セレクタなどから取得しますが、その値はそのインスタンスの「value」プロパティから取得します。色だけでなくペンの太さも「#pen」セレクタのvalueプロパティから取得します。

コンテキストの「strokeStyle」プロパティと「fillStyle」プロパティに渡す色は、例えば'rgb(64,128,255)'のような文字列で表したRGB値です。

・「src」→「main.js」のサンプルコード
const { invoke } = window.__TAURI__.tauri;
const { open,save } = window.__TAURI__.dialog;

let _canvas = document.querySelector('canvas');
let _context = _canvas.getContext('2d');
//赤色
let _red = document.querySelector('#red');
//緑色
let _green = document.querySelector('#green');
//青色
let _blue = document.querySelector('#blue');
//ペン
let _pen = document.querySelector('#pen');
//HTMLの読み込みが終了しとき
window.addEventListener("DOMContentLoaded", () => {
  //キャンバスでマウスクリックが離されたとき
  _canvas.addEventListener("mouseup", (e) => { maru(e); });
});
//キャンバスにおけるマウスの位置を取得
function getPos(event) {
	let clientRect = _canvas.getBoundingClientRect();
	let x = event.clientX - clientRect.left;
	let y = event.clientY - clientRect.top;
  return [x,y];
}
//円を描く
function maru(event) {
  let pos = getPos(event);
  let color = 'rgb('+2*_red.value/3+','+2*_green.value/3+','+2*_blue.value/3+')';
  _context.beginPath();
  _context.lineWidth = 1;
  _context.strokeStyle = color;
  color = 'rgb('+_red.value+','+_green.value+','+_blue.value+')';
  _context.fillStyle = color;
  _context.arc(pos[0],pos[1],_pen.value,0,Math.PI*2,true);
  _context.fill();
  _context.stroke();
}
(後略)

【サンプルコードの解説】
index.htmlの#redセレクタを「_red」変数に、#greenセレクタを「_green」変数に、#blueセレクタを「_blue」変数に、#penセレクタを「_pen」変数に代入します。
イベントリスナーでDOMが全て読み込まれたら(DOMContentLoaded)、canvasタグ上でマウスクリックが離されたとき(mouseup)、「maru」関数をイベントリスナーで呼ぶようにセットします。
「getPos」関数はcanvas上のマウス座標、つまりcanvasタグの一番左上が(X,Y)=(0,0)となるようにマウス座標を取得します。
canvas上でマウスクリックが離されたらmaru関数が呼ばれ、取得したマウス座標の位置に_penの太さで_redと_greenと_blueの色の円とその色を黒っぽくした円周の輪郭を描きます。

スタイルシートの装飾

「スタイルシートのCSSは後からデザインを微調整すれば良い」と言いましたが、今回は次の「src」→「styles.css」ファイルのように円のプレビューなどデフォルトの値を設定しておいた方が良いです。

ここまでコーディングしたら「$ cargo-tauri dev」でデバッグビルドして実行してみてください。canvasをクリックすれば円が描けますね。ペンの色や太さも変更して円を描いてみてください。余談ですが「開く」ボタンで画像ファイルを開けば、その画像サイズにcanvasタグの幅高さのサイズが変更できます。

・「src」→「styles.css」のサンプルコード
(前略)
#ui {
  float: left;
  color: #000;
  margin: 10px;
}
#color {
  background-color: #000;
  border: 1px solid #000;
  width: 50px;
  height: 50px;
  border-radius: 50%;
}

【サンプルコードの解説】
#uiセレクタでペンの色と太さとcanvasの幅高さのUIはできるだけcanvasタグの右側に配置されるように「float: left;」で左側に浮動します。「できるだけ」と書いたのは、図3のように画面幅が狭いときに左側に浮動する配置が下に折り返されるからです。
#colorセレクタで、プレビューのペンをまずは真っ黒の半径25px(50pxの50%)で円を描画します。

図3:UIが折り返される(「保存」ボタンなども折り返される)

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

連載バックナンバー

開発言語技術解説
第13回

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

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

「TAURI」でExcelのデータを読み書きしてWebページに表示してみよう

2024/4/2
第12回の今回は「TAURI」で「Rust」の「umya-spreadsheet」クレートを使って「Excel」の「xlsx」ファイルを読み書きし、Webページに表示するところまでを解説します。
開発言語技術解説
第11回

「TAURI」と「Rust」の「テスト」機能を試してみよう

2024/3/12
第11回の今回は「Rust」で値が正しいか「テスト」する「assert_eq!」マクロなどを解説します。また「TAURI」でもプログラムが正しく動作するかの「テスト」についても解説します。

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

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

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

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