// [file name]: cesium_popup.ts export default (hnMap: any) => { class CesiumPopup { private viewer: any = null; private popupElement: HTMLElement | null = null; private popupContainer: HTMLElement | null = null; private currentEntity: any = null; private screenSpaceEventHandler: any = null; private isPopupVisible: boolean = false; private defaultStyle: any = {}; constructor(viewer: any, options: any = {}) { this.viewer = viewer; this.defaultStyle = { container: { position: "absolute", top: "0", left: "0", width: "100%", height: "100%", pointerEvents: "none", zIndex: "1000", display: "none", }, popup: { position: "absolute", // background: "white", // borderRadius: "4px", // boxShadow: "0 2px 10px rgba(0,0,0,0.2)", // padding: "12px", // fontSize: "14px", pointerEvents: "auto", zIndex: "1002", transform: "translate(-50%, -100%)", // fontFamily: "Arial, sans-serif", }, close: { position: "absolute", top: "5px", right: "5px", width: "20px", height: "20px", lineHeight: "20px", textAlign: "center", cursor: "pointer", fontSize: "16px", color: "#666", zIndex: "1002", }, }; // 合并自定义样式 if (options.style) { this.mergeStyles(this.defaultStyle, options.style); } this.initPopup(); } // 递归合并样式对象 private mergeStyles(defaultStyle: any, customStyle: any): void { for (const key in customStyle) { if ( defaultStyle.hasOwnProperty(key) && typeof defaultStyle[key] === "object" && !(defaultStyle[key] instanceof Array) ) { this.mergeStyles(defaultStyle[key], customStyle[key]); } else { defaultStyle[key] = customStyle[key]; } } } // 初始化弹窗容器 private initPopup(): void { // 创建弹窗容器 this.popupContainer = document.createElement("div"); this.popupContainer.className = "cesium-popup-container"; // 应用样式 this.applyStyles(this.popupContainer, this.defaultStyle.container); // 创建弹窗元素 this.popupElement = document.createElement("div"); this.popupElement.className = "cesium-popup"; // 应用样式 this.applyStyles(this.popupElement, this.defaultStyle.popup); // 添加关闭按钮 const closeButton = document.createElement("div"); closeButton.className = "cesium-popup-close"; closeButton.innerHTML = "×"; // 应用关闭按钮样式 this.applyStyles(closeButton, this.defaultStyle.close); closeButton.onclick = () => this.hide(); // // 添加标题区域 // const titleElement = document.createElement("div"); // titleElement.className = "cesium-popup-title"; // titleElement.style.cssText = ` // font-weight: bold; // margin-bottom: 8px; // color: #333; // border-bottom: 1px solid #eee; // padding-bottom: 4px; // `; // titleElement.textContent = "详细信息"; // 添加内容区域 const contentElement = document.createElement("div"); contentElement.className = "cesium-popup-content"; this.popupElement.appendChild(closeButton); // this.popupElement.appendChild(titleElement); this.popupElement.appendChild(contentElement); this.popupContainer.appendChild(this.popupElement); // 添加到页面 if (this.viewer && this.viewer.container) { this.viewer.container.appendChild(this.popupContainer); } } // 显示弹窗 public show( position: any, content: string | HTMLElement, title: string = "详细信息" ): void { if (!this.popupContainer || !this.popupElement) return; // 更新标题 const titleElement = this.popupElement.querySelector( ".cesium-popup-title" ); if (titleElement) { titleElement.textContent = title; } // 更新内容 const contentElement = this.popupElement.querySelector( ".cesium-popup-content" ); if (contentElement) { contentElement.innerHTML = ""; if (typeof content === "string") { contentElement.innerHTML = content; } else { contentElement.appendChild(content); } } // 计算屏幕位置 if (position.cartesian) { const canvasPosition = this.viewer.scene.cartesianToCanvasCoordinates(position.cartesian); if (canvasPosition) { this.popupElement.style.left = `${canvasPosition.x}px`; this.popupElement.style.top = `${canvasPosition.y}px`; } } else if (position.x !== undefined && position.y !== undefined) { this.popupElement.style.left = `${position.x}px`; this.popupElement.style.top = `${position.y}px`; } // 显示弹窗 this.popupContainer.style.display = "block"; this.isPopupVisible = true; // 添加点击外部关闭功能 setTimeout(() => { document.addEventListener("click", this.handleOutsideClick); }, 0); } // 隐藏弹窗 public hide(): void { if (this.popupContainer && this.popupContainer.parentNode) { // 移除点击外部关闭的监听器 document.removeEventListener("click", this.handleOutsideClick); // 从 DOM 中彻底移除容器 this.popupContainer.parentNode.removeChild(this.popupContainer); // 重置引用 this.popupContainer = null; this.popupElement = null; this.isPopupVisible = false; this.currentEntity = null; } } // 处理外部点击 private handleOutsideClick = (event: MouseEvent): void => { if ( this.popupElement && !this.popupElement.contains(event.target as Node) ) { this.hide(); } }; // 应用样式到元素 private applyStyles(element: HTMLElement, styles: any): void { for (const key in styles) { if (styles.hasOwnProperty(key)) { const cssKey = this.toKebabCase(key); element.style.setProperty(cssKey, styles[key]); } } } // 将驼峰命名转换为短横线命名 private toKebabCase(str: string): string { return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`); } // 销毁弹窗 public destroy(): void { this.hide(); if (this.popupContainer && this.popupContainer.parentNode) { this.popupContainer.parentNode.removeChild(this.popupContainer); } if (this.screenSpaceEventHandler) { this.screenSpaceEventHandler.destroy(); } } } return CesiumPopup; };