JavaScript - お絵描きアプリの作り方【基本編】
HTML のキャンバス <canvas> と JavaScript でシンプルなお絵描きアプリを作ります。線の描画機能と消去ボタンのみの最低限のコードで、自由に描画するための基本的な方法を学びましょう。
JavaScript でお絵描きしよう
今回は、JavaScript を使ってキャンバスに自由に描画する方法を紹介します。マウスやタッチパッドなどのポインティングデバイスでお絵描きできるシンプルなアプリを作ってみましょう。
今回の【基本編】では、線を描くことと描いたものを消すことができるだけの、単純なお絵描きアプリを作ります。 【機能追加編】では、線の太さや色を選べるように機能を追加していきます。
この記事を読むと分かること
mousedown
イベントmousemove
イベントmouseup
イベントfunction(e)
offsetX
プロパティoffsetY
プロパティ
サンプルプロジェクト
最初に、今回作成するお絵描きアプリの動きを確認しておきましょう。
- キャンバス内でマウスボタン(タッチパッドなど)を押して、描画を開始する
- マウスを押下したまま動かすと、描画される
- マウスボタンを離すと、描画されない
- 消去ボタンを押すと、キャンバスがクリアされる
HTML
まずは HTML です。
- 描画エリア
drawing-area <canvas>
を作成し、width
属性とheight
属性でキャンバスのサイズを指定します。 - 描画したものを全消去できるように、消去ボタン
clear-button <button>
を作ります。
<!--キャンバス 描画エリア-->
<canvas id="drawing-area" width="500" height="400"></canvas>
<!--消去ボタン-->
<button id="clear-button">Clear</button>
JavaScript
続いて JavaScript です。JavaScript では、キャンバスに線を描画できるようにコーディングしていきます。もし、キャンバスに描画する基本的な方法が分からない場合は、以下の記事から先にご覧ください。
要素を取得する
では、キャンバスに描画する準備をしていきましょう。
- 描画エリアを
querySelector()
メソッドで取得し、変数canvas
に保存します。 - キャンバスの
2d
コンテキストを取得して変数ctx
に格納し、描画するためのメソッドやプロパティを利用できるようにします。 - 消去ボタンを
querySelector()
メソッドで取得し、変数clearBtn
に格納します。
// キャンバス(描画エリアの取得)
const canvas = document.querySelector('#drawing-area');
// 2dコンテキスト(描画ツールの取得)
const ctx = canvas.getContext('2d');
// 消去ボタン
const clearBtn = document.querySelector('#clear-button');
変数を宣言する
次に行うのは、変数の宣言です。
- キャンバスのどこに描くかを決めるのは、x 座標と y 座標です。描画の開始位置を指定するための変数
x
とy
を用意します。 mousePressed
は、マウスボタンが押されているtrue
か、押されていないfalse
かを示す変数です。この変数の値によって、描画するかしないかを切り替えます。最初は、マウスボタンが押されていないfalse
で初期化します。
// 描画開始点 横の位置
let x;
// 描画開始点 縦の位置
let y;
// マウスボタンが押されているかどうか(trueまたはfalse)
let mousePressed = false;
マウスボタンを押して描画を開始する
要素の取得と変数の用意ができたら、マウスボタンを押して描画を開始するためのコードを書いていきます。
マウスボタン(タッチパッドなど)を押したときに発生するのは、mousedown
イベントです。addEventListener()
メソッドでキャンバスに mousedown
イベントを追加し、startDrawing
を呼び出しましょう。startDrawing
は描画を開始する関数で、このあと定義します。
- キャンバス内でマウスボタンを押したとき、描画を開始します。
// キャンバスにmousedownイベントを追加
canvas.addEventListener('mousedown', startDrawing);
マウスなどを押したときに発生するイベントといえば、click
イベントを思いつくかもしれませんね。click
イベントはマウスが押されて離された後に発生するのに対し、mousedown
は押下した時点でイベントが発生するという違いがあるので、覚えておきましょう。
描画を開始する関数
ここからは、キャンバス内でマウスボタンを押したときに実行する関数 startDrawing
を定義していきます。
startDrawing
は、マウスボタンが押されたところから描画を開始できるように、押された位置の座標を取得する関数です。関数には、startDrawing(e)
のように引数 e
を指定します。こうすることで、キャンバス内で mousedown
イベントが発生したときに、その座標情報を受け取ることができるようになりますよ。
// 描画を開始する
function startDrawing(e) {
// マウスボタンが押されていることを示す
// 押下された位置を取得する
}
イベントオブジェクトを表す e
について、詳しくは以下の記事で解説しているのでご覧ください。
マウスボタンが押されていることを示す
描画を開始するためには、まず、マウスボタンが押されていることを示し、描画できる状態に切り替えます。
- 変数
mousePressed
の値をtrue
にします。
function startDrawing(e) {
// マウスボタンが押されていることを示す
mousePressed = true;
// 押下された位置を取得する
// …
}
押下された位置を取得する
続いて、マウスボタンが押された x 座標と y 座標を取得し、それぞれ変数に格納しましょう。座標を取得するために使うのが、引数 e
と、offsetX
/offsetY
プロパティです。引数 e
には、mousedown
イベントが発生したときの位置情報が保存されています。
e
が持つ情報の中から、offsetX
プロパティで x 座標(キャンバス内での横の位置) を取得します。e
が持つ情報の中から、offsetY
プロパティで y 座標(キャンバス内での縦の位置)を取得します。
function startDrawing(e) {
mousePressed = true;
// 押下された位置を取得する
x = e.offsetX;
y = e.offsetY;
}
以上で、マウスボタンが押された位置を変数 x
と y
に格納し描画を開始する関数 startDrawing
を定義できました。
マウスを動かしてキャンバスに描画する
次は、マウスを動かして描画するためのコードを書いていきます。
マウスなどを動かしたときに発生するのは、mousemove
イベントです。キャンバスに mousemove
イベントを追加して、draw
を呼び出しましょう。draw
は実際に描画するための関数で、このあと定義します。
- キャンバス内でマウスを動かしたとき、描画します。
// キャンバスにmousemoveイベントを追加
canvas.addEventListener('mousemove', draw);
線を描画する関数
ここからは、キャンバスに線を描画するための関数 draw
を定義していきます。この関数でもマウスの位置情報を利用するので、 draw(e)
のように引数に e
を指定してくださいね。
// 線を描画する
function draw(e) {
// マウスボタンを押していないときは描画しない
//*** 描画する処理 ***
// 移動先の座標を取得する
// 線を描画する
// 開始点の座標を更新する
}
マウスボタンを押していないときは描画しない
キャンバスに描画するのは、マウスボタンを押している間だけです。まず最初に、マウスボタンが押下されているかどうかを確認しましょう。そのために使うのは、if
文です。もしマウスボタンを押していなければ、関数 draw
は実行しないようにします。
- 論理否定演算子
!
を使って、「マウスボタンを押していない」という条件を指定します。 return
で、関数の実行を停止します。
function draw(e) {
// マウスボタンを押していないときは描画しない
if (!mousePressed) return;
//*** 描画する処理 ***
// 移動先の座標を取得する
// 線を描画する
// 開始点の座標を更新する
// …
}
ここでは、「もしマウスボタンを押していないなら関数を実行しない」という false
の場合の文を書きました。これは、下のように、「もしマウスボタンを押しているなら関数を実行する」という true
の場合の if
文にすることもできますよ。
// trueの場合に関数を実行するコード例
function draw(e) {
// もしマウスボタンを押しているなら
if (mousePressed) {
//*** 描画する処理 ***
// …
}
}
if
文での true
や false
については、以下の記事で詳しく解説しているのでご覧ください。
移動先の座標を取得する
マウスボタンを押しているかいないかを if
文で条件分岐したら、線を描画する処理を書いていきましょう。
線を描くためには、どこからどこまで線を引くのか、その座標を指定する必要があります。開始点の座標は、先ほど定義した関数 startDrawing
で変数 x
と y
に格納されるようにしましたね。あと必要になるのは、移動先の座標です。
この関数 draw
は、mousemove
イベントが発生したときに呼び出されるので、引数 e
にはマウスを動かした先の座標情報が含まれます。マウスの移動先座標を、それぞれ変数 x2
、y2
に格納しましょう。
e
が持つ情報の中から、offsetX
プロパティで x 座標を取得します。e
が持つ情報の中から、offsetY
プロパティで y 座標を取得します。
function draw(e) {
if (!mousePressed) return;
// 移動先の座標を取得する
x2 = e.offsetX;
y2 = e.offsetY;
// 線を描画する
// 開始点の座標を更新する
// …
}
線を描画する
開始点 x
と y
、移動先 x2
と y2
の座標を使って、線を描きましょう。
beginPath()
メソッドで、描画を開始します。moveTo()
メソッドで、どこから描き始めるのかを指定します。lineTo()
メソッドで、どこへ線を引くのかを指定します。stroke()
メソッドで、線を描画します。
function draw(e) {
if (!mousePressed) return;
x2 = e.offsetX;
y2 = e.offsetY;
// 線を描画する
ctx.beginPath(); //描画開始
ctx.moveTo(x, y); //開始点
ctx.lineTo(x2, y2); //移動先
ctx.stroke(); //線を引く
// 開始点の座標を更新する
// …
}
beginPath()
メソッドなどを使用して線を描く方法はこちらで解説しています。
開始点の座標を更新する
マウスを動かしながら線を描き続けるためには、開始点を移動先の座標で上書きしていく必要があります。mousemove
イベントが発生するたびに、変数 x2
と y2
にはマウスの移動先の座標が入り、開始点 x
と y
の値も更新することで、線が引かれます。
- 変数
x
とy
を、変数x2
とy2
の値で更新する
function draw(e) {
if (!mousePressed) return;
x2 = e.offsetX;
y2 = e.offsetY;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x2, y2);
ctx.stroke();
// 開始点の座標を更新する
x = x2;
y = y2;
}
以上で、キャンバスに線を描画するための関数 draw
を定義できました。
マウスボタンを離して描画を終了する
さて今度は、マウスボタンを離したときに描画を終了するための文を書いていきます。この処理は、とても単純です。
マウスボタン(タッチパッドなど)の押下を離したときに発生するのは、mouseup
イベントです。ウィンドウに mouseup
イベントを追加して、描画できない状態に切り替えます。
- 変数
mousePressed
の値をfalse
にします。
// ウィンドウにmouseupイベントを追加
window.addEventListener('mouseup', () => mousePressed = false);
ここでは、処理の文が1行のみなので、簡単にアロー関数で書いています。これは、たとえば関数にstopDrawing
という名前をつけて宣言し、下のように書くこともできますよ。
// 関数を別で宣言するコード例
window.addEventListener('mouseup', stopDrawing);
function stopDrawing() {
mousePressed = false;
}
注意:mouseup イベントはキャンバスではなくウィンドウに追加する
mouseup
イベントは、mousedown
や mousemove
とは異なり、canvas
ではなく window
に追加しました。これは、キャンバスの内側か外側かに関わらず、画面上でマウスボタンを離したときに必ず、描画できない状態 mousePressed = false
にする必要があるからです。
もし下のように、canvas
に mouseup
イベントを追加するとどうなるでしょうか。
// 間違いの例
canvas.addEventListener('mouseup', () => mousePressed = false);
上の文を使った場合、キャンバスの外でマウスボタンを離してからキャンバス内にポインターを戻すと、マウスを押下していないのに描画できてしまいます。理由は次のとおりです。
- キャンバスの外で
mouseup
イベントが発生した場合には、mousePressed = false
が実行されない。 - つまり、押下をやめても
mousePressed = true
のままとなっている。 - そのため、キャンバス内でポインターを動かすと関数
draw
が実行されて描画できてしまう。
マウスボタンを離したときに確実に描画を終えるためには、「mouseup
イベントは window
に追加する」ということに注意してくださいね。
消去ボタンでキャンバスの内容を消去する
最後に、消去ボタンを押したときにキャンバスの内容を消すための文を書きましょう。ボタンを押したときに発生するのは click
イベントです。キャンバスに描いたものを全て消去するためには、clearRect()
メソッドでキャンバスの原点 (左上角) 0, 0
からキャンバスの幅 canvas.width
と高さ canvas.height
を指定します。
- 消去ボタンに
click
イベントを追加し、clearRect()
メソッドでキャンバス全体を消去します。
// 消去ボタンにclickイベントを追加
clearBtn.addEventListener('click', () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
});
完成コード
シンプルなお絵描きアプリのコードが完成です。
//*******************************
// シンプルなお絵描きアプリのコード
//*******************************
const canvas = document.querySelector('#drawing-area');
const ctx = canvas.getContext('2d');
const clearBtn = document.querySelector('#clear-button');
let x;
let y;
let mousePressed = false;
// 描画を開始する
function startDrawing(e) {
mousePressed = true;
x = e.offsetX;
y = e.offsetY;
}
// 線を描画する
function draw(e) {
if (!mousePressed) return;
x2 = e.offsetX;
y2 = e.offsetY;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x2, y2);
ctx.stroke();
x = x2;
y = y2;
}
// マウス押下で描画開始
canvas.addEventListener('mousedown', startDrawing);
// マウスを動かして描画
canvas.addEventListener('mousemove', draw);
// マウスボタンを離して描画終了
window.addEventListener('mouseup', () => mousePressed = false);
/// 消去ボタンクリックで全消去
clearBtn.addEventListener('click', () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
});
まとめ
今回は、JavaScript を使ってキャンバスに自由に描画できる、シンプルなお絵描きアプリの作り方を紹介しました。
マウスボタンを押す mousedown
、マウスを動かす mousemove
、マウスボタンを離す mouseup
、それぞれのイベントで実行する処理を分けることでマウスで線を描けるようにしました。描画する線の色や太さは、今回は特に設定していないので、デフォルトの黒 1px です。線の太さや色を選ぶ機能については、【機能追加編】の記事で紹介します。
最後まで読んでいただき、ありがとうございます。この記事をシェアしてくれると嬉しいです!
SNSで Pyxofy とつながりましょう! LinkedIn・ Threads・ Mastodon・ X (Twitter) @pyxofy・ Facebook