Canvas上の矢印がマウスカーソルを追いかけてくる。ただ追いかけるだけでなく、マウスカーソルの方向を向いて追ってくる。ベースは「【JavaScript】オブジェクトを回転させる」であり、そこに移動処理を加えている。
ArrowクラスとMousestalkerクラス
まず、簡単なCanvasに矢印を描画するArrowクラス。
/** * 矢印の作成と描画 */ export class Arrow { constructor(parent = undefined, x = 0, y = 0) { if (parent !== undefined) { this._parent = parent; } this._x = x; this._y = y; this._color = "#ff0000"; this._rotation = 0; this._init(); } ////////////////////////////////// // Private and protected ////////////////////////////////// _init() { if (this._parent !== undefined) { this._ctx = this._parent; } this.draw(); } (mousestalker.js) ////////////////////////////////// // Public ////////////////////////////////// draw() { // 現在の描画状態を保存 this._ctx.save(); // コンテキストの座標を変更し、キャンバス中央に移動 this._ctx.translate(this._x, this._y); // コンテキストの角度をラジアン値で指定 this._ctx.rotate(this._rotation); this._ctx.fillStyle = this._color; this._ctx.beginPath(); this._ctx.moveTo(-50, -25); this._ctx.lineTo(0, -25); this._ctx.lineTo(0, -50); this._ctx.lineTo(50, 0); this._ctx.lineTo(0, 50); this._ctx.lineTo(0, 25); this._ctx.lineTo(-50, 25); this._ctx.lineTo(-50, -25); this._ctx.closePath(); this._ctx.fill(); this._ctx.stroke(); // save()で保存した描画状態を復元 // 変形情報(translate、rotate)を変更前に戻している this._ctx.restore(); } ////////////////////////////////// // Getters/Setters ////////////////////////////////// get x() { return this._x; } set x(x) { this._x = x; } get y() { return this._y; } set y(y) { this._y = y; } get rotation() { return this._rotation; } set rotation(rotation) { this._rotation = rotation; } }
次に、追いかける処理を担当するMouseStalkerクラス。
import { Arrow } from "./arrow.js"; /** * マウスカーソルを追跡させる(mousestalker.js) */ export class MouseStalker { constructor() { this._cvs = document.getElementById('canvas'); this._ctx = this._cvs.getContext('2d'); this._arrow; this._speed = 3; this._mouseX = 0; this._mouseY = 0; this._cvs.addEventListener('mousemove', this._onMouseMove.bind(this)); // タイマー関連 this._animID; this._isAnim = 0; this._FPS = 60; this._frame = 0; this._startTime; this._nowTime; this._init(); } ////////////////////////////////// // Private and protected ////////////////////////////////// _init() { this._cvs.style.backgroundColor = "#eeeeee"; this._arrow = new Arrow(this._ctx); this._startTime = performance.now(); this._mainLoop(); } _mainLoop() { this._nowTime = performance.now(); let elapsedTime = this._nowTime - this._startTime; let idealTime = this._frame * (1000 / this._FPS); if (idealTime < elapsedTime) { this._ctx.clearRect(0, 0, this._cvs.width, this._cvs.height); // ①矢印の回転と移動量の計算 let dx = this._mouseX - this._arrow.x; let dy = this._mouseY - this._arrow.y; let angle = Math.atan2(dy, dx); let vx = Math.cos(angle) * this._speed; let vy = Math.sin(angle) * this._speed; this._arrow.rotation = angle; this._arrow.x += vx; this._arrow.y += vy; this._arrow.draw(); this._frame++; if (elapsedTime >= 1000) { this._startTime = this._nowTime; this._frame = 0; } } this._animID = requestAnimationFrame(this._mainLoop.bind(this)); } ////////////////////////////////// // Handlers ////////////////////////////////// _onMouseMove(e) { this._mouseX = e.clientX; this._mouseY = e.clientY; } }
三角関数に関しては「【JavaScript】オブジェクトを回転させる」で解説している。
Canvasにmousemoveイベントを登録。マウスカーソルが動く度にthis._mouseX、this._mouseYに格納。Math.atan2()に渡してマウスカーソルに対する角度を求める。
ここでは角度を仮に30度とする。矢印とマウスカーソルの位置を結んだ線を斜辺とする。
この斜辺が矢印が進む道であり、進む量は速度(this._speed)を使う。直角三角形は1つの辺と1つの角度が分かれば、他全てを求められる。
さらに、カーソルに向かってX座標、Y座標上に線を伸ばしていくと直角三角形ができる。つまり、角度と斜辺の長さ(速度)が分かれば、Math.cos()とMath.sin()を使い、this._vxとthis._vyの長さが求められる。これにより1フレームレートにどの方向にどれだけ移動させられるか分かる。
コサインは隣辺と斜辺の比率(隣辺 ÷ 斜辺)だから、隣辺はコサインに斜辺を掛ければ求まる。同様にサインは対辺と斜辺の比率(対辺 ÷ 斜辺)だから、対辺はサインに斜辺を掛ければ求まる。
斜辺の長さは即ち速度だから、mousestalker.jsのコード①の部分で求まる。
this._vx = Math.cos(angle) * this._speed;
this._vy = Math.sin(angle) * this._speed;
手順をまとめると、
- マウスカーソルのX、Y座標を取得
- Math.atan2()にX、Y座標を渡し角度を求める
- 角度を使い矢印を回転させる
- コサインに速度を掛け、X座標上の移動距離を計算
- サインに速度を掛け、Y座標上の移動距離を計算
- 矢印を計算結果を元に移動
requestAnimationFrameを使ったゲームループの実装は、「【JavaScript】requestAnimationFrameでゲームループを作る」を参照。
HTML(index.html)のコード。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JavaScriptでマウスカーソルを追いかけるマウスストーカーを作る</title>
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
</body>
<script type="module">
import { MouseStalker } from "./mousestalker.js";
window.onload = function () {
let ms = new MouseStalker();
}
</script>
</html>
参考図書
コメント