'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(); } else 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 - this.renderUnderdrawX - this.renderOverdrawX, height: this.renderHeight - this.renderUnderdrawY - this.renderOverdrawY, 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.renderUnderdrawX = defaultValue(newSize.underdrawX, 0) * this.scaleX; this.renderUnderdrawY = defaultValue(newSize.underdrawY, 0) * this.scaleY; this.renderOverdrawX = defaultValue(newSize.overdrawX, 0) * this.scaleX; this.renderOverdrawY = defaultValue(newSize.overdrawY, 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; };