lazy-widgets
Version:
Typescript retained mode GUI for the HTML canvas API
126 lines • 4.89 kB
JavaScript
import { TextPasteEvent } from '../events/TextPasteEvent.js';
import { Root } from './Root.js';
import { Msg } from './Strings.js';
/**
* Like Root, but for easy use in an HTML page.
*
* Instead of calling each individual update method, simply call
* {@link DOMRoot#update} on every animation frame. {@link Driver | Drivers}
* still need to be manually registered.
*
* @category Core
*/
export class DOMRoot extends Root {
constructor(child, properties) {
super(child, properties);
// Make DOM element, which is a canvas, and get a 2D context for it
this.domElem = document.createElement('canvas');
this.updateDOMDims();
const context = this.domElem.getContext('2d', { alpha: true });
if (context === null) {
throw new Error(Msg.REUSABLE_CANVAS_CONTEXT);
}
this.domCanvasContext = context;
// Setup pointer style handler
if (this.pointerStyleHandler === null) {
this.pointerStyleHandler = (newPointerStyle) => {
this.domElem.style.cursor = newPointerStyle;
};
}
if (properties === null || properties === void 0 ? void 0 : properties.enablePasteEvents) {
// Listen to paste events
this.domElem.addEventListener('paste', event => {
event.preventDefault();
if (event.clipboardData !== null) {
this.dispatchEvent(new TextPasteEvent(event.clipboardData.getData('text')));
}
});
this.domElem.contentEditable = 'true';
// Remove styling added by contenteditable
this.domElem.style.outline = '0px solid transparent';
this.domElem.style.caretColor = 'transparent';
this.domElem.style.cursor = 'default';
}
this.domSizeController = properties === null || properties === void 0 ? void 0 : properties.domSizeController;
}
/**
* Update DOMRoot.
*
* If root is disabled, {@link DOMRoot#domElem}'s display style is set to
* 'none', hiding it.
*
* Calls {@link Root#preLayoutUpdate}, {@link Root#resolveLayout},
* {@link Root#postLayoutUpdate} and {@link Root#paint}.
*/
update() {
if (this.enabled) {
this.domElem.style.removeProperty('display');
}
else {
return;
}
this.preLayoutUpdate();
if (this.resolveLayout()) {
this.updateDOMDims();
this.rescaleDOMElement();
}
this.postLayoutUpdate();
if (this.paint()) {
this.domCanvasContext.globalCompositeOperation = 'copy';
const [w, h] = this.viewport.usableCanvasDimensions;
let sx = 0, sy = 0;
if (this.preventAtlasBleeding) {
sx++;
sy++;
}
this.domCanvasContext.drawImage(this.canvas, sx, sy, w, h, 0, 0, w, h);
}
}
/** Update the width and height of {@link DOMRoot#domElem} */
updateDOMDims() {
const [scaleX, scaleY] = this.effectiveScale;
const [dimsX, dimsY] = this.dimensions;
// XXX canvas width/height is auto-truncated, so manually round it
// so that values such as 99.9997 don't get turned into 99 instead
// of 100
this.domElem.width = Math.round(dimsX * scaleX);
this.domElem.height = Math.round(dimsY * scaleY);
}
/**
* Re-scale the DOM element of this UI root. You only need to manually call
* this if you use a {@link DOMRoot#domSizeController} and an external
* factor would affect the result of your custom size.
*
* Counters Root viewport scaling with an opposite CSS scale (via width and
* height, not CSS transforms), and applies size given by domSizeController,
* if any is supplied.
*/
rescaleDOMElement() {
const [scaleX, scaleY] = this.effectiveScale;
let wantedWidth = this.domElem.width / scaleX;
let wantedHeight = this.domElem.height / scaleY;
if (this.domSizeController) {
[wantedWidth, wantedHeight] = this.domSizeController(this, [scaleX, scaleY], this.dimensions, [wantedWidth, wantedHeight]);
}
this.domElem.style.width = (wantedWidth).toString() + 'px';
this.domElem.style.height = (wantedHeight).toString() + 'px';
}
get enabled() {
return super.enabled;
}
set enabled(enabled) {
if (!enabled && this._enabled !== enabled) {
this.domElem.style.display = 'none';
}
super.enabled = enabled;
}
destroy() {
super.destroy();
// remove DOM element
const parentElem = this.domElem.parentElement;
if (parentElem) {
parentElem.removeChild(this.domElem);
}
}
}
//# sourceMappingURL=DOMRoot.js.map