var inherit = require('../inherit');
var registerRenderer = require('../registry').registerRenderer;
var renderer = require('../renderer');
/**
* Create a new instance of class canvasRenderer.
*
* @class
* @alias geo.canvas.renderer
* @extends geo.renderer
* @param {object} arg Options for the renderer.
* @param {geo.layer} [arg.layer] Layer associated with the renderer.
* @param {HTMLElement} [arg.canvas] Canvas element associated with the
* renderer.
* @returns {geo.canvas.canvasRenderer}
*/
var canvasRenderer = function (arg) {
'use strict';
var $ = require('jquery');
if (!(this instanceof canvasRenderer)) {
return new canvasRenderer(arg);
}
arg = arg || {};
renderer.call(this, arg);
var m_this = this,
m_clearCanvas = true,
s_init = this._init,
s_exit = this._exit;
/**
* Set the clear canvas flag. If truthy, the canvas is erased at the start
* of the render cycle. If falsy, the old data is kept.
*
* @param {boolean} arg Truthy to clear the canvas when rendering is started.
*/
this.clearCanvas = function (arg) {
m_clearCanvas = arg;
};
/**
* Get API used by the renderer.
*
* @returns {string} 'canvas'.
*/
this.api = function () {
return canvasRenderer.apiname;
};
/**
* Initialize.
*
* @returns {this}
*/
this._init = function () {
if (m_this.initialized()) {
return m_this;
}
s_init.call(m_this);
var canvas = arg.canvas || $(document.createElement('canvas'));
m_this.context2d = canvas[0].getContext('2d');
canvas.addClass('canvas-canvas');
$(m_this.layer().node().get(0)).append(canvas);
m_this.canvas(canvas);
/* Initialize the size of the renderer */
var map = m_this.layer().map(),
mapSize = map.size();
m_this._resize(0, 0, mapSize.width, mapSize.height);
return m_this;
};
/**
* Handle resize event.
*
* @param {number} x Ignored.
* @param {number} y Ignored.
* @param {number} w New width in pixels.
* @param {number} h New height in pixels.
* @returns {this}
*/
this._resize = function (x, y, w, h) {
var canvas = m_this.canvas();
if (parseInt(canvas.attr('width'), 10) !== w ||
parseInt(canvas.attr('height'), 10) !== h) {
m_this._setWidthHeight(w, h);
canvas.attr('width', w);
canvas.attr('height', h);
m_this._render();
}
return m_this;
};
/**
* Render.
*
* @returns {this}
*/
this._render = function () {
m_this.layer().map().scheduleAnimationFrame(m_this._renderFrame);
return m_this;
};
/**
* Render during an animation frame callback.
*/
this._renderFrame = function () {
var layer = m_this.layer(),
map = layer.map(),
mapSize = map.size(),
features = layer.features ? layer.features() : [],
i;
for (i = 0; i < features.length; i += 1) {
if (features[i]._delayRender()) {
// reschedule the render for the next animation frame
m_this._render();
// exit this render loop so it doesn't occur
return;
}
}
// Clear the canvas.
if (m_clearCanvas) {
m_this.context2d.setTransform(1, 0, 0, 1, 0, 0);
m_this.context2d.clearRect(0, 0, mapSize.width, mapSize.height);
}
for (i = 0; i < features.length; i += 1) {
if (features[i].visible()) {
features[i]._renderOnCanvas(m_this.context2d, map);
}
}
};
/**
* Exit.
*/
this._exit = function () {
m_this.canvas().remove();
s_exit();
};
return this;
};
canvasRenderer.apiname = 'canvas';
inherit(canvasRenderer, renderer);
registerRenderer('canvas', canvasRenderer);
/* Code for checking if the renderer is supported */
var checkedCanvas;
/**
* Report if the canvas renderer is supported.
*
* @returns {boolean} true if available.
*/
canvasRenderer.supported = function () {
if (checkedCanvas === undefined) {
/* This is extracted from what Modernizr uses. */
var canvas; // eslint-disable-line no-unused-vars
try {
canvas = document.createElement('canvas');
checkedCanvas = !!(canvas.getContext && canvas.getContext('2d'));
} catch (e) {
checkedCanvas = false;
}
canvas = undefined;
}
return checkedCanvas;
};
/**
* If the canvas renderer is not supported, supply the name of a renderer that
* should be used instead. This asks for the null renderer.
*
* @returns {null} `null` for the null renderer.
*/
canvasRenderer.fallback = function () {
return null;
};
module.exports = canvasRenderer;