HTML

HTMLのcanvasとJavaScriptでお絵かきアプリ【線の太さ機能編】

oekaki-part3

今回はHTMLのcanvas・JavaScriptを使って作る「お絵かきアプリ」シリーズの最後の記事です。

前回・前々回の記事で使用したコードをベースにしているので、まだ読んでいない方は以下の記事を読むことでより理解が深まるかと思います。

今回作成するものの完成形は次の画像になります。

線の太さの変更と太さを表現する「○」を組み合わせた挙動線の太さの変更と太さを表現する「○」を組み合わせた挙動

 

上のアニメーション画像で新しく追加された機能は次の2つです。

新機能
  1. 「文字の太さ」スライダーで太さを変更できる
  2. canvas上にマウスカーソルが乗ると現在の線の太さが「○」の形で表示される

今回の記事で作成したコードと、公開されているアプリは以下のリンクからアクセスできます。

今回の記事を読むことで身につくスキルは次の通りです。

身につくスキル
  1. HTMLの「 <input type=”range”> 」を使ったイベント処理
  2. style(CSS)の「 position: absolute 」を使ったHTML要素を重ねるやり方
  3. canvas上でマウスカーソルについてくる円の描画方法
  4. 2つのcanvasを重ねて使うテクニック

③と④に関してはゲームを作るときにも利用できるスキルになるので、「将来ゲーム作ってみたいな」と思っている方にとっても参考になる記事になります。

それでは、今回の記事は次の内容で話を進めていきます。

今回の記事内容
  • 線の太さを変更できる機能を作る
  • マウスカーソルについてくる円の描画用のcanvasを用意する

それでは1つ目の内容から見ていきましょう。

線の太さを変更できる機能を作る

線の太さを変更するには、線の太さを表す「数値」を変更すれば良いので、今回使った「<input type=”range”>」でなくても、次のような選択肢もあります。

他の選択肢

どれを使うかは好みの問題になりますが、今回「<input type=”range”>」を選んだ理由は次の理由からです。

rangeを選択した理由
  • マウス操作だけで完結させたかった
  • 「0.1」単位で数の調整を行えるようにしたかったが、「<input type=”radio”>」と「<select>, <option>」を利用する場合はあらかじめ要素をたくさん準備する必要がある
  • 「<input type=”range”>」を使うと、スライドバーで数を選択できて直感的だった

今回言っている「スライドバー」とは以下の画像でいうと、canvasの下にある値変更に使っているパーツのことです。

線の太さの変更と太さを表現する「○」を組み合わせた挙動

サンプルコードを使って解説

HTMLファイル、JavaScriptファイルの両方で、今回追加した機能の部分にのみコメントが書かれています。

今回の記事以外部分の機能を詳細を知りたい方は、前回・前々回の記事で使用するサンプルコードの中に細かくコメントで説明しているのでそちらを参考にしていただけたらと思います。

HTML

それではHTMLの内容から見ていきましょう。

上記のコードのうち、線の太さ変更に関わる部分は、32~44行目の部分になり、2つのパーツに分けられます。

  1. 34~40行目: 「<input type=”range”>」 を使ってスライドバーを用意
  2. 43行目: 「<span id=”line-width”>」で現在のスライドバーで選択している値を目視できるようにする

①の「<input type=”range”>」では、スライドバーの基本設定を行っています。それぞれの意味は次のとおりです。

設定内容
  • id: JavaScriptでこのinput要素を操作するための値
  • type: range(スライドバー)を使うことを宣言
  • value: デフォルト値
  • min: スライドバーで選択できる最小値
  • max: スライドバーで選択できる最大値
  • step: 値の細かさの設定(0.01まで可能(ドキュメントより))

スライドバーを動かすことで値の変更を行うことが出来るのですが、「<input type=”range”>」だけだとページ上に現在の値を表示することが出来ません。

そこで、現在の値を表示するための要素として②の「<span id=”line-width”>」を追加して、JavaScript経由で値を更新出来るようにしています。

JavaScript

それでは、どのように値を更新しているのかJavaScriptのコードを確認しましょう。

上記のコードの内、線の太さの更新に該当する箇所は以下の部分です。

該当箇所
  1. 21行目: 線の太さの値を保持する「currentLineWidth」変数
  2. 135~153行目: 線の太さの値の更新処理をまとめている「initConfigOfLineWidth」関数
    • <input type=”range”>、<span id=”line-width”>のDOM操作
    • 太さ情報を保持している「currentLineWidth」変数の値の更新
  3. 159行目: 「initConfigOfLineWidth」関数を実行して、線の太さの値の更新処理を有効化

①で用意した「currentLineWidth」変数を使って線の太さを表現して、値が小さいほど線が細くなり、値が大きいほど線が太くなります。

この「currentLineWidth」変数は線を引く時に実行されるdraw関数内の29行目で線の太さを定義する時に使われます。

②では、HTML内で値変更用に用意した「<input type=”range”>」、現在選択している数値を表示するための「<span id=”line-width”>」のDOM操作と、値に変更があったときのイベント処理の定義をしています。

144行目のaddEventListenerで「’input’」イベントをセットすることで、スライドを動かしている最中も値を取得することが出来ます。

次のアニメーション画像は、スライドバーを動かしている時に上記145行目のwidthの値を「console.log」を使って確認しているところです。

range-slidebarスライドバーを動かしているときに画面の右側で値が更新されているのがわかる

 

③で行っていることは②で用意した「initConfigOfLineWidth」を呼び出して有効化しているだけです。

以上で線の太さを変更する実装は完了です。

次にマウスカーソルについてくる円の描画方法について説明します。

マウスカーソルについてくる円の描画用のcanvasを用意する

「マウスカーソルについてくる円」とはちょっと言葉だけだとわかりにくいので、次の画像を見てください。

show-line-width-indicator画面左のcanvas上にマウスカーソルが乗ったら「○」がマウスについてくる

 

上の画像を見ると、左側にあるcanvasにマウスカーソルが乗った時に「○(円)」がついてきているのがわかるかと思います。

この「○(円)」は現在の線の太さを表現しているのですが、この機能を実現するためにもう一つ別のcanvasを用意しています。

以下のHTMLは先程のHTMLファイルの中からcanvasの部分だけ抜粋したものです。

上記2つのcanvasの役割はそれぞれの次のとおりです。

それぞれの役割
  1. 4~8行目のcanvas: 線を書くためのもの
  2. 9~13行目のcanvas: 「○(円)」を描画するためのもの

2つのcanvasを使う理由

なぜ役割がことなるだけで2つのcanvasが必要になるのでしょうか?

①の機能に関しては、「全消し」ボタンを押さない限りは、過去に引いた線を残し続ける必要があります

線を引くたびに過去に書いた線が消されたらただの点の移動になってしまうからです。

しかし、②の機能に関しては、過去に描画した「○(円)」をマウスカーソルが移動するたびに削除する必要があります。

過去に描画した「○(円)」を削除しなかった場合は次の画像のように「○(円)」が足跡のように残ってしまいます

過去の描画を削除しなかった時の「○」の表示の挙動過去の描画を削除しなかった時の「○」の表示の挙動

 

上記までに話した内容をまとめると次の通りです。

  1. 線を引くときは過去の描画を削除してはいけない。削除するとただの点の移動になる。
  2. 「○(円)」を描画するとき、過去の「○(円)」を消さないと、足跡のように残り続けてします。

このように、①線を書く機能と、②「○(円)」を描画する機能で正反対のことをしなければいけないのです。

その理由より「○(円)」描画用にcanvasを追加しました。

2つのcanvasを重ねる

2つのcanvasを重ねることで、マウスドラッグを開始した時に、「○(円)」の位置から線を書くことが出来ます。

しかし、どのようにして2つのHTML要素を重ねているのでしょうか?

CSSの「position: absolute;」を使うことで実現できます。

今回はサンプルコードなので8行目のcanvas要素のstyle値に直接指定しています。

JavaScriptで○(円)を描画する

先程共有したJavaScriptのコードの内、「○(円)」の描画に関係する部分のコードだけ抜粋します。

「○(円)」を描画するために必要なことは大きく分けると以下の3つになります。

円描画で必要な3点
  1. 2~3行目: 円描画用のcanvas情報取得
  2. 7~28行目: 円描画のための「showLineWidthIndicator」関数の用意
  3. 30~65行目: canvasを2つ使うようになったため、マウスイベントをcanvas2つの親要素である「<span id=”layerd-canvas-area”>」に定義する

①は、線を書くためのcanvasと同じように、円描画用のcanvas情報から描画するために必要なcontext情報を取得します。

context」とはcanvasに図や線などを描画するのに必要なものという認識で大丈夫です。

②は、線引くときと同じようにcanvasに描画を行う処理をまとめたものですが、2点線を引くときと異なる部分があります。

線描画と異なる2点
  1. 18行目: 描画が実行されるたびcontextの「clearRect」メソッドを使って、canvas上に描画されているものを全て削除する
  2. 25行目: contextの「arc」メソッドを使って、マウスカーソルの位置(x, y座標)を中心に円を描画する

上記2つの機能により、「showLineWidthIndicator」関数が実行されるたびに、canvasがリセットされた後に、マウスカーソル位置を中心に円の描画が実現されます。

そして、③で行っているマウスイベントによって実行される56行目のコールバック関数内で、②で定義した「showLineWidthIndicator」関数は実行されます。

前回までは、線を書くためのcanvasに対してマウスイベントを定義していましたが、今回の追加機能により2つのcanvasを利用することになったため、

2つのcanvasの共通の親要素である「<span id=”layerd-canvas-area”>」に対してマウスイベントを定義するように変更しました。

仮に前回から変更せずに、線をかくためのcanvasに対してイベントを定義していたとしても同じように動くかと思いますが、以下の理由より親要素である「<span id=”layerd-canvas-area”>」に対して変更しました。

イベント対象の変更理由
  1. 1つのcanvasに対してのイベント定義の場合、指定したcanvasのみを対象とする意味合いになる
  2. 親要素に対してイベントを定義すると、親が内包している子要素(今回の場合は2つのcanvas)もイベント対象に含まれる意味合いになる

上記の理由のように、ただ動くことだけを考えるのではなく、イベントで実行する処理内容の意味まで考えて適切だと思われる要素に対してイベントを定義しています。

まとめ

あらためて、今まで作成したお絵かきアプリの内容を振り返ってみましょう。

今まで実装した機能
  1. 絵を描くために必要な最低限の機能を実装(1つ目の記事)
  2. 色の変更と、部分消しをするための消しゴム機能を追加(2つ目の記事)
  3. 線の太さの変更と、現在選択している線の太さを直感にわかるようにするために「○(円)」をマウスカーソルを中心に描画する機能(今回の記事)

以下にこれまで説明してきたコードのGithubレポジトリのリンクと、実際に機能を確認できるリンクを貼っておきます。

これまでcanvasで出来ることの説明として「お絵かき」アプリを題材にして説明しましたが、canvasで出来ることたくさんありその一例は次のとおりです。

ぜひ、HTMLのcanvasを使ったアプリケーションづくりにもチャレンジしてみてください^^

【3つの特典付き】Webエンジニアに必要な基礎スキルを体系的に効率良く身につけたいですか?

LINE登録で無料特典を受け取る

次のような方向けに、学習をサポートするコンテンツを無料プレゼントしています。

  • Webエンジニアになりたいけど何から学習を始めれば良いかわからない
  • 今まで独学で頑張ってきたけど、独学に限界を感じてきた
  • プログラミングは出来るけど、大きめのコードが書けない

「プレゼント詳細が知りたい」「プレゼントを受け取りたい」といった方は、以下のボタンをクリックして詳細ページをご確認ください。

無料特典を受け取る

 

COMMENT

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です