添加3dpopup显示
This commit is contained in:
parent
3dd9270911
commit
72d91355bb
@ -2,12 +2,25 @@
|
|||||||
import type { MDOptions } from './map.class';
|
import type { MDOptions } from './map.class';
|
||||||
import type { MapInterface, layer } from './map.d';
|
import type { MapInterface, layer } from './map.d';
|
||||||
import { getIconPath } from '@/utils/index';
|
import { getIconPath } from '@/utils/index';
|
||||||
|
import {
|
||||||
|
generatePopupHtml,
|
||||||
|
shouldPreferEng2Popup
|
||||||
|
} from '@/utils/popupHtmlGenerator';
|
||||||
import { useModelStore } from '@/store/modules/model';
|
import { useModelStore } from '@/store/modules/model';
|
||||||
|
|
||||||
export class MapCesium implements MapInterface {
|
export class MapCesium implements MapInterface {
|
||||||
private viewer: Cesium.Viewer | null = null;
|
private viewer: Cesium.Viewer | null = null;
|
||||||
private clickEventHandler: Cesium.ScreenSpaceEventHandler | null = null;
|
private clickEventHandler: Cesium.ScreenSpaceEventHandler | null = null;
|
||||||
private popupElement: HTMLElement | null = null;
|
private popupElement: HTMLElement | null = null;
|
||||||
|
private hoveredEntityId: string | null = null;
|
||||||
|
private removePostRenderListener: (() => void) | null = null;
|
||||||
|
private isBatchPopupMode = false;
|
||||||
|
private batchPopupContainer: HTMLDivElement | null = null;
|
||||||
|
private batchPopupItems: Array<{
|
||||||
|
entity: Cesium.Entity;
|
||||||
|
element: HTMLDivElement;
|
||||||
|
}> = [];
|
||||||
|
private batchPostRenderRemover: (() => void) | null = null;
|
||||||
private containerId = '';
|
private containerId = '';
|
||||||
private containerElement: HTMLElement | null = null;
|
private containerElement: HTMLElement | null = null;
|
||||||
private loadingOverlayElement: HTMLDivElement | null = null;
|
private loadingOverlayElement: HTMLDivElement | null = null;
|
||||||
@ -48,6 +61,8 @@ export class MapCesium implements MapInterface {
|
|||||||
private readonly CESIUM_ZOOM_FORMULA_B = 0.00007096758;
|
private readonly CESIUM_ZOOM_FORMULA_B = 0.00007096758;
|
||||||
private readonly CESIUM_ZOOM_FORMULA_C = 91610.74;
|
private readonly CESIUM_ZOOM_FORMULA_C = 91610.74;
|
||||||
private readonly CESIUM_ZOOM_FORMULA_D = -40467.74;
|
private readonly CESIUM_ZOOM_FORMULA_D = -40467.74;
|
||||||
|
private readonly HOVER_POPUP_MAX_HEIGHT = 9_000_000; // ~zoom 4.5,中国视角以上不触发 hover
|
||||||
|
private readonly BATCH_POPUP_MODE_ZOOM = 15; // zoom >= 15 进入批量固定模式
|
||||||
|
|
||||||
// 图标缩放可调参数(只影响 3D Cesium,不参与 2D 计算)
|
// 图标缩放可调参数(只影响 3D Cesium,不参与 2D 计算)
|
||||||
// scale = base + (zoom - 4.5) * rate,钳位到 [min, max]
|
// scale = base + (zoom - 4.5) * rate,钳位到 [min, max]
|
||||||
@ -115,6 +130,7 @@ export class MapCesium implements MapInterface {
|
|||||||
await this.flyToChina();
|
await this.flyToChina();
|
||||||
|
|
||||||
this.setupClickHandler();
|
this.setupClickHandler();
|
||||||
|
this.setupCameraPopupReset();
|
||||||
|
|
||||||
return this.viewer;
|
return this.viewer;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -250,23 +266,58 @@ export class MapCesium implements MapInterface {
|
|||||||
// 默认箭头光标,与 2D 一致(不显示拖拽小手)
|
// 默认箭头光标,与 2D 一致(不显示拖拽小手)
|
||||||
canvas.style.cursor = 'default';
|
canvas.style.cursor = 'default';
|
||||||
|
|
||||||
// 悬停锚点时显示 pointer
|
// 鼠标移动:光标样式 + hover popup(批量模式下只改光标)
|
||||||
this.clickEventHandler.setInputAction(
|
this.clickEventHandler.setInputAction(
|
||||||
(move: { endPosition: Cesium.Cartesian2 }) => {
|
(move: { endPosition: Cesium.Cartesian2 }) => {
|
||||||
const scene = this.viewer?.scene;
|
const scene = this.viewer?.scene;
|
||||||
if (!scene) return;
|
if (!scene) return;
|
||||||
|
|
||||||
|
const cameraHeight = this.getCurrentCameraHeight();
|
||||||
|
// 缩放门槛:中国视角以上不触发 hover
|
||||||
|
if (cameraHeight > this.HOVER_POPUP_MAX_HEIGHT) {
|
||||||
|
canvas.style.cursor = 'default';
|
||||||
|
this.hidePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const picked = scene.pick(move.endPosition);
|
const picked = scene.pick(move.endPosition);
|
||||||
canvas.style.cursor =
|
const entity =
|
||||||
Cesium.defined(picked) &&
|
Cesium.defined(picked) && picked.id instanceof Cesium.Entity
|
||||||
picked.id &&
|
? (picked.id as Cesium.Entity)
|
||||||
(picked.id as any).properties?._layerKey
|
: null;
|
||||||
? 'pointer'
|
|
||||||
: 'default';
|
// 检测是否命中可交互的锚点
|
||||||
|
if (entity && this.isEntityInteractive(entity)) {
|
||||||
|
canvas.style.cursor = 'pointer';
|
||||||
|
|
||||||
|
// 批量模式下单点 hover 不触发 popup
|
||||||
|
if (this.isBatchPopupMode) return;
|
||||||
|
|
||||||
|
// 同一个实体不重复刷新
|
||||||
|
if (this.hoveredEntityId === entity.id) {
|
||||||
|
const position = entity.position?.getValue(
|
||||||
|
this.viewer!.clock.currentTime
|
||||||
|
);
|
||||||
|
if (position) {
|
||||||
|
this.updatePopupPosition(position);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showPopupForEntity(entity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.style.cursor = 'default';
|
||||||
|
// 批量模式下不隐藏批量 popup
|
||||||
|
if (!this.isBatchPopupMode) {
|
||||||
|
this.hidePopup();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Cesium.ScreenSpaceEventType.MOUSE_MOVE
|
Cesium.ScreenSpaceEventType.MOUSE_MOVE
|
||||||
);
|
);
|
||||||
|
|
||||||
// 点击:先强制渲染一帧确保 scene.pick 不阻塞,然后打开详情弹框
|
// 点击:打开详情弹框
|
||||||
this.clickEventHandler.setInputAction(
|
this.clickEventHandler.setInputAction(
|
||||||
(click: { position: Cesium.Cartesian2 }) => {
|
(click: { position: Cesium.Cartesian2 }) => {
|
||||||
const scene = this.viewer?.scene;
|
const scene = this.viewer?.scene;
|
||||||
@ -276,16 +327,10 @@ export class MapCesium implements MapInterface {
|
|||||||
if (!Cesium.defined(picked) || !picked.id) return;
|
if (!Cesium.defined(picked) || !picked.id) return;
|
||||||
|
|
||||||
const entity = picked.id as Cesium.Entity;
|
const entity = picked.id as Cesium.Entity;
|
||||||
const bag = (entity as any).properties;
|
if (!this.isEntityInteractive(entity)) return;
|
||||||
if (!bag?._layerKey) return;
|
|
||||||
|
|
||||||
// 直接读取原始数据引用,避免 Cesium Property getValue 遍历开销
|
|
||||||
const rawData: Record<string, any> = (entity as any)._rawData || {};
|
const rawData: Record<string, any> = (entity as any)._rawData || {};
|
||||||
|
|
||||||
if (this.popupElement) {
|
|
||||||
this.popupElement.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawData.sttpMap === 'ylfb') {
|
if (rawData.sttpMap === 'ylfb') {
|
||||||
modelStore.ylfbModalVisible = true;
|
modelStore.ylfbModalVisible = true;
|
||||||
modelStore.params = rawData;
|
modelStore.params = rawData;
|
||||||
@ -299,9 +344,420 @@ export class MapCesium implements MapInterface {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== Step 5: hover popup + 批量 popup ====================
|
||||||
|
|
||||||
|
/** 监听相机移动结束:检测缩放阈值切换批量模式 */
|
||||||
|
private setupCameraPopupReset() {
|
||||||
|
if (!this.viewer) return;
|
||||||
|
|
||||||
|
if (this.removePostRenderListener) {
|
||||||
|
this.removePostRenderListener();
|
||||||
|
this.removePostRenderListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.removePostRenderListener = this.viewer.camera.moveEnd.addEventListener(
|
||||||
|
() => {
|
||||||
|
const zoom = this.getZoomLevelFromCameraHeight(
|
||||||
|
this.getCurrentCameraHeight()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (zoom >= this.BATCH_POPUP_MODE_ZOOM) {
|
||||||
|
if (!this.isBatchPopupMode) {
|
||||||
|
this.enableBatchPopupMode();
|
||||||
|
}
|
||||||
|
// 已处于批量模式时不做重建(postRender 已持续更新位置),
|
||||||
|
// 避免 clear+rebuild 产生空白帧闪烁
|
||||||
|
} else {
|
||||||
|
if (this.isBatchPopupMode) {
|
||||||
|
this.disableBatchPopupMode();
|
||||||
|
}
|
||||||
|
// hover popup 重定位而非隐藏,避免缩放结束后闪烁
|
||||||
|
if (this.hoveredEntityId) {
|
||||||
|
this.repositionHoverPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据 hoveredEntityId 重定位 popup(相机移动后调用,不重建内容) */
|
||||||
|
private repositionHoverPopup(): void {
|
||||||
|
if (!this.viewer || !this.popupElement || !this.hoveredEntityId) return;
|
||||||
|
const entity = this.viewer.entities.getById(this.hoveredEntityId);
|
||||||
|
if (!entity || !entity.position) {
|
||||||
|
this.hidePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const position = entity.position.getValue(this.viewer.clock.currentTime);
|
||||||
|
if (!position) {
|
||||||
|
this.hidePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.updatePopupPosition(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量模式下每帧更新 popup 位置,实现流畅跟随拖拽 */
|
||||||
|
private setupBatchPopupPostRenderSync() {
|
||||||
|
if (!this.viewer || this.batchPostRenderRemover) return;
|
||||||
|
this.batchPostRenderRemover = this.viewer.scene.postRender.addEventListener(
|
||||||
|
() => {
|
||||||
|
if (!this.isBatchPopupMode) return;
|
||||||
|
this.updateBatchPopupPositions();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 停止每帧同步 */
|
||||||
|
private clearBatchPopupPostRenderSync() {
|
||||||
|
if (this.batchPostRenderRemover) {
|
||||||
|
this.batchPostRenderRemover();
|
||||||
|
this.batchPostRenderRemover = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取当前相机高度(米) */
|
||||||
|
private getCurrentCameraHeight(): number {
|
||||||
|
if (!this.viewer) return Number.POSITIVE_INFINITY;
|
||||||
|
const height = this.viewer.camera.positionCartographic.height;
|
||||||
|
if (!height || !isFinite(height) || height <= 0) {
|
||||||
|
return Number.POSITIVE_INFINITY;
|
||||||
|
}
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 将相机高度映射为近似 2D zoom 级别(与 getCurrentCesiumZoom 同公式) */
|
||||||
|
private getZoomLevelFromCameraHeight(height: number): number {
|
||||||
|
const zoom =
|
||||||
|
this.CESIUM_ZOOM_FORMULA_D +
|
||||||
|
(this.CESIUM_ZOOM_FORMULA_A - this.CESIUM_ZOOM_FORMULA_D) /
|
||||||
|
(1 +
|
||||||
|
Math.pow(
|
||||||
|
height / this.CESIUM_ZOOM_FORMULA_C,
|
||||||
|
this.CESIUM_ZOOM_FORMULA_B
|
||||||
|
)) +
|
||||||
|
1;
|
||||||
|
return zoom > -1 ? zoom : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 判断实体当前是否可交互(图层可见 + 图例可见 + 区域可见) */
|
||||||
|
private isEntityInteractive(entity: Cesium.Entity): boolean {
|
||||||
|
const e = entity as any;
|
||||||
|
if (e._layerVisible === false) return false;
|
||||||
|
if (e._legendVisible === false) return false;
|
||||||
|
if (e._regionVisible === false) return false;
|
||||||
|
if (entity.show === false) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据实体属性生成 popup HTML,复用 generatePopupHtml */
|
||||||
|
private getPopupHtml(rawData: Record<string, any>): string {
|
||||||
|
return rawData.popupHtml || generatePopupHtml(rawData) || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 关闭单个 hover popup */
|
||||||
|
private hidePopup(): void {
|
||||||
|
this.hoveredEntityId = null;
|
||||||
|
if (!this.popupElement) return;
|
||||||
|
this.popupElement.style.display = 'none';
|
||||||
|
this.popupElement.innerHTML = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据实体世界坐标把 popup 定位到屏幕坐标 */
|
||||||
|
private updatePopupPosition(position: Cesium.Cartesian3): void {
|
||||||
|
if (!this.viewer || !this.popupElement) return;
|
||||||
|
|
||||||
|
const canvasPosition = Cesium.SceneTransforms.worldToWindowCoordinates(
|
||||||
|
this.viewer.scene,
|
||||||
|
position
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!canvasPosition) {
|
||||||
|
this.hidePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.popupElement.style.left = `${canvasPosition.x}px`;
|
||||||
|
this.popupElement.style.top = `${canvasPosition.y}px`;
|
||||||
|
this.popupElement.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 读取实体原始数据并渲染 hover popup */
|
||||||
|
private showPopupForEntity(entity: Cesium.Entity): void {
|
||||||
|
if (!this.viewer || !this.popupElement || !entity.position) return;
|
||||||
|
|
||||||
|
const rawData: Record<string, any> = {
|
||||||
|
...((entity as any)._rawData || {})
|
||||||
|
};
|
||||||
|
if (!rawData.sttpMap) {
|
||||||
|
rawData.sttpMap = rawData._sttpMap || rawData.popupSttpMap || '';
|
||||||
|
}
|
||||||
|
const popupHtml = this.getPopupHtml(rawData);
|
||||||
|
const position = entity.position.getValue(this.viewer.clock.currentTime);
|
||||||
|
|
||||||
|
if (!popupHtml || !position) {
|
||||||
|
this.hidePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.popupElement.innerHTML = popupHtml;
|
||||||
|
this.hoveredEntityId = entity.id;
|
||||||
|
this.updatePopupPosition(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 批量 popup ====================
|
||||||
|
|
||||||
|
/** 启用批量 popup:所有可见锚点固定显示 popup */
|
||||||
|
private enableBatchPopupMode() {
|
||||||
|
this.isBatchPopupMode = true;
|
||||||
|
this.clearBatchPopups();
|
||||||
|
this.showBatchPopups();
|
||||||
|
this.setupBatchPopupPostRenderSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 退出批量 popup */
|
||||||
|
private disableBatchPopupMode() {
|
||||||
|
this.isBatchPopupMode = false;
|
||||||
|
this.clearBatchPopupPostRenderSync();
|
||||||
|
this.clearBatchPopups();
|
||||||
|
this.hidePopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 遍历所有图层中交互可见且视口内的实体,分帧创建批量 popup(避免卡顿) */
|
||||||
|
private showBatchPopups() {
|
||||||
|
if (!this.viewer || !this.containerElement) return;
|
||||||
|
|
||||||
|
const batchContainer = document.createElement('div');
|
||||||
|
batchContainer.className = 'batch-popup-container';
|
||||||
|
batchContainer.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1000;
|
||||||
|
`;
|
||||||
|
batchContainer.id = 'batch-popup-container';
|
||||||
|
this.batchPopupContainer = batchContainer;
|
||||||
|
this.containerElement.appendChild(batchContainer);
|
||||||
|
|
||||||
|
const canvas = this.viewer.scene.canvas;
|
||||||
|
const viewW = canvas.clientWidth;
|
||||||
|
const viewH = canvas.clientHeight;
|
||||||
|
const padding = 60;
|
||||||
|
|
||||||
|
// 收集所有需要渲染的实体信息(含视口裁剪)
|
||||||
|
const candidates: Array<{
|
||||||
|
entity: Cesium.Entity;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
popupHtml: string;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
this.pointLayerRegistry.forEach(entities => {
|
||||||
|
entities.forEach(entity => {
|
||||||
|
if (!this.isEntityInteractive(entity)) return;
|
||||||
|
const position = entity.position?.getValue(
|
||||||
|
this.viewer!.clock.currentTime
|
||||||
|
);
|
||||||
|
if (!position) return;
|
||||||
|
|
||||||
|
const cp = Cesium.SceneTransforms.worldToWindowCoordinates(
|
||||||
|
this.viewer!.scene,
|
||||||
|
position
|
||||||
|
);
|
||||||
|
if (!cp) return;
|
||||||
|
|
||||||
|
// 视口裁剪:超出屏幕的直接跳过
|
||||||
|
if (
|
||||||
|
cp.x < -padding ||
|
||||||
|
cp.y < -padding ||
|
||||||
|
cp.x > viewW + padding ||
|
||||||
|
cp.y > viewH + padding
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const rawData: Record<string, any> = {
|
||||||
|
...((entity as any)._rawData || {})
|
||||||
|
};
|
||||||
|
if (!rawData.sttpMap) {
|
||||||
|
rawData.sttpMap = rawData._sttpMap || rawData.popupSttpMap || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量模式:eng 锚点优先用 eng2(详细数据),与 2D 对齐
|
||||||
|
const popupHtml = shouldPreferEng2Popup(rawData)
|
||||||
|
? generatePopupHtml(rawData, { forceEng2: true }) ||
|
||||||
|
rawData.popupHtml ||
|
||||||
|
generatePopupHtml(rawData)
|
||||||
|
: this.getPopupHtml(rawData);
|
||||||
|
if (!popupHtml) return;
|
||||||
|
|
||||||
|
candidates.push({ entity, x: cp.x, y: cp.y, popupHtml });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 分帧渲染,每帧最多 80 个,避免一次性 DOM 操作卡顿
|
||||||
|
let index = 0;
|
||||||
|
const CHUNK_SIZE = 80;
|
||||||
|
const placedPopups: Array<{
|
||||||
|
left: number;
|
||||||
|
right: number;
|
||||||
|
top: number;
|
||||||
|
bottom: number;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
const processChunk = () => {
|
||||||
|
if (!this.isBatchPopupMode || !this.batchPopupContainer) return;
|
||||||
|
|
||||||
|
const end = Math.min(index + CHUNK_SIZE, candidates.length);
|
||||||
|
for (let i = index; i < end; i++) {
|
||||||
|
const { entity, x, y, popupHtml } = candidates[i];
|
||||||
|
|
||||||
|
const popupEl = document.createElement('div');
|
||||||
|
popupEl.className = 'map-popup-container';
|
||||||
|
popupEl.innerHTML = popupHtml;
|
||||||
|
popupEl.style.position = 'absolute';
|
||||||
|
popupEl.style.display = 'block';
|
||||||
|
popupEl.style.transform = 'translate(-50%, calc(-100% - 10px))';
|
||||||
|
popupEl.style.pointerEvents = 'none';
|
||||||
|
|
||||||
|
// 先隐藏测量尺寸
|
||||||
|
popupEl.style.visibility = 'hidden';
|
||||||
|
popupEl.style.left = `${x}px`;
|
||||||
|
popupEl.style.top = `${y}px`;
|
||||||
|
this.batchPopupContainer!.appendChild(popupEl);
|
||||||
|
|
||||||
|
const rect = popupEl.getBoundingClientRect();
|
||||||
|
const pw = rect.width || 120;
|
||||||
|
const ph = rect.height || 40;
|
||||||
|
|
||||||
|
const popupRect = {
|
||||||
|
left: x - pw / 2,
|
||||||
|
right: x + pw / 2,
|
||||||
|
top: y - 10 - ph,
|
||||||
|
bottom: y - 10
|
||||||
|
};
|
||||||
|
|
||||||
|
let hasCollision = false;
|
||||||
|
for (const placed of placedPopups) {
|
||||||
|
if (this.checkPopupCollision(popupRect, placed)) {
|
||||||
|
hasCollision = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasCollision) {
|
||||||
|
popupEl.style.visibility = 'visible';
|
||||||
|
popupEl.style.left = `${x}px`;
|
||||||
|
popupEl.style.top = `${y}px`;
|
||||||
|
this.batchPopupItems.push({ entity, element: popupEl });
|
||||||
|
placedPopups.push(popupRect);
|
||||||
|
} else {
|
||||||
|
this.batchPopupContainer!.removeChild(popupEl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
index = end;
|
||||||
|
if (index < candidates.length && this.isBatchPopupMode) {
|
||||||
|
requestAnimationFrame(processChunk);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
requestAnimationFrame(processChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 每帧更新批量 popup 屏幕位置(不重建 DOM,拖拽时流畅跟随) */
|
||||||
|
private updateBatchPopupPositions() {
|
||||||
|
if (!this.viewer || !this.batchPopupContainer) return;
|
||||||
|
if (this.batchPopupItems.length === 0) return;
|
||||||
|
|
||||||
|
const canvas = this.viewer.scene.canvas;
|
||||||
|
const viewW = canvas.clientWidth;
|
||||||
|
const viewH = canvas.clientHeight;
|
||||||
|
const padding = 48;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.batchPopupItems.length; i++) {
|
||||||
|
const { entity, element } = this.batchPopupItems[i];
|
||||||
|
|
||||||
|
if (!this.isEntityInteractive(entity)) {
|
||||||
|
element.style.display = 'none';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const position = entity.position?.getValue(
|
||||||
|
this.viewer!.clock.currentTime
|
||||||
|
);
|
||||||
|
if (!position) {
|
||||||
|
element.style.display = 'none';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cp = Cesium.SceneTransforms.worldToWindowCoordinates(
|
||||||
|
this.viewer!.scene,
|
||||||
|
position
|
||||||
|
);
|
||||||
|
if (!cp) {
|
||||||
|
element.style.display = 'none';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
cp.x < -padding ||
|
||||||
|
cp.y < -padding ||
|
||||||
|
cp.x > viewW + padding ||
|
||||||
|
cp.y > viewH + padding
|
||||||
|
) {
|
||||||
|
element.style.display = 'none';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
element.style.left = `${cp.x}px`;
|
||||||
|
element.style.top = `${cp.y}px`;
|
||||||
|
element.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 清除所有批量 popup */
|
||||||
|
private clearBatchPopups() {
|
||||||
|
this.batchPopupItems = [];
|
||||||
|
if (this.batchPopupContainer) {
|
||||||
|
this.batchPopupContainer.remove();
|
||||||
|
this.batchPopupContainer = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const existing = document.getElementById('batch-popup-container');
|
||||||
|
if (existing) {
|
||||||
|
existing.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 简单碰撞检测(AABB + padding) */
|
||||||
|
private checkPopupCollision(
|
||||||
|
a: { left: number; right: number; top: number; bottom: number },
|
||||||
|
b: { left: number; right: number; top: number; bottom: number }
|
||||||
|
): boolean {
|
||||||
|
const padding = 4;
|
||||||
|
return !(
|
||||||
|
a.right + padding < b.left ||
|
||||||
|
a.left - padding > b.right ||
|
||||||
|
a.bottom + padding < b.top ||
|
||||||
|
a.top - padding > b.bottom
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== destroy ====================
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.clearFlightFallbackTimer();
|
this.clearFlightFallbackTimer();
|
||||||
this.hideLoadingOverlay();
|
this.hideLoadingOverlay();
|
||||||
|
this.isBatchPopupMode = false;
|
||||||
|
this.clearBatchPopupPostRenderSync();
|
||||||
|
this.clearBatchPopups();
|
||||||
|
this.hidePopup();
|
||||||
|
if (this.removePostRenderListener) {
|
||||||
|
this.removePostRenderListener();
|
||||||
|
this.removePostRenderListener = null;
|
||||||
|
}
|
||||||
this.destroyAllPointLayers();
|
this.destroyAllPointLayers();
|
||||||
this.pointLayerRegistry.clear();
|
this.pointLayerRegistry.clear();
|
||||||
this.baseLayerRegistry.clear();
|
this.baseLayerRegistry.clear();
|
||||||
@ -620,6 +1076,12 @@ export class MapCesium implements MapInterface {
|
|||||||
|
|
||||||
initPopupOverlay(popupContainer: HTMLDivElement): void {
|
initPopupOverlay(popupContainer: HTMLDivElement): void {
|
||||||
this.popupElement = popupContainer;
|
this.popupElement = popupContainer;
|
||||||
|
if (this.popupElement) {
|
||||||
|
this.popupElement.style.display = 'none';
|
||||||
|
this.popupElement.style.position = 'absolute';
|
||||||
|
this.popupElement.style.transform = 'translate(-50%, calc(-100% - 10px))';
|
||||||
|
this.popupElement.style.pointerEvents = 'none';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Step 3: 锚点展示 ====================
|
// ==================== Step 3: 锚点展示 ====================
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user