@egjs/view3d
Version:
Fast & Customizable glTF 3D model viewer, packed with full of features!
184 lines (155 loc) • 6.64 kB
text/typescript
/*
* Copyright (c) 2020 NAVER Corp.
* egjs projects are licensed under the MIT license
*/
import View3D from "../View3D";
import CloseIcon from "../asset/close-icon";
import { EVENTS } from "../const/external";
import * as BROWSER from "../const/browser";
import { range } from "../utils";
import View3DPlugin from "./View3DPlugin";
/**
* Options for the {@link AROverlay}
* @interface
* @param {AROverlay.DEFAULT_CLASS} [className={}] Override default class names
* @param {boolean} [showPlaneDetection=true] Whether to show plane detection icon / toast before placing the model
* @param {string} [toastText="Point your device downwards to find the ground and move it around."] Toast text
*/
export interface AROverlayOptions {
className: Partial<{ -readonly [key in keyof typeof AROverlay.DEFAULT_CLASS]: string }>;
showPlaneDetection: boolean;
toastText: string;
}
/**
* An UI that will be displayed on top of {@link WebARSession}.
* This will be automatically added on the overlayRoot of the {@link WebARSession}.
*/
class AROverlay implements View3DPlugin {
/**
* Default class names that AROverlay uses
* @type {object}
* @property {"view3d-ar-root"} ROOT A class name for the root element of AROverlay
* @property {"view3d-ar-close"} CLOSE_BUTTON A class name for the close button element
* @property {"view3d-ar-detection"} DETECTION_ROOT A class name for the root element of floor detection annotator
* @property {"view3d-ar-detection-icon"} DETECTION_ICON A class name for the wrapper element of floor detection icon
* @property {"view3d-ar-detection-toast"} DETECTION_TOAST A class name for the toast element of floor detection annotator
* @property {"view3d-ar-phone"} DETECTION_PHONE A class name for the root element of floor detection phone shape
* @property {"view3d-ar-cube"} DETECTION_CUBE A class name for the root element of floor detection cube
* @property {"view3d-ar-cube-face"} DETECTION_CUBE_FACE A class name for the face elements of floor detection cube
* @property {"view3d-ar-plane"} DETECTION_PLANE A class name for the face elements of floor detection plane
*/
public static readonly DEFAULT_CLASS = {
ROOT: "view3d-ar-root",
CLOSE_BUTTON: "view3d-ar-close",
DETECTION_ROOT: "view3d-ar-detection",
DETECTION_ICON: "view3d-ar-detection-icon",
DETECTION_TOAST: "view3d-ar-detection-toast",
DETECTION_PHONE: "view3d-ar-phone",
DETECTION_CUBE: "view3d-ar-cube",
DETECTION_CUBE_FACE: "view3d-ar-cube-face",
DETECTION_PLANE: "view3d-ar-plane",
DETECTION_VISIBLE: "visible"
} as const;
public className: AROverlayOptions["className"];
public showPlaneDetection: AROverlayOptions["showPlaneDetection"];
public toastText: AROverlayOptions["toastText"];
private _rootEl: HTMLElement;
private _closeButtonEl: HTMLElement;
private _detectionRootEl: HTMLElement | null;
/**
* Create new instance of AROverlay
* @param {object} [options={}] Options for the AROverlay
*/
public constructor({
className = {},
showPlaneDetection = true,
toastText = "Point your device downwards to find the ground and move it around."
}: Partial<AROverlayOptions> = {}) {
this.className = className;
this.showPlaneDetection = showPlaneDetection;
this.toastText = toastText;
this._createElements();
}
public async init(view3D: View3D) {
const rootEl = this._rootEl;
const detectionRoot = this._detectionRootEl;
const closeButton = this._closeButtonEl;
const className = {
...AROverlay.DEFAULT_CLASS,
...this.className
};
view3D.on(EVENTS.AR_START, ({ session }) => {
const overlayRoot = session.domOverlay.root;
if (!overlayRoot) return;
overlayRoot.appendChild(rootEl);
const closeButtonHandler = () => {
void session.exit();
};
detectionRoot?.classList.add(className.DETECTION_VISIBLE);
const onPlacedHandler = () => {
detectionRoot?.classList.remove(className.DETECTION_VISIBLE);
};
view3D.once(EVENTS.AR_MODEL_PLACED, onPlacedHandler);
closeButton.addEventListener(BROWSER.EVENTS.CLICK, closeButtonHandler);
view3D.once(EVENTS.AR_END, () => {
if (rootEl.parentElement) {
rootEl.parentElement.removeChild(rootEl);
}
closeButton.removeEventListener(BROWSER.EVENTS.CLICK, closeButtonHandler);
view3D.off(EVENTS.AR_MODEL_PLACED, onPlacedHandler);
});
});
}
public teardown() {
// DO NOTHING
}
private _createElements() {
const className = {
...AROverlay.DEFAULT_CLASS,
...this.className
};
const root = document.createElement(BROWSER.EL_DIV);
const closeButton = document.createElement(BROWSER.EL_DIV);
closeButton.classList.add(className.CLOSE_BUTTON);
closeButton.innerHTML = CloseIcon;
root.classList.add(className.ROOT);
root.appendChild(closeButton);
if (this.showPlaneDetection) {
this._detectionRootEl = this._createPlaneDetectionElements();
root.appendChild(this._detectionRootEl);
}
this._rootEl = root;
this._closeButtonEl = closeButton;
}
private _createPlaneDetectionElements() {
const className = {
...AROverlay.DEFAULT_CLASS,
...this.className
};
const detectionRoot = document.createElement(BROWSER.EL_DIV);
const detectionIcon = document.createElement(BROWSER.EL_DIV);
const detectionLabel = document.createElement(BROWSER.EL_DIV);
const detectionPhone = document.createElement(BROWSER.EL_DIV);
const detectionCube = document.createElement(BROWSER.EL_DIV);
const detectionPlane = document.createElement(BROWSER.EL_DIV);
const cubeFaces = range(5).map(() => document.createElement(BROWSER.EL_DIV));
detectionRoot.classList.add(className.DETECTION_ROOT);
detectionIcon.classList.add(className.DETECTION_ICON);
detectionLabel.classList.add(className.DETECTION_TOAST);
detectionPhone.classList.add(className.DETECTION_PHONE);
detectionCube.classList.add(className.DETECTION_CUBE);
detectionPlane.classList.add(className.DETECTION_PLANE);
detectionLabel.innerHTML = this.toastText;
cubeFaces.forEach(face => {
face.classList.add(className.DETECTION_CUBE_FACE);
detectionCube.appendChild(face);
});
detectionIcon.appendChild(detectionPhone);
detectionIcon.appendChild(detectionCube);
detectionIcon.appendChild(detectionPlane);
detectionRoot.appendChild(detectionIcon);
detectionRoot.appendChild(detectionLabel);
return detectionRoot;
}
}
export default AROverlay;