import { ref, onUnmounted } from 'vue' import * as Cesium from 'cesium' import { use3DTiles } from './use3DTiles' /** * 双地图对比模式 * 使用两个独立的Cesium Viewer实现并排对比 * 左侧显示灾前场景,右侧显示灾后场景 */ export function useDualMapCompare() { /** 左侧Viewer引用 */ const leftViewer = ref(null) /** 右侧Viewer引用 */ const rightViewer = ref(null) /** 对比模式是否激活 */ const isCompareMode = ref(false) /** 相机同步监听器移除函数 */ let cameraSyncRemover = null /** 左侧3D Tileset(灾前) */ let leftTileset = null /** 右侧3D Tileset(灾后,主地图的tileset) */ let rightTileset = null const { load3DTileset } = use3DTiles() /** * 初始化左侧Viewer(灾前场景) * @param {HTMLElement} container - 容器元素 * @returns {Cesium.Viewer} */ const initLeftViewer = (container) => { if (!container) { console.error('[useDualMapCompare] 左侧容器元素不存在') return null } // 创建左侧viewer const viewer = new Cesium.Viewer(container, { animation: false, baseLayerPicker: false, fullscreenButton: false, geocoder: false, homeButton: false, infoBox: false, sceneModePicker: false, selectionIndicator: false, timeline: false, navigationHelpButton: false, scene3DOnly: true, shouldAnimate: false, }) // 移除默认的Cesium logo和版权信息 viewer.cesiumWidget.creditContainer.style.display = 'none' leftViewer.value = viewer console.log('[useDualMapCompare] 左侧Viewer初始化成功') return viewer } /** * 设置相机同步 * 右侧相机移动时,左侧相机跟随 * @param {Cesium.Viewer} rightViewerInstance - 右侧Viewer(主地图) * @param {Cesium.Viewer} leftViewerInstance - 左侧Viewer(对比地图) */ const setupCameraSync = (rightViewerInstance, leftViewerInstance) => { if (!rightViewerInstance || !leftViewerInstance) { console.warn('[useDualMapCompare] Viewer未初始化,无法设置相机同步') return } console.log('[useDualMapCompare] 设置相机同步(右侧主地图 → 左侧对比地图)...') // 监听右侧相机变化 const syncCamera = () => { if (!leftViewerInstance || leftViewerInstance.isDestroyed()) return const rightCamera = rightViewerInstance.camera const leftCamera = leftViewerInstance.camera // 同步位置和方向 leftCamera.setView({ destination: rightCamera.position.clone(), orientation: { heading: rightCamera.heading, pitch: rightCamera.pitch, roll: rightCamera.roll } }) } // 添加监听器 rightViewerInstance.camera.changed.addEventListener(syncCamera) // 保存移除函数 cameraSyncRemover = () => { rightViewerInstance.camera.changed.removeEventListener(syncCamera) console.log('[useDualMapCompare] 相机同步已移除') } console.log('[useDualMapCompare] 相机同步设置完成') } /** * 启用对比模式 * @param {Cesium.Viewer} rightViewerInstance - 右侧Viewer实例(主地图,灾后场景) */ const enableCompareMode = async (rightViewerInstance) => { if (!rightViewerInstance) { console.error('[useDualMapCompare] 右侧主地图Viewer未初始化') return } console.log('[useDualMapCompare] 启用对比模式...') rightViewer.value = rightViewerInstance // 查找左侧容器(容器已存在于DOM中) const leftContainer = document.getElementById('leftCesiumContainer') if (!leftContainer) { console.error('[useDualMapCompare] 找不到左侧容器元素') return } // 先设置状态,触发CSS动画 isCompareMode.value = true // 等待一小段时间让CSS过渡开始 await new Promise(resolve => setTimeout(resolve, 50)) // 初始化左侧Viewer const leftViewerInstance = initLeftViewer(leftContainer) if (!leftViewerInstance) { console.error('[useDualMapCompare] 左侧Viewer初始化失败') isCompareMode.value = false return } // 立即同步右侧相机的当前位置到左侧 console.log('[useDualMapCompare] 同步初始相机位置...') const rightCamera = rightViewerInstance.camera leftViewerInstance.camera.setView({ destination: rightCamera.position.clone(), orientation: { heading: rightCamera.heading, pitch: rightCamera.pitch, roll: rightCamera.roll } }) // 设置相机同步(右侧主地图 → 左侧对比地图) setupCameraSync(rightViewerInstance, leftViewerInstance) // 异步加载灾前模型到左侧(不阻塞对比模式启用) // 让模型在后台加载,用户可以立即看到对比效果 console.log('[useDualMapCompare] 开始异步加载左侧灾前模型...') load3DTileset(leftViewerInstance, 'before', false) .then(tileset => { if (tileset) { leftTileset = tileset console.log('[useDualMapCompare] 左侧灾前模型加载完成') } }) .catch(error => { console.error('[useDualMapCompare] 左侧模型加载失败:', error) }) // 右侧保持灾后模型(已加载) // 触发左侧viewer resize setTimeout(() => { if (leftViewerInstance && leftViewerInstance.canvas) { leftViewerInstance.resize() leftViewerInstance.camera.changed.raiseEvent() } // 同时触发右侧viewer resize if (rightViewerInstance && rightViewerInstance.canvas) { rightViewerInstance.resize() } }, 350) console.log('[useDualMapCompare] 对比模式已启用') } /** * 禁用对比模式 */ const disableCompareMode = () => { console.log('[useDualMapCompare] 禁用对比模式...') // 移除相机同步 if (cameraSyncRemover) { cameraSyncRemover() cameraSyncRemover = null } // 销毁左侧Viewer if (leftViewer.value && !leftViewer.value.isDestroyed()) { // 清理左侧tileset if (leftTileset) { leftViewer.value.scene.primitives.remove(leftTileset) leftTileset = null } leftViewer.value.destroy() leftViewer.value = null console.log('[useDualMapCompare] 左侧Viewer已销毁') } // 触发右侧viewer resize恢复全屏 if (rightViewer.value && !rightViewer.value.isDestroyed()) { setTimeout(() => { if (rightViewer.value && rightViewer.value.canvas) { rightViewer.value.resize() } }, 350) } isCompareMode.value = false console.log('[useDualMapCompare] 对比模式已禁用') } /** * 切换对比模式 * @param {boolean} active - true启用,false禁用 * @param {Cesium.Viewer} rightViewerInstance - 右侧Viewer实例(主地图) */ const toggleCompareMode = async (active, rightViewerInstance) => { if (active) { await enableCompareMode(rightViewerInstance) } else { disableCompareMode() } } // 清理 onUnmounted(() => { disableCompareMode() }) return { leftViewer, rightViewer, isCompareMode, enableCompareMode, disableCompareMode, toggleCompareMode } } export default useDualMapCompare