UNPKG

lazy-widgets

Version:

Typescript retained mode GUI for the HTML canvas API

126 lines 4.89 kB
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