pixi-game-camera
Version:
A flexible and non-opinionated way to add camera effects to your PIXI application via containers.
117 lines (98 loc) • 4.05 kB
text/typescript
'use strict'
import { Container } from '@pixi/display';
import { Effect } from './effect';
import { Vector } from '../vector';
/**
* A zooming effect that involves the camera zooming in to a particular point on the container.
*/
export class ZoomTo extends Effect {
/**
* The zoom level to zoom to with values larger than 1 being zoomed in and values smaller than 1 being zoomed out.
*
* @private
*
* @property {Vector}
*/
private _desiredZoomLevel: Vector;
/**
* A reference to the easing function to use for this effect.
*
* @private
*
* @property {Function}
*/
private _easing: Function;
/**
* A reference to the initial zoom level.
*
* @private
*
* @property {Vector}
*/
private _initialZoomLevel: Vector;
/**
* Indicates whether the desired x zoom level is greater than the current zoom level or not.
*
* @private
*
* @property {boolean}
*/
private _xIsGreater = false;
/**
* Indicates whether the desired y zoom level is greater than the current zoom level or not.
*
* @private
*
* @property {boolean}
*/
private _yIsGreater = false;
/**
* @param {Container} container A reference to the container to apply the zoomto effect to.
* @param {number} xZoomLevel The zoom level to zoom horizontally with values larger than 1 being zoomed in and values smaller than 1 being zoomed out.
* @param {number} yZoomLevel The zoom level to zoom vertically with values larger than 1 being zoomed in and values smaller than 1 being zoomed out.
* @param {number} duration The amount of time, in milliseconds, that the effect should take.
* @param {Function} [easing] The easing function that should be used.
*/
constructor(container: Container, xZoomLevel: number, yZoomLevel: number, duration: number, easing?: Function) {
super(container);
this.duration = duration;
this._desiredZoomLevel = { x: xZoomLevel, y: yZoomLevel };
this._easing = easing || this.easeLinear;
this._initialZoomLevel = { x: this.container.scale.x, y: this.container.scale.y };
if (this._desiredZoomLevel.x > this._initialZoomLevel.x) this._xIsGreater = true;
if (this._desiredZoomLevel.y > this._initialZoomLevel.y) this._yIsGreater = true;
}
/**
* Updates the status of this effect on a frame by frame basis.
*/
update() {
if (this.criteriaMet() || this.current > this.duration) {
this.finished.dispatch();
return;
}
this.current = performance.now();
const timeDiffPercentage: number = (this.current - this.started) / this.duration;
const percentageThroughAnimation: number = this._easing(timeDiffPercentage);
const xZoomAmount: number = this._desiredZoomLevel.x * percentageThroughAnimation;
const yZoomAmount: number = this._desiredZoomLevel.y * percentageThroughAnimation;
this.container.scale.x = this._xIsGreater ? this._initialZoomLevel.x + xZoomAmount / 2 : this._initialZoomLevel.x - xZoomAmount;
this.container.scale.y = this._yIsGreater ? this._initialZoomLevel.y + yZoomAmount / 2 : this._initialZoomLevel.y - yZoomAmount;
if (this.useRAF) this.id = requestAnimationFrame(() => this.update());
}
/**
* Checks to see if the container's current zoom level is very close to the desired zoom level.
*
* We can't use container zoom == desired zoom because with the game loop we might miss that exact moment so we check a very small window.
*
* @private
*
* @returns {boolean} Returns true if the zoom criteria is met or false otherwise.
*/
criteriaMet(): boolean {
if (
(this.container.scale.x > this._desiredZoomLevel.x - 0.01 && this.container.scale.x < this._desiredZoomLevel.x + 0.01) &&
(this.container.scale.y > this._desiredZoomLevel.y - 0.01 && this.container.scale.y < this._desiredZoomLevel.y + 0.01)
) return true;
return false;
}
}