import { HTMLElementAnchorData } from '@shapediver/viewer.shared.types' import { vec2, vec3 } from 'gl-matrix' import { ITreeNode } from '@shapediver/viewer.shared.node-tree'; import { ILoader } from '../interfaces/ILoader' import { RenderingEngine } from '../RenderingEngine' import { BUSY_MODE_DISPLAY } from '@shapediver/viewer.rendering-engine.rendering-engine'; export class HTMLElementAnchorLoader implements ILoader { // #region Properties (2) private readonly _htmlElements: { [key: string]: { anchor: HTMLElementAnchorData, node: ITreeNode } } = {}; private readonly _parentDiv: HTMLDivElement; // #endregion Properties (2) // #region Constructors (1) constructor(private readonly _renderingEngine: RenderingEngine) { this._parentDiv = document.createElement('div'); this._parentDiv.style.userSelect = 'none'; this._parentDiv.style.cursor = 'default'; this._parentDiv.style.pointerEvents = 'none'; this._parentDiv.style.overflow = 'hidden'; this._parentDiv.style.position = 'absolute'; this._parentDiv.style.width = '100%'; this._parentDiv.style.height = '100%'; this._parentDiv.style.left = '0%'; this._parentDiv.style.top = '0%'; } // #endregion Constructors (1) // #region Public Accessors (1) public get parentDiv(): HTMLDivElement { return this._parentDiv; } // #endregion Public Accessors (1) // #region Public Methods (5) public adjustPositions(scaleWidth: number, scaleHeight: number): void { for (let anchorId in this._htmlElements) { const anchor = this._htmlElements[anchorId].anchor; const { page, container, client, hidden } = this._renderingEngine.sceneTracingManager.convert3Dto2D(vec3.clone(anchor.location)); const htmlElement = anchor.createViewerHtmlElement(this._renderingEngine.id); if (!htmlElement) continue; let node = this._htmlElements[anchorId].node; let visible = node.visible; while(node.parent) { node = node.parent; visible = node.visible && visible; } if(this._renderingEngine.show === false) visible = false; anchor.update({ anchor, htmlElement, page, container, client, scale: vec2.fromValues(scaleWidth, scaleHeight), hidden, visible }); } } public init(): void { this._renderingEngine.canvas.parentNode?.appendChild(this._parentDiv); } public load(node: ITreeNode, anchor: HTMLElementAnchorData, isVisibleInHierarchy: boolean): void { const htmlElement = anchor.createViewerHtmlElement(this._renderingEngine.id); if (!htmlElement) return; // set the display property to "none" if the viewport is not shown or the node is not visible if(this._renderingEngine.show === false || isVisibleInHierarchy === false) htmlElement.style.display = 'none'; // if the node is not visible return if(isVisibleInHierarchy === false) return; this._parentDiv.appendChild(htmlElement); this._htmlElements[anchor.id + '_' + anchor.version] = { node, anchor }; } public removeData(id: string, version: string) { // since the data object might be there, but no data is loaded for this viewport // this check is needed if(!this._htmlElements[id + '_' + version]) return; const anchor = this._htmlElements[id + '_' + version].anchor; if (anchor && anchor.getViewerHtmlElement(this._renderingEngine.id)) { this._parentDiv.removeChild(anchor.getViewerHtmlElement(this._renderingEngine.id)!); delete this._htmlElements[id + '_' + version] } } public toggleBusyMode(toggle: boolean) { if (toggle && this._renderingEngine.busyModeDisplay === BUSY_MODE_DISPLAY.BLUR) { if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1 && navigator.userAgent.toLowerCase().indexOf('android') > -1) return; this._parentDiv.style.filter = 'blur(3px)'; } else { this._parentDiv.style.filter = ''; } } // #endregion Public Methods (5) }