Compare commits

...

3 Commits

@ -0,0 +1,53 @@
'use strict';
const defaultValue = require("default-value");
const validateSync = require("./validate-sync");
let originAliases = {
top: 0,
left: 0,
center: "50%",
bottom: "100%",
right: "100%"
}
function tryAlias(value) {
if (originAliases[value] != null) {
return originAliases[value];
} else {
return value;
}
}
function calculateOffset(value, origin) {
if (typeof origin === "number") {
return -origin;
} else {
let match = /^(-?[0-9]+(?:\.[0-9]+)?)%$/.exec(origin);
if (match == null) {
throw new Error(`Encountered invalid origin value: ${origin}`);
} else {
let percentage = match[1];
return -(value * (parseFloat(percentage) / 100));
}
}
}
module.exports = function calculateOriginOffsets(options) {
validateSync(options, {
width: "required",
height: "required",
originX: "required",
originY: "required"
});
let originX = tryAlias(options.originX);
let originY = tryAlias(options.originY);
return {
x: calculateOffset(options.width, originX),
y: calculateOffset(options.height, originY)
}
}

@ -0,0 +1,93 @@
'use strict';
const dotty = require("dotty");
const defaultValue = require("default-value");
const createObject = require("./create-object");
const createPropertyMapper = require("./property-mapper");
const max = require("./util/max");
const min = require("./util/min");
function getPosition(object) {
return {
x: defaultValue(object.x, 0),
y: defaultValue(object.y, 0)
}
}
module.exports = function createCompositeObject(options) {
let mapProperties = createPropertyMapper(options.propertyMap);
let compositeObject = createObject(Object.assign({
onSet: function setCompositeProperties(properties) {
let mappedProperties = mapProperties(properties);
Object.keys(mappedProperties).forEach((objectName) => {
if (objectName !== "_") {
if (this.objects[objectName] != null) {
this.objects[objectName].set(mappedProperties[objectName]);
} else {
throw new Error(`The object specified in the property map as '${objectName}' does not exist`);
}
}
});
/* We return the `_` object here, which contains all the unmapped
* properties - we assume that all unmapped properties are meant to
* apply to the composite object rather than its child objects. The
* original `.set` method (as defined in createObject) will then use
* this result. */
return defaultValue(mappedProperties._, {});
},
onRender: function onRender(context) {
Object.keys(this.objects).forEach((objectName) => {
let object = this.objects[objectName];
let position = getPosition(object);
object.render(context, {
x: object.x - this.renderOffsetX,
y: object.y - this.renderOffsetY
});
});
},
onRecalculateSize: function onRecalculateSize() {
let objectMetrics = Object.keys(this.objects).map((objectName) => {
let object = this.objects[objectName];
let position = getPosition(object);
console.log(object.type, [object.x, object.y], [position.x, position.y], object.renderWidth, object.renderHeight);
return {
x1: position.x,
y1: position.y,
x2: position.x + object.renderWidth,
y2: position.y + object.renderHeight
}
});
let x1 = min(objectMetrics.map(object => object.x1));
let y1 = min(objectMetrics.map(object => object.y1));
let x2 = max(objectMetrics.map(object => object.x2));
let y2 = max(objectMetrics.map(object => object.y2));
return {
width: x2 - x1,
height: y2 - y1,
offsetX: x1,
offsetY: y1
}
}
}, options));
Object.keys(compositeObject.objects).forEach((objectName) => {
compositeObject.objects[objectName].on("recalculatedSize", () => {
compositeObject.recalculateSize();
});
compositeObject.objects[objectName].on("renderedToCache", () => {
compositeObject.bustCache();
});
});
return compositeObject;
};

@ -4,6 +4,7 @@ const createEventEmitter = require("create-event-emitter");
const defaultValue = require("default-value");
const validateSync = require("./validate-sync");
const calculateOriginOffsets = require("./calculate-origin-offsets");
module.exports = function createObject(options) {
validateSync(options, {
@ -18,16 +19,29 @@ module.exports = function createObject(options) {
flipY: false,
scaleX: 1,
scaleY: 1,
originX: "left",
originY: "top",
opacity: 1,
bustCache: function bustCache() {
this.isCached = false;
},
set: function setProperties(properties) {
Object.assign(this, properties);
let transformedProperties;
if (this.onSet != null) {
transformedProperties = this.onSet(properties);
} else {
transformedProperties = properties;
}
Object.assign(this, transformedProperties);
if (Object.keys(properties).some(property => this.sizeBustingProperties.includes(property))) {
if (Object.keys(transformedProperties).some(property => this.sizeBustingProperties.includes(property))) {
this.emit("bustedSize");
this.recalculateSize();
}
if (Object.keys(properties).some(property => this.cacheBustingProperties.includes(property))) {
if (Object.keys(transformedProperties).some(property => this.cacheBustingProperties.includes(property))) {
this.emit("bustedCache");
this.isCached = false;
}
@ -35,12 +49,19 @@ module.exports = function createObject(options) {
render: function renderObject(context, options = {}) {
if (this.isCached === false) {
this.renderToCache(this.cacheCanvas);
// TODO: render cached canvas
this.isCached = true;
}
this.emit("rendering");
context.drawImage(this.cacheCanvas, defaultValue(options.x, 0), defaultValue(options.y, 0));
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, defaultValue(options.y, 0) + offset.y);
this.emit("rendered");
},
@ -51,6 +72,8 @@ module.exports = function createObject(options) {
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.cacheCanvas.width = this.renderWidth;
this.cacheCanvas.height = this.renderHeight;
@ -64,6 +87,8 @@ module.exports = function createObject(options) {
alpha: true
});
context.clearRect(0, 0, targetCanvas.width, targetCanvas.height);
context.save();
this.emit("renderingToCache");

@ -2,5 +2,6 @@
module.exports = {
validateSync: require("./validate-sync"),
createObject: require("./create-object")
createObject: require("./create-object"),
createCompositeObject: require("./create-composite-object")
}

@ -62,6 +62,13 @@ module.exports = function createPropertyMapper(propertyMap) {
return Object.assign({
value: propertyValue
}, mappings[propertyName]);
} else {
return {
sourceProperty: propertyName,
destinationObject: "_",
destinationProperty: propertyName,
value: propertyValue
}
}
}).filter((item) => {
return (item != null);

@ -0,0 +1,5 @@
'use strict';
module.exports = function max(values) {
return Math.max(...values);
}

@ -0,0 +1,5 @@
'use strict';
module.exports = function min(values) {
return Math.min(...values);
}
Loading…
Cancel
Save