Canvas上に描画した画像を円運動させてみる。
まず、画像を表示するCatクラス(cat.js)を用意。猫のイラストを表示する単純なもの。
/** * cat.js * 猫画像を表示 */ export class Cat { constructor(parent = undefined, x = 0, y = 0) { if (parent !== undefined) { this._parent = parent; } this._x = x; this._y = y; this._img; this._init(); } ////////////////////////////////// // Private and protected ////////////////////////////////// _init() { if (this._parent !== undefined) { this._ctx = this._parent; } this._img = new Image(); this._img.src = "./cat.png"; this.draw(); } ////////////////////////////////// // Public ////////////////////////////////// draw() { // 現在の描画状態を保存 this._ctx.save(); // コンテキストの座標を変更し、キャンバス中央に移動 this._ctx.translate(this._x, this._y); this._ctx.drawImage(this._img, -(this._img.width / 2), -(this._img.height / 2)); // save()で保存した描画状態を復元 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; } }
次に、画像を円運動させるCircularMotionクラス(circularmotion.js)。Catクラスを使い猫画像を表示して、タイマー処理で円運動させる。
import { Cat } from "./cat.js"; /** * circularmotion.js * 画像を円運動させる */ export class CircularMotion { constructor() { this._cvs = document.getElementById('canvas'); this._ctx = this._cvs.getContext('2d'); this._cat; this._angle = 0; // 角度 this._centerX = this._cvs.width / 2; // 回転円の中心X座標 this._centerY = this._cvs.height / 2; // 回転円の中心Y座標 this._radius = 50; // 回転円の半径 this._speed = 0.05; // 回転速度 // タイマー関連 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 = "#ccc"; this._cat = new Cat(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); // ①猫画像を円形運動させる処理 this._cat.x = this._centerX + Math.sin(this._angle) * this._radius; this._cat.y = this._centerY + Math.cos(this._angle) * this._radius; this._angle += this._speed; this._cat.draw(); this._frame++; if (elapsedTime >= 1000) { this._startTime = this._nowTime; this._frame = 0; } } this._animID = requestAnimationFrame(this._mainLoop.bind(this)); } }
画像を円運動させている箇所が①。三角関数のサイン波でY軸(縦)の動き、コサイン波でX軸(横)の動きを計算し、2つを組み合わせて円運動を実現している。このコードではサイン、コサイン計算で同じ値(this._radius変数)を使っているが、それぞれ異なる値を使えば楕円軌道にもできる。サインとコサインを逆に使えば逆回転する。
三角関数に関しては「【JavaScript】オブジェクトを回転させる」参照。
requestAnimationFrameを使ったゲームループの実装は、「【JavaScript】requestAnimationFrameでゲームループを作る」を参照。
上の例は、中心点からの角度と半径が分かっている時、指定できる時に有用。しかし、分かっているのが位置と中心点だけの場合は使えない。この場合、回転させるオブジェクトの位置と中心点を元に角度と半径を求める。
let dx = this._cat.x - this._centerX;
let dy = this._cat.y - this._centerY;
let angle = Math.atan2(dy, dx);
let radius = Math.sqrt(dx ** 2, dy ** 2);
今オブジェクトがある位置から回転を始めるには以下の公式が使える。
x1 = cos(angle) * x - sin(angle) * y;
y1 = cos(angle) * y - sin(angle) * x;
xとyは回転させる中心点を基準にした、回転させるオブジェクトの座標。cos()やsin()に渡している角度は移動先の角度ではなく、回転する量になる。
以下のスクリプトは読み込む度に、猫画像の出現位置がランダムで変化する。
下記がcircularmotion2クラスのコード。
import { Cat } from "./cat.js"; /** * 画像を円運動させる(circularmotion2.js) */ export class CircularMotion2 { constructor() { this._cvs = document.getElementById('canvas'); this._ctx = this._cvs.getContext('2d'); this._cat; this._angle = 0.05; // 角度 this._centerX = this._cvs.width / 2; // 回転円の中心X座標 this._centerY = this._cvs.height / 2; // 回転円の中心Y座標 // タイマー関連 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._cat = new Cat(this._ctx); // 画像の出現位置をランダムにしている this._cat.x = Math.random() * this._cvs.width; this._cat.y = Math.random() * this._cvs.height; 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); // 回転させる中心点を基準にしたオブジェクトのx,y座標を計算 let oldX = this._cat.x - this._centerX; let oldY = this._cat.y - this._centerY; // 画像を移動させる新しいx,y座標を計算 let newX = Math.cos(this._angle) * oldX - Math.sin(this._angle) * oldY; let newY = Math.cos(this._angle) * oldY + Math.sin(this._angle) * oldX; // newX,newYは中心点を基準にした値のため、それぞれの座標を中心点に加算し、 // 画像の最終的な移動先を取得 this._cat.x = this._centerX + newX; this._cat.y = this._centerY + newY; this._cat.draw(); this._frame++; if (elapsedTime >= 1000) { this._startTime = this._nowTime; this._frame = 0; } } this._animID = requestAnimationFrame(this._mainLoop.bind(this)); } }
メインループ内部では、まず回転させる中心点を基準にした猫画像のX、Y座標を取得。上の公式を使い、猫画像の移動先のX、Y座標を計算。最後に猫画像のX、Y座標に移動先座標を代入するが、その値は回転の中心点を基準にした値のため、中心点のX、Y座標にそれぞれ加算する。
参考図書
コメント