src/viewport.js
import { Model } from './model';
import { Drawer } from './drawer';
import mouse from './mouse';
/**
* Create a new Viewport
* @example
* const app = new Application({ width: 200, height: 200 });
* const player = new Sprite(...);
* const world = new Container(...);
* const viewport = new Viewport(0, 0, app.canvas, 1, 1, app.width / 2, app.height / 2);
*
* viewport.follow(player, world);
* viewport.drawImage(world);
*/
export class Viewport extends Model {
/**
* @param {number} x - x position of viewport into world
* @param {number} y - y position of viewport into world
* @param {CanvasRenderingContext2D} canvas - canvas where to draw image
* @param {number} [scaleX=1]
* @param {number} [scaleY=1]
* @param {number} [deadZoneX=0] - x position of dead zone (where viewport move when following target)
* @param {number} [deadZoneY=0] - y position of dead zone (where viewport move when following target)
*/
constructor(x, y, canvas, scaleX = 1, scaleY = 1, deadZoneX = 0, deadZoneY = 0) {
super(x, y, canvas.width, canvas.height);
/** @type {CanvasRenderingContext2D} */
this.canvas = canvas;
/** @type {number} */
this.scaleX = scaleX;
/** @type {number} */
this.scaleY = scaleY;
/** @type {number} */
this.deadZoneX = deadZoneX;
/** @type {number} */
this.deadZoneY = deadZoneY;
// Update mouse coordinates
this.canvas.addEventListener('mousemove', event => {
mouse.ax = mouse.x + (this.x * this.scaleX);
mouse.ay = mouse.y + (this.y * this.scaleY);
});
mouse.scaleX = this.scaleX;
mouse.scaleY = this.scaleY;
this.ctx = this.canvas.getContext('2d');
}
/**
* Viewport follow target into world
* @param {Object} target
* @param {Object} world
*/
follow(target, world) {
// Follow target
if (target.x - this.x + (this.deadZoneX / this.scaleX) > this.width / this.scaleX) {
this.x = target.x - ((this.width / this.scaleX) - (this.deadZoneX / this.scaleX));
} else if (target.x - (this.deadZoneX / this.scaleX) < this.x) {
this.x = target.x - (this.deadZoneX / this.scaleX);
}
if (target.y - this.y + (this.deadZoneY / this.scaleY) > (this.height / this.scaleY)) {
this.y = target.y - ((this.height / this.scaleY) - (this.deadZoneY / this.scaleY));
} else if (target.y - (this.deadZoneY / this.scaleY) < this.y) {
this.y = target.y - (this.deadZoneY / this.scaleY);
}
// Rest into world
if (this.x < world.x) {
this.x = world.x;
}
if (this.x + (this.width / this.scaleX) > world.x + world.width) {
this.x = (world.x + world.width) - (this.width / this.scaleX);
}
if (this.y < world.y) {
this.y = world.y;
}
if (this.y + (this.height / this.scaleY) > world.y + world.height) {
this.y = (world.y + world.height) - (this.height / this.scaleY);
}
}
/**
* Drawn image from source into canvas with viewport
* @param {Drawer} source
* @param {number} [x=0]
* @param {number} [y=0]
* @param {number} [width=null]
* @param {number} [height=null]
*/
drawImage(source, x = 0, y = 0, width = null, height = null) {
if (!source instanceof Drawer) {
throw new Error(`Parameter source has to be an instance of Drawer, it's an instance of ${typeof model} instead.`);
}
this.ctx.drawImage(
source.canvas,
this.x,
this.y,
this.width,
this.height,
x,
y,
width || this.canvas.width,
height || this.canvas.height
);
}
}