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を使ったアプリケーションづくりにもチャレンジしてみてください^^

【やる気満々な方向け】学習サポートとオンライン講義やってます!

Webエンジニアになりたい人向けに、MENTAにて『【やる気満々な方向け】未経験からのWebエンジニア育成コース』というプランを用意しております。

誰向けのプラン?

こちらのプランは次のような方に向けて作られています。

プラン対象者
  1. Progateやドットインストールで一通り学習したけど、次に何をすればよいかわからない
  2. Webエンジニアになるまでの学習プランがあると助かる
  3. 学習していてわからないところが出てきたときに質問したい
  4. コードレビューして欲しい
  5. オンライン講義に参加したい
  6. 学習した内容をアウトプットした際に、フィードバックがもらえたら嬉しい
  7. おすすめの学習教材を知りたい
  8. Webフロントエンド・バックエンドを学習したい
  9. フルスタックエンジニアになりたい
  10. 文系出身で今までプログラミングをしてこなかったけど、将来Webエンジニアになりたい
  11. 将来フリーランスになることを目指している

プラン内容は?

【やる気満々な方向け】未経験からのWebエンジニア育成コース』では次のことを行っております。

プラン内容
  1. Udemyの教材や、Web白熱教室の学習コンテンツを使った学習プランの提示 (学習プランに沿って学習を進めていただきます。)
  2. 教材・学習コンテンツの課題をこなしたあとは、僕にコードレビュー依頼を投げていただくことで、コードを添削する(課題クリア後は、引き続き学習プランの内容を続けていただきます。)
  3. プラン契約者が参加するSlackグループにて質問し放題
  4. Slack内にある「アウトプットの場」チャンネルにて、学習したことをアウトプットをしていただくことで、それに対しフィードバックをする (学習内容のアウトプットは復習にもなるため、アウトプットすることを強く推奨します。)
  5. 画面共有を使ったオンライン講義 (オンライン講義は録画して、いつでもふり返れるようにしています。(オンライン講義動画(ウェビナー動画)))
  6. 学習プラン以外の内容に対しての質疑応答 (内容によっては回答できないものもありますが、まずは気軽に質問していただけたらと思います。)

その他注意点

1人で対応していることもあるため、対応が難しくなった場合は急遽募集を終了する可能性もあることをご了承ください。

もしくは、僕以外にメンターを増やすことで募集を継続するということも考えているため、その場合は現在のプラン料金の値段を上げることなると思います。

仮にプラン料金を上げる場合は、『現在のプラン料金 × メンター人数』の料金になるかと思います。

現在の値段が5000円なので、1日1本ペットボトルジュースを買うのを我慢することで(約160円)、効率よく学習できるので費用対効果はかなり高いかなと自負しています。

また、今後メンターが増えて、プラン料金が10000円15000円と上がったとしても、月々の料金は申し込んだときの料金となるので、5000円のときに契約した場合はずっと5000円で受講することができます。

詳しく話を聞いてみたい方は

以下のいずれかでメッセージをいただけたらと思います^^

 

プラン内容については以下のリンク先からも確認することが出来ます^^

【やる気満々な方向け】未経験からのWebエンジニア育成コース

COMMENT

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