diff --git a/frontend/src/components/gis/map.cesium.ts b/frontend/src/components/gis/map.cesium.ts index 2b7079ca..84e29dd4 100644 --- a/frontend/src/components/gis/map.cesium.ts +++ b/frontend/src/components/gis/map.cesium.ts @@ -13,6 +13,7 @@ export class MapCesium implements MapInterface { private clickEventHandler: Cesium.ScreenSpaceEventHandler | null = null; private popupElement: HTMLElement | null = null; private hoveredEntityId: string | null = null; + private hoverRafId: number | null = null; // requestAnimationFrame 节流 private removePostRenderListener: (() => void) | null = null; private isBatchPopupMode = false; private batchPopupContainer: HTMLDivElement | null = null; @@ -266,53 +267,65 @@ export class MapCesium implements MapInterface { // 默认箭头光标,与 2D 一致(不显示拖拽小手) canvas.style.cursor = 'default'; - // 鼠标移动:光标样式 + hover popup(批量模式下只改光标) + // 鼠标移动:光标样式 + hover popup(rAF 节流,批量模式下只改光标) + let pendingMoveEvent: { endPosition: Cesium.Cartesian2 } | null = null; + this.clickEventHandler.setInputAction( (move: { endPosition: Cesium.Cartesian2 }) => { - const scene = this.viewer?.scene; - if (!scene) return; + pendingMoveEvent = move; - const cameraHeight = this.getCurrentCameraHeight(); - // 缩放门槛:中国视角以上不触发 hover - if (cameraHeight > this.HOVER_POPUP_MAX_HEIGHT) { - canvas.style.cursor = 'default'; - this.hidePopup(); - return; - } + if (this.hoverRafId !== null) return; // 已有待处理的帧,跳过 + this.hoverRafId = requestAnimationFrame(() => { + this.hoverRafId = null; + const evt = pendingMoveEvent; + pendingMoveEvent = null; + if (!evt) return; - const picked = scene.pick(move.endPosition); - const entity = - Cesium.defined(picked) && picked.id instanceof Cesium.Entity - ? (picked.id as Cesium.Entity) - : null; + const scene = this.viewer?.scene; + if (!scene) return; - // 检测是否命中可交互的锚点 - 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); - } + const cameraHeight = this.getCurrentCameraHeight(); + // 缩放门槛:中国视角以上不触发 hover + if (cameraHeight > this.HOVER_POPUP_MAX_HEIGHT) { + canvas.style.cursor = 'default'; + this.hidePopup(); return; } - this.showPopupForEntity(entity); - return; - } + const picked = scene.pick(evt.endPosition); + const entity = + Cesium.defined(picked) && picked.id instanceof Cesium.Entity + ? (picked.id as Cesium.Entity) + : null; - canvas.style.cursor = 'default'; - // 批量模式下不隐藏批量 popup - if (!this.isBatchPopupMode) { - this.hidePopup(); - } + // 检测是否命中可交互的锚点 + 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 ); @@ -337,7 +350,7 @@ export class MapCesium implements MapInterface { } else { modelStore.modalVisible = true; modelStore.params = rawData; - modelStore.title = (rawData.titleName || rawData.stnm) + ' 详情信息'; + modelStore.title = rawData.titleName || rawData.stnm; } }, Cesium.ScreenSpaceEventType.LEFT_CLICK @@ -614,7 +627,8 @@ export class MapCesium implements MapInterface { const { entity, x, y, popupHtml } = candidates[i]; const popupEl = document.createElement('div'); - popupEl.className = 'map-popup-container'; + popupEl.className = + this.popupElement?.className || 'map-popup-container'; popupEl.innerHTML = popupHtml; popupEl.style.position = 'absolute'; popupEl.style.display = 'block'; @@ -1077,10 +1091,19 @@ export class MapCesium implements MapInterface { initPopupOverlay(popupContainer: HTMLDivElement): void { this.popupElement = popupContainer; if (this.popupElement) { + // 清除可能残留的旧样式 + this.popupElement.style.removeProperty('position'); + this.popupElement.style.removeProperty('transform'); + this.popupElement.style.removeProperty('left'); + this.popupElement.style.removeProperty('top'); this.popupElement.style.display = 'none'; this.popupElement.style.position = 'absolute'; this.popupElement.style.transform = 'translate(-50%, calc(-100% - 10px))'; - this.popupElement.style.pointerEvents = 'none'; + this.popupElement.style.setProperty( + 'pointer-events', + 'none', + 'important' + ); } }