【JavaScript】Canvas要素をドラッグ&ドロップで動かす【ゲーム】

JavaScript

1. こんな感じに実装する

青いボールをドラッグ&ドロップできます。

See the Pen キャンバス by TKG1 (@tkg1) on CodePen.

これは実際に要素をドラック&ドロップしているわけでは無いです。

マウスで要素をドラッグすると、マウスの座標を取得し、その座標にボールを描写する動作を繰り返しています。

ドラッグ&ドロップイベントはmousedownmousemovemouseupイベントで実装します。

2. おおまかな流れ

  1. canvasと動かしたい対象を用意する。
  2. 動かしたい対象を描画するためのdraw関数を定義
  3. canvas上でのマウスの座標を取得
  4. mousedownmousemovemouseupのイベント順で実行

CanvasAPIについてはこちらの記事を参考に進めています。

純粋な JavaScript を使ったブロック崩しゲーム - ゲーム開発 | MDN
このステップバイステップのチュートリアルでは、すべて JavaScript だけで書かれた、 HTML5 の で表示できる簡単な MDN ブロック崩しゲームを作ります。

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();
}

こちらの記事を参考にしたので、説明は割愛します。

キャンバスを作ってその上に描画する - ゲーム開発 | MDN
これは、ゲーム開発キャンバスチュートリアル の 10 ステップ中 1 ステップ目になります。このレッスンを終えた後のソースコードは、Gamedev-Canvas-workshop/lesson1.htmlで見ることができます。

5. マウスの座標を取得

let move = (e) => { // eにはイベントの情報が入る
  let rect = e.target.getBoundingClientRect() ;
  x = e.clientX - rect.left;
  y = e.clientY - rect.top;
  draw();
}

clientXclientYはウィンドドキュメントの左上が原点にして、MouseEventが起きた点の座標を取得できます。

canvas上で利用する場合はcanvasの左上を原点にする必要があるので、ウィンドドキュメンの左上の原点からcanvasまでの距離を引きます。

これでcanvasの左上からの相対座標になり、canvas上で正確な座標を取得できます。

  x = e.clientX - canvas.offsetLeft;
  y = e.clientY - canvas.offsetTop;

画像だとわかりやすいですね。(原点がウィンドドキュメントの左上と考えてください)

抜粋:https://www.instructables.com/Making-a-Joystick-With-HTML-pure-JavaScript/

しかし、この書き方だと画面を下にスクロールすると、座標にズレが生じてしまいます。

スクロールにも対応したコードがこちら。

  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(マウスを離した時)が発火した後に、removeEventLisnermousemoveのイベントリスナーを削除すれば、ドロップしているように見えます。

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を勉強中なので、コードの書き方が拙い箇所があると思うので、アドバイス、修正等あればコメントしていただけると嬉しいです。

コメント

タイトルとURLをコピーしました