|
|
|
'use strict';
|
|
|
|
|
|
|
|
const createEventEmitter = require("create-event-emitter");
|
|
|
|
const defaultValue = require("default-value");
|
|
|
|
|
|
|
|
const requireProperties = require("./require-properties");
|
|
|
|
const calculateOriginOffsets = require("./calculate-origin-offsets");
|
|
|
|
|
|
|
|
module.exports = function createObject(options) {
|
|
|
|
requireProperties(options, ["type", "onRecalculateSize"].concat(defaultValue(options.requiredProperties, [])));
|
|
|
|
|
|
|
|
let object = createEventEmitter(Object.assign({
|
|
|
|
isCached: false,
|
|
|
|
cacheCanvas: document.createElement("canvas"),
|
|
|
|
flipX: false,
|
|
|
|
flipY: false,
|
|
|
|
scaleX: 1,
|
|
|
|
scaleY: 1,
|
|
|
|
originX: "left",
|
|
|
|
originY: "top",
|
|
|
|
opacity: 1,
|
|
|
|
bustCache: function bustCache() {
|
|
|
|
this.emit("bustedCache");
|
|
|
|
this.isCached = false;
|
|
|
|
},
|
|
|
|
bustSize: function bustSize() {
|
|
|
|
this.emit("bustedSize");
|
|
|
|
this.recalculateSize();
|
|
|
|
},
|
|
|
|
set: function setProperties(properties) {
|
|
|
|
let transformedProperties;
|
|
|
|
|
|
|
|
if (this.onSet != null) {
|
|
|
|
transformedProperties = this.onSet(properties);
|
|
|
|
} else {
|
|
|
|
transformedProperties = properties;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object.assign(this, transformedProperties);
|
|
|
|
|
|
|
|
if (Object.keys(transformedProperties).some(property => this.sizeBustingProperties.includes(property))) {
|
|
|
|
/* If the size changes, the cache should also be implicitly busted. */
|
|
|
|
this.bustSize();
|
|
|
|
this.bustCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Object.keys(transformedProperties).some(property => this.cacheBustingProperties.includes(property))) {
|
|
|
|
this.bustCache();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
render: function renderObject(context, options = {}) {
|
|
|
|
if (this.isCached === false) {
|
|
|
|
this.renderToCache(this.cacheCanvas);
|
|
|
|
this.isCached = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.emit("rendering");
|
|
|
|
|
|
|
|
let offset = calculateOriginOffsets({
|
|
|
|
width: this.renderWidth,
|
|
|
|
height: this.renderHeight,
|
|
|
|
originX: this.originX,
|
|
|
|
originY: this.originY
|
|
|
|
});
|
|
|
|
|
|
|
|
context.drawImage(this.cacheCanvas, defaultValue(options.x, 0) + offset.x - this.renderUnderdrawX, defaultValue(options.y, 0) + offset.y - this.renderUnderdrawY);
|
|
|
|
|
|
|
|
this.emit("rendered");
|
|
|
|
},
|
|
|
|
recalculateSize: function recalculateSize() {
|
|
|
|
this.emit("recalculatingSize");
|
|
|
|
|
|
|
|
let newSize = this.onRecalculateSize();
|
|
|
|
|
|
|
|
this.renderWidth = newSize.width * this.scaleX;
|
|
|
|
this.renderHeight = newSize.height * this.scaleY;
|
|
|
|
this.renderOffsetX = defaultValue(newSize.offsetX, 0) * this.scaleX;
|
|
|
|
this.renderOffsetY = defaultValue(newSize.offsetY, 0) * this.scaleY;
|
|
|
|
this.renderUnderdrawX = defaultValue(newSize.underdrawX, 0) * this.scaleX;
|
|
|
|
this.renderUnderdrawY = defaultValue(newSize.underdrawY, 0) * this.scaleY;
|
|
|
|
|
|
|
|
this.cacheCanvas.width = this.renderWidth;
|
|
|
|
this.cacheCanvas.height = this.renderHeight;
|
|
|
|
|
|
|
|
// TODO: bounding box?
|
|
|
|
|
|
|
|
this.emit("recalculatedSize");
|
|
|
|
},
|
|
|
|
renderToCache: function renderToCache(targetCanvas) {
|
|
|
|
let context = targetCanvas.getContext("2d", {
|
|
|
|
alpha: true
|
|
|
|
});
|
|
|
|
|
|
|
|
context.clearRect(0, 0, targetCanvas.width, targetCanvas.height);
|
|
|
|
|
|
|
|
context.save();
|
|
|
|
|
|
|
|
this.emit("renderingToCache");
|
|
|
|
|
|
|
|
let scaleX = (this.flipX === false) ? this.scaleX : (0 - this.scaleX);
|
|
|
|
let scaleY = (this.flipY === false) ? this.scaleY : (0 - this.scaleY);
|
|
|
|
|
|
|
|
context.scale(scaleX, scaleY);
|
|
|
|
context.globalAlpha = this.opacity;
|
|
|
|
|
|
|
|
this.onRender(context);
|
|
|
|
|
|
|
|
this.emit("renderedToCache");
|
|
|
|
|
|
|
|
context.restore();
|
|
|
|
}
|
|
|
|
}, options, {
|
|
|
|
cacheBustingProperties: ["scaleX", "scaleY", "opacity", "flipX", "flipY"].concat(options.cacheBustingProperties),
|
|
|
|
sizeBustingProperties: ["scaleX", "scaleY"].concat(options.sizeBustingProperties)
|
|
|
|
}));
|
|
|
|
|
|
|
|
object.recalculateSize();
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
return object;
|
|
|
|
};
|