1. こんな感じに実装する
青いボールをドラッグ&ドロップできます。
See the Pen キャンバス by TKG1 (@tkg1) on CodePen.
これは実際に要素をドラック&ドロップしているわけでは無いです。
マウスで要素をドラッグすると、マウスの座標を取得し、その座標にボールを描写する動作を繰り返しています。
ドラッグ&ドロップイベントはmousedown
、mousemove
、mouseup
イベントで実装します。
2. おおまかな流れ
- canvasと動かしたい対象を用意する。
- 動かしたい対象を描画するためのdraw関数を定義
- canvas上でのマウスの座標を取得
mousedown
→mousemove
→mouseup
のイベント順で実行
CanvasAPIについてはこちらの記事を参考に進めています。

3. canvasの準備
500×500の<canvas>
要素を作成します。
<style>
で<canvas>
のスタイルを設定します。
<html>
<head>
<meta charset="utf-8" />
<title>Canvas Workshop</title>
<style>
* { padding: 0; margin: 0; }
canvas { background: #eee; display: block; margin: 0 auto; }
</style>
</head>
<body>
<canvas id="myCanvas" width="500" height="500"></canvas>
</body>
</html>
4.canvas内にボールを描画する
let canvas = document.getElementById("myCanvas");
let ctx = canvas.getContext("2d");
let ballRadius = 10;
let x = 250;
let y = 250;
// ボールを描画する
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
// canvasの内容を消去して、ボールを描画する
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
}
こちらの記事を参考にしたので、説明は割愛します。

5. マウスの座標を取得
let move = (e) => { // eにはイベントの情報が入る
let rect = e.target.getBoundingClientRect() ;
x = e.clientX - rect.left;
y = e.clientY - rect.top;
draw();
}
clientX
、clientY
はウィンドドキュメントの左上が原点にして、MouseEvent
が起きた点の座標を取得できます。
canvas
上で利用する場合はcanvas
の左上を原点にする必要があるので、ウィンドドキュメンの左上の原点からcanvas
までの距離を引きます。
これでcanvas
の左上からの相対座標になり、canvas
上で正確な座標を取得できます。
x = e.clientX - canvas.offsetLeft;
y = e.clientY - canvas.offsetTop;
画像だとわかりやすいですね。(原点がウィンドドキュメントの左上と考えてください)

しかし、この書き方だと画面を下にスクロールすると、座標にズレが生じてしまいます。
スクロールにも対応したコードがこちら。
let rect = e.target.getBoundingClientRect() ;
x = e.clientX - rect.left;
y = e.clientY - rect.top;
詳細説明はDOM level 3のマウスイベントにおけるカーソル位置の詳細を参考にしてください。
6. イベントリスナーの設定
// loadイベントで、ボールを描画
canvas.addEventListener('load',drawBall());
// ドラッグの部分
canvas.addEventListener('mousedown', ()=> {
canvas.addEventListener('mousemove',move);
});
// ドロップの部分
canvas.addEventListener('mouseup',()=> {
canvas.removeEventListener('mousemove',move);
});
ドラッグ機能にあたる「マウスを長押ししながら、要素を移動する」処理を追加します。
これは、mousedown
(マウスを押された時)が発火した後に、mousemove
(マウスが動いた時)が発火するようにすればいいです。
mouosemove
は変数move
を呼び出しています。これは先ほど定義したマウスの位置の座標を取得し、ボールを描画する関数が入っています。
これで、ボールをドラッグしているように見えます。
次にドロップ機能にあたる「マウスを離したら、要素がその位置に表示される」処理を追加します。
今のままだとmousemove
は、マウスを離した後も発火し続けてしまいます。
なので、mouseup
(マウスを離した時)が発火した後に、removeEventLisner
でmousemove
のイベントリスナーを削除すれば、ドロップしているように見えます。
7. 全体のコード
let canvas = document.getElementById("myCanvas");
let ctx = canvas.getContext("2d");
let ballRadius = 10;
let x = 250;
let y = 250;
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
}
let move = (e) => {
let rect = e.target.getBoundingClientRect() ;
x = e.clientX - rect.left;
y = e.clientY - rect.top;
draw();
}
canvas.addEventListener('load',drawBall());
// 処理を別の関数で定義すれば、まとまったコードになると思います。
canvas.addEventListener('mousedown', ()=> {
canvas.addEventListener('mousemove',move);
});
canvas.addEventListener('mouseup',()=> {
canvas.removeEventListener('mousemove',move);
});
8. 応用:的当てゲーム
今回のコードを応用すれば、ボールを引っ張って、的に当てるゲームが実装できます。
See the Pen 的当てゲーム by TKG1 (@tkg1) on CodePen.
時間があれば、こちらの解説もしたいです。
9. まとめ
ドラッグ&ドロップ機能はcanvas
以外のHTML要素にも使えるので、ぜひ使ってみてください。
また、私自身JavaScriptを勉強中なので、コードの書き方が拙い箇所があると思うので、アドバイス、修正等あればコメントしていただけると嬉しいです。
コメント