File: src\canvasrenderer.js
/**
* A canvas renderer base class.
*
* @class CanvasRenderer
* @constructor
* @param {Object} [options] - The settings.
* @param {Number} [options.dt=1/60] - The update rate in seconds.
* @param {Boolean} [options.clearCanvas=true] - Whether the canvas should automatically be cleared.
* @param {Boolean} [options.enabled=true] - Whether the animation should be rendered. If set to false, the render function will merely update the time.
* @param {Number} [options.size] - The canvas size.
*/
export default function CanvasRenderer(options) {
var self = this;
/**
* Delta time in milliseconds.
*
* @property dt
* @type Number
*/
this.dt = 1000.0 / 60.0;
/**
* Used for time based rendering. Milliseconds.
*
* @property now
* @type Number
* @private
*/
this.now = (window.performance !== undefined) ? window.performance.now() : Date.now();
/**
* Used for time based rendering. Milliseconds.
*
* @property then
* @type Number
* @private
*/
this.then = this.now;
/**
* Used for time based rendering. Milliseconds.
*
* @property accumulator
* @type Number
* @private
*/
this.accumulator = 0;
/**
* The rendering context.
*
* @property ctx
* @type CanvasRenderingContext2D
* @private
*/
this.ctx = null;
// Create an initial canvas.
this.canvas = document.createElement("canvas");
/**
* Clear flag.
*
* @property clearCanvas
* @type Boolean
*/
this.clearCanvas = true;
/**
* Enabled flag.
*
* @property enabled
* @type Boolean
*/
this.enabled = true;
// Overwrite the defaults.
if(options !== undefined) {
if(options.dt !== undefined) { this.dt = options.dt * 1000.0; }
if(options.canvas !== undefined) { this.canvas = options.canvas; }
if(options.clearCanvas !== undefined) { this.clearCanvas = options.clearCanvas; }
if(options.enabled !== undefined) { this.enabled = options.enabled; }
this.size = options.size;
}
/**
* The animation loop.
*
* This ugly thing will evolve into an
* arrow function some day!
*
* @method render
*/
this.render = function(now) { self._render(now); };
}
/**
* The canvas.
*
* @property canvas
* @type HTMLCanvasElement
*/
Object.defineProperty(CanvasRenderer.prototype, "canvas", {
get: function() { return this.ctx.canvas; },
set: function(x) {
if(x !== undefined && x.getContext !== undefined) {
this.ctx = x.getContext("2d");
}
}
});
/**
* The size of the canvas.
*
* @property size
* @type Array
* @example
* [width, height]
*/
Object.defineProperty(CanvasRenderer.prototype, "size", {
get: function() {
return [
this.ctx.canvas.width,
this.ctx.canvas.height
];
},
set: function(x) {
if(x !== undefined && x.length === 2) {
this.ctx.canvas.width = x[0];
this.ctx.canvas.height = x[1];
}
}
});
/**
* Abstract update method.
*
* This method will be called by the render function
* at a maximum rate of x fps where x corresponds to
* the refresh rate of the used monitor.
*
* @method update
* @param {Number} elapsed - The time since the last update call in milliseconds.
* @throws An error if not implemented.
*/
CanvasRenderer.prototype.update = function() {
throw new Error("Not implemented.");
};
/**
* Abstract draw method.
*
* @method draw
* @throws An error if not implemented.
*/
CanvasRenderer.prototype.draw = function() {
throw new Error("Not implemented.");
};
/**
* Renders the animation.
*
* @method _render
* @private
* @param {DOMHighResTimeStamp} now - The time since the page was loaded.
* @throws An error if update() or draw() hasn't been implemented.
*/
CanvasRenderer.prototype._render = function(now) {
var elapsed;
if(now === undefined) {
now = (window.performance !== undefined) ? window.performance.now() : Date.now();
}
if(this.clearCanvas) {
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
}
this.now = now;
elapsed = this.now - this.then;
this.then = this.now;
if(this.enabled) {
this.accumulator += elapsed;
if(this.accumulator >= this.dt) {
this.update(elapsed);
this.accumulator -= this.dt;
}
this.draw();
}
};