UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

197 lines (155 loc) 4.51 kB
import Rectangle from "../../core/geom/2d/Rectangle.js"; import AABB2 from "../../core/geom/2d/aabb/AABB2.js"; import { assert } from "../../core/assert.js"; /** * * @param {Element} el * @param {number} depth * @param {AABB2} aabb */ export function resizeAABB2ToFitBoundingClientRect(el, depth, aabb) { /** * * @type {ClientRect | DOMRect} */ const rect = el.getBoundingClientRect(); if (rect.left < aabb.x0) { aabb.x0 = rect.left; } if (rect.right > aabb.x1) { aabb.x1 = rect.right; } if (rect.top < aabb.y0) { aabb.y0 = rect.top; } if (rect.bottom > aabb.y1) { aabb.y1 = rect.bottom; } if (depth > 0) { const children = el.children; for (let i = 0, l = children.length; i < l; i++) { const child = children[i]; resizeAABB2ToFitBoundingClientRect(child, depth - 1, aabb); } } } const aabb = new AABB2(); /** * Size observer of a DOM element */ export class DomSizeObserver { /** * * @param {number} [depth=0] how deep should the observation go */ constructor({ depth = 0 } = {}) { /** * * @type {Rectangle} */ this.dimensions = new Rectangle(); /** * * @type {number} * @private */ this.__depth = depth; /** * * @type {HTMLElement} */ this.element = null; /** * @private * @type {boolean} */ this.running = false; /** * * @type {ResizeObserver} * @private */ this.__dom_resize_observer = new ResizeObserver(this.__dom_resize_observer_callback.bind(this)); } /** * * @param {ResizeObserverEntry[]} entries * @param {ResizeObserver} observer * @private */ __dom_resize_observer_callback(entries, observer) { const first_entry = entries[0]; /** * * @type {ClientRect | DOMRect} */ const rect = first_entry.contentRect; aabb.setNegativelyInfiniteBounds(); resizeAABB2ToFitBoundingClientRect(this.element, this.__depth, aabb); const rectangle = this.dimensions; rectangle.position.set(aabb.x0, aabb.y0); rectangle.size.set(rect.width, rect.height); } synchronize() { aabb.setNegativelyInfiniteBounds(); resizeAABB2ToFitBoundingClientRect(this.element, this.__depth, aabb); const rectangle = this.dimensions; rectangle.position.set(aabb.x0, aabb.y0); rectangle.size.set(aabb.getWidth(), aabb.getHeight()); } start() { if (this.running) { // already running return; } this.running = true; // perform an immediate update this.synchronize(); if (this.element !== null) { this.__dom_resize_observer.observe(this.element); } } /** * * @param {Element} element */ attach(element) { this.element = element; if (this.running) { this.__dom_resize_observer.unobserve(this.element); } } stop() { if (!this.running) { // not running return; } this.running = false; if (this.element !== null) { this.__dom_resize_observer.unobserve(this.element); } } /** * Will attach to the view's element and automatically start/stop observing when the view is linked/unlinked * @param {View} view */ watchView(view) { assert.defined(view, 'view'); assert.notNull(view, 'view'); assert.equal(view.isView, true, 'view.isView !== true'); this.attach(view.el); view.on.linked.add(this.start, this); view.on.unlinked.add(this.stop, this); } /** * Counterpart to {@link #watchView} * @param {View} view */ unwatchView(view) { assert.defined(view, 'view'); assert.notNull(view, 'view'); assert.equal(view.isView, true, 'view.isView !== true'); view.on.linked.remove(this.start, this); view.on.unlinked.remove(this.stop, this); } }