UNPKG

@vci/quick-three

Version:

quick three

149 lines (135 loc) 4.65 kB
import { instantiateComponentVm } from "../../components/vue.component"; import { getScreenCoordsFromWorldCoords } from "../helper/ThingHelper"; import { getElementMaxZIndex } from "@vci/helper/src/element"; import { ThingEvents } from "../events/ThingEvents"; import { mergeDeep } from "@vci/helper/src/object"; import Thing from "./Thing"; import { Vector3 } from "three"; import Animator from "../core/Animator"; // 弹框 export default class Popup extends Animator { constructor(option) { super(mergeDeep( { // 弹框配置 popup: { enableBindEventClick: true, enableAdapt: true, enableAdaptHorizontal: false, component: null, position: null, offset: [0, 0, 0] } }, option )); } init() { super.init(); this.isThingPopup = true; Object.defineProperty(this, "popupCenter", { get: () => this.vm ? this.vm.center : null, set: center => { this.vm && (this.vm.center = center); }, configurable: true, enumerable: true }); Object.defineProperty(this, "enableAdapt", { get: () => this.option.popup.enableAdapt, set: enableAdapt => { this.option.popup.enableAdapt = enableAdapt; }, configurable: true, enumerable: true }); Object.defineProperty(this, "enableAdaptHorizontal", { get: () => this.option.popup.enableAdaptHorizontal, set: enableAdaptHorizontal => { this.option.popup.enableAdaptHorizontal = enableAdaptHorizontal; }, configurable: true, enumerable: true }); } afterInstantiation() { super.afterInstantiation(); const { enableBindEventClick } = this.option.popup; enableBindEventClick && this.addEventListener(ThingEvents.Click, async () => !this.vm ? await this.openPopup() : await this.closePopup()); this.addEventListener(ThingEvents.HoverOn, this.topping.bind(this)); } update(option) { super.update(option); this.vm && (this.vm.extData = this.extData); return this; } getAdaptPosition() { const { position, offset } = this.option.popup; if (position) return position; else return this.getCenter(Thing.CenterMode.CenterTop).add(new Vector3().fromArray(offset)).toArray(); } async openPopup(component) { component = component || this.option.popup.component; if (this.vm) return Promise.resolve(); if (!component) return Promise.reject("未配置弹框组件"); this.vm = instantiateComponentVm(component, { propsData: { extData: this.extData } }); this.vm.topping = this.topping.bind(this); setTimeout(this.adaptPopupPosition.bind(this)); this.qt.el.appendChild(this.vm.$el); this.vm.$on("close", () => this.vm && this.closePopup()); this.topping(); await this.vm.fadeIn(); } async closePopup() { if (!this.vm) return false; const vm = this.vm; delete this.vm; const elQt = this.qt.el; await vm.fadeOut(); elQt.removeChild(vm.$el); vm.$destroy(); } adaptPopupPosition() { if (this.vm && this.enableAdapt) { const widthEl = this.vm.$el.clientWidth; const { left, top } = getScreenCoordsFromWorldCoords( this.getAdaptPosition(), this.qt.camera, this.qt.el ); this.vm.left = left; this.vm.top = top; if (!this.enableAdaptHorizontal) return false; const isOverRight = this.vm.isOverRight = widthEl + left >= window.innerWidth; isOverRight && (this.vm.left = left - widthEl); } } adaptPopupPositionIcv() { const isInCameraView = this.qt.helper.isInCameraView(this.getAdaptPosition(), this.qt.camera); this.vm && isInCameraView && this.adaptPopupPosition(); if (isInCameraView) { if (this.enableRecoveryPopup) { delete this.enableRecoveryPopup; this.openPopup(); } } else { if (this.vm) { this.enableRecoveryPopup = true; this.closePopup(); } } } // 置顶 topping() { const zIndex = getElementMaxZIndex(this.qt.el); this.vm && (this.vm.zIndex = zIndex); } matrixChange(isWindowResize = false, handle) { return super.matrixChange(isWindowResize, () => { handle && handle(); this.enableAdapt && this.adaptPopupPositionIcv(); }); } fadeOut(tweenOption) { this.vm && this.closePopup(); return super.fadeOut(tweenOption); } destroy(force = false) { this.vm && this.closePopup(); return super.destroy(force); } }