import { constructors } from '../core/library.js';
import { doCreate, mergeOver, xt, Ωempty } from '../helper/utilities.js';
import { makeCanvas } from './canvas.js';
import baseMix from '../mixin/base.js';To be aware - this artefact factory is HIGHLY EXPERIMENTAL; its API will be subject to short-notice breaking changes as we amend and inprove the artefact’s functionality
TODO - documentation
import { constructors } from '../core/library.js';
import { doCreate, mergeOver, xt, Ωempty } from '../helper/utilities.js';
import { makeCanvas } from './canvas.js';
import baseMix from '../mixin/base.js';Shared constants
import { _computed, _entries, _floor, _max, ABSOLUTE, AUTO, CANVAS, HEIGHT, RELATIVE, WIDTH } from '../helper/shared-vars.js';Local constants
const DATA_SCRAWL_NAME = 'data-scrawl-name',
STATIC = 'static',
T_UNSTACKED_ELEMENT = 'UnstackedElement',
UE_INCLUDED_STYLES = ['width', 'height', 'zIndex', 'borderBottomLeftRadius', 'borderBottomRightRadius', 'borderTopLeftRadius', 'borderTopRightRadius'],
UE_MIMICKED_STYLES = ['borderBottomLeftRadius', 'borderBottomRightRadius', 'borderTopLeftRadius', 'borderTopRightRadius'],
UNSTACKEDELEMENT = 'unstackedelement',
Z_INDEX = 'zIndex';const UnstackedElement = function (el) {
const name = el.id || el.name;
this.makeName(name);
this.register();
el.setAttribute(DATA_SCRAWL_NAME, this.name);
this.domElement = el;
this.elementComputedStyles = _computed(el);
this.hostStyles = {};
this.canvasStartX = 0;
this.canvasStartY = 0;
this.canvasWidth = 0;
this.canvasHeight = 0;
this.canvasZIndex = 0;
return this;
};const P = UnstackedElement.prototype = doCreate();
P.type = T_UNSTACKED_ELEMENT;
P.lib = UNSTACKEDELEMENT;
P.isArtefact = false;
P.isAsset = false;Apply mixins to prototype object
baseMix(P);const defaultAttributes = {TODO - documentation
canvasOnTop: false,
};
P.defs = mergeOver(P.defs, defaultAttributes);This is going to be rewritten as part of the “kill” review/recode work
P.demolish = function () {
return true;
};Adds a canvas element to sit behind the element, or in front of it, depending on the setting of the canvasOnTop attribute
P.addCanvas = function (items = Ωempty) {
if (!this.canvas) {
const canvas = document.createElement(CANVAS),
el = this.domElement,
style = el.style;
if (style.position === STATIC) style.position = RELATIVE;
canvas.id = `${this.name}-canvas`;
el.prepend(canvas);
const art = makeCanvas({
name: `${this.name}-canvas`,
domElement: canvas,
position: ABSOLUTE,
});
this.canvas = art;
art.set(items);
this.updateCanvas();
return art;
}
};Observer function - runs on every RAF loop (when the DOM element is viewable); aim is to:
P.checkElementStyleValues = function () {
const results = {};
const el = this.domElement,
wrapper = this.canvas;
if (el && wrapper && wrapper.domElement) {
const host = this.hostStyles,
style = this.elementComputedStyles,
canvas = wrapper.domElement;
let {x: elX, y: elY, width: elW, height: elH} = el.getBoundingClientRect();
let {x: canvasX, y: canvasY} = canvas.getBoundingClientRect();
let {width: styleW, height: styleH} = style;
const styleZ = style.zIndex;
elX = _floor(elX);
elY = _floor(elY);
canvasX = _floor(canvasX);
canvasY = _floor(canvasY);
elW = _floor(elW);
elH = _floor(elH);
styleW = _floor(parseFloat(styleW));
styleH = _floor(parseFloat(styleH));
let w, h, z, hi, si;
UE_INCLUDED_STYLES.forEach(item => {
switch (item) {
case WIDTH :
w = _max(styleW, elW);
if (this.canvasWidth !== w) {
this.canvasWidth = w;
this.dirtyDimensions = true;
}
break;
case HEIGHT :
h = _max(styleH, elH);
if (this.canvasHeight !== h) {
this.canvasHeight = h;
this.dirtyDimensions = true;
}
break;
case Z_INDEX :
z = (styleZ === AUTO) ? 0 : parseInt(styleZ, 10);
z = (this.canvasOnTop) ? z + 1 : z - 1;
if (this.canvasZIndex !== z) {
this.canvasZIndex = z;
this.dirtyZIndex = true;
}
break;
default :
hi = host[item];
si = style[item];
if(!xt(hi) || hi !== si) {
host[item] = si;
results[item] = si;
}
}
});
const dx = elX - canvasX,
dy = elY - canvasY;
if (dx || dy) {
this.canvasStartX += dx;
this.canvasStartY += dy;
this.dirtyStart = true;
}
}
return results;
};Perform any updates reported by the observer function
UnstackedElement canvases keep their display and base canvases in dimensional sync - which means that relatively positioned and dimensioned entitys in the canvas (those set with appropriate ‘val%’ string values) will update in line with changes in the DOM element’s width, height, padding and margin values
P.updateCanvas = function () {
if (this.canvas && this.canvas.domElement) {
const canvas = this.canvas,
style = canvas.domElement.style,
updates = this.checkElementStyleValues();
for (const [key, value] of _entries(updates)) {
if (UE_MIMICKED_STYLES.includes(key)) {
style[key] = value;
}
}
if (this.dirtyStart) {
this.dirtyStart = false;
canvas.set({
startX: this.canvasStartX,
startY: this.canvasStartY,
});
}
if (this.dirtyDimensions) {
this.dirtyDimensions = false;
const w = this.canvasWidth,
h = this.canvasHeight;
canvas.set({
width: w,
height: h,
});
canvas.dirtyDimensions = true;
canvas.base.set({
width: w,
height: h,
});
canvas.base.dirtyDimensions = true;
canvas.cleanDimensions();
canvas.base.cleanDimensions();
}
if (this.dirtyZIndex) {
this.dirtyZIndex = false;
canvas.stampOrder = this.canvasZIndex;
}
}
};export const makeUnstackedElement = function (items) {
if (!items) return false;
return new UnstackedElement(items);
};
constructors.UnstackedElement = UnstackedElement;