From dc4c88acb52adf084e8dd6abdd06c5f7626025e3 Mon Sep 17 00:00:00 2001 From: Zzc <1373857752@qq.com> Date: Thu, 27 Nov 2025 15:47:42 +0800 Subject: [PATCH 1/3] =?UTF-8?q?refactor(3d-situational-awareness):=20**?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=8A=A8=E7=94=BB=E8=A1=8C=E4=B8=BA=E8=87=B3?= =?UTF-8?q?=E6=9C=AB=E7=AB=AF=E5=81=9C=E6=AD=A2=E5=B9=B6=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E9=80=9F=E5=BA=A6**?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将计时范围从 `LOOP_STOP` 更改为 `CLAMPED`,以防止动画循环。 同时将设备实体的速度乘数提高 1.5 倍,以实现更快的移动。 添加动画持续时间详情的日志记录。 --- .../composables/useEntityAnimation.js | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useEntityAnimation.js b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useEntityAnimation.js index 802b1b9..235c6b7 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useEntityAnimation.js +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useEntityAnimation.js @@ -120,7 +120,7 @@ export function useEntityAnimation() { viewer.clock.startTime = startTime.clone() viewer.clock.stopTime = stopTime.clone() viewer.clock.currentTime = startTime.clone() - viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP // 动画结束后停止 + viewer.clock.clockRange = Cesium.ClockRange.CLAMPED // 动画到达终点后停止,不循环 viewer.clock.multiplier = 1 // 实时速度 viewer.clock.shouldAnimate = true @@ -385,7 +385,7 @@ export function useEntityAnimation() { viewer.clock.startTime = startTime.clone() viewer.clock.stopTime = stopTime.clone() viewer.clock.currentTime = startTime.clone() - viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP + viewer.clock.clockRange = Cesium.ClockRange.CLAMPED // 动画到达终点后停止,不循环 viewer.clock.multiplier = 1 viewer.clock.shouldAnimate = true @@ -470,7 +470,12 @@ export function useEntityAnimation() { }) // 计算动画时长(使用速度倍数) - const speedMultiplier = options.speedMultiplier || 10 + // 装备移动速度更快(speedMultiplier 更大 = 动画时间更短 = 移动更快) + const baseSpeedMultiplier = options.speedMultiplier || 10 + const speedMultiplier = route.type === 'equipment' + ? baseSpeedMultiplier * 1.5 // 装备速度是人员的1.5倍 + : baseSpeedMultiplier // 人员保持原速 + const animationDuration = Math.min( Math.max(route.duration / speedMultiplier, 30), // 最小30秒 120 // 最大120秒 @@ -510,6 +515,14 @@ export function useEntityAnimation() { const icon = route.type === 'equipment' ? deviceIcon : soldierIcon const trailColor = route.type === 'equipment' ? Cesium.Color.ORANGE : Cesium.Color.CYAN + // 日志输出:显示每个实体的动画时长 + console.log( + `[useEntityAnimation] ${route.name} (${route.type}): ` + + `原始时长=${Math.round(route.duration)}秒, ` + + `速度倍数=${speedMultiplier.toFixed(1)}, ` + + `动画时长=${Math.round(animationDuration)}秒` + ) + // 创建动画实体 return viewer.entities.add({ availability: new Cesium.TimeIntervalCollection([ @@ -584,7 +597,7 @@ export function useEntityAnimation() { viewer.clock.startTime = startTime.clone() viewer.clock.stopTime = stopTime.clone() viewer.clock.currentTime = startTime.clone() - viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP + viewer.clock.clockRange = Cesium.ClockRange.CLAMPED // 动画到达终点后停止,不循环 viewer.clock.multiplier = 1 viewer.clock.shouldAnimate = true From bb5bed014cd86fd1af3bc1072e7bf44a1ec59d35 Mon Sep 17 00:00:00 2001 From: Zzc <1373857752@qq.com> Date: Thu, 27 Nov 2025 17:12:32 +0800 Subject: [PATCH 2/3] =?UTF-8?q?refactor(3d-situational-awareness):=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=88=87=E7=89=87=E9=9B=86=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=EF=BC=8C=E5=BC=95=E5=85=A5=E6=B8=85=E7=90=86?= =?UTF-8?q?=E6=9C=BA=E5=88=B6=E4=B8=8E=E6=8F=90=E5=89=8D=E9=80=80=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增统一的清理函数,用于处理超时及移除事件监听器。 针对已加载切片引入提前退出检查,从而避免冗余等待。 强化错误处理机制,用于区分超时错误,并确保在 `finally` 块中执行清理操作。 --- .../composables/use3DTiles.js | 58 +++++++++++++------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/use3DTiles.js b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/use3DTiles.js index 4e40916..3e26c6a 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/use3DTiles.js +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/use3DTiles.js @@ -87,46 +87,66 @@ export function use3DTiles() { return } + let timeoutId = null + let eventHandler = null + + // 统一清理函数 + const cleanup = () => { + if (timeoutId) { + clearTimeout(timeoutId) + timeoutId = null + } + if (eventHandler) { + tileset.initialTilesLoaded.removeEventListener(eventHandler) + eventHandler = null + } + } + try { // 步骤1:等待 Tileset 基础就绪 await tileset.readyPromise console.log('[use3DTiles] Tileset readyPromise 已完成') - // 步骤2:等待初始瓦片加载完成(带超时) + // 步骤2:早期出口 - 快速检查 + if (tileset.tilesLoaded) { + console.log('[use3DTiles] 瓦片已加载,直接继续') + return + } + + // 步骤3:等待初始瓦片加载完成(带超时) console.log('[use3DTiles] 等待初始瓦片加载...') await Promise.race([ - // 等待initialTilesLoaded事件 + // 等待 initialTilesLoaded 事件 new Promise((resolve) => { - const handleInitialTilesLoaded = () => { + eventHandler = () => { console.log('[use3DTiles] 初始瓦片加载完成(事件触发)') - tileset.initialTilesLoaded.removeEventListener(handleInitialTilesLoaded) - resolve() - } - - tileset.initialTilesLoaded.addEventListener(handleInitialTilesLoaded) - - // 如果已经加载完成,可能事件已经触发过了,直接resolve - // 通过检查tileset.tilesLoaded来判断 - if (tileset.tilesLoaded) { - console.log('[use3DTiles] 瓦片已加载,直接继续') - tileset.initialTilesLoaded.removeEventListener(handleInitialTilesLoaded) + cleanup() resolve() } + tileset.initialTilesLoaded.addEventListener(eventHandler) }), + // 超时机制 - new Promise((resolve) => { - setTimeout(() => { - console.warn(`[use3DTiles] 等待瓦片加载超时(${timeout}ms),继续执行`) - resolve() + new Promise((_, reject) => { + timeoutId = setTimeout(() => { + cleanup() + reject(new Error(`等待瓦片加载超时(${timeout}ms)`)) }, timeout) }) ]) console.log('[use3DTiles] Tileset 已完全就绪') } catch (error) { - console.error('[use3DTiles] 等待 Tileset 就绪失败:', error) + if (error.message && error.message.includes('超时')) { + // 超时但继续执行(保持原有行为) + console.warn(`[use3DTiles] ${error.message},继续执行`) + } else { + console.error('[use3DTiles] 等待 Tileset 就绪失败:', error) + } // 即使失败也不抛出异常,允许程序继续执行 + } finally { + cleanup() // 保险清理 } } From 49e963e196af76f2510b70c4deaa1209f9dea143 Mon Sep 17 00:00:00 2001 From: Zzc <1373857752@qq.com> Date: Thu, 27 Nov 2025 18:04:58 +0800 Subject: [PATCH 3/3] =?UTF-8?q?refactor(3d-situational-awareness):=20?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E5=8F=8C=E5=9C=B0=E5=9B=BE=E7=9B=B8=E6=9C=BA?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=8A=9F=E8=83=BD=E5=B9=B6=E5=8A=A0=E5=85=A5?= =?UTF-8?q?=E9=98=B2=E5=BE=A1=E6=80=A7=E6=A3=80=E6=9F=A5=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 引入视窗(viewer)存在性和销毁检查,以防止同步过程中发生错误。 * 引入原子停止标志(atomic stop flags)和移动结束(moveEnd)追踪,以优化控制流程。 * 提高位置变化检测的精度,并优化日志记录功能。 --- .../composables/useDualMapCompare.js | 171 ++++++++++++++---- 1 file changed, 134 insertions(+), 37 deletions(-) diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useDualMapCompare.js b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useDualMapCompare.js index 4b0c6ce..1676603 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useDualMapCompare.js +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useDualMapCompare.js @@ -102,10 +102,32 @@ export function useDualMapCompare() { let isSyncing = false const handleCameraSync = () => { if (isSyncing) return + + // 防御检查1:viewer 存在性检查 + if (!rightViewerInstance || !leftViewerInstance) { + return + } + + // 防御检查2:viewer destroyed 状态检查 + if (rightViewerInstance.isDestroyed?.() || leftViewerInstance.isDestroyed?.()) { + return + } + isSyncing = true requestAnimationFrame(() => { try { + // 再次检查(requestAnimationFrame 可能延迟执行) + if (!rightViewerInstance || !leftViewerInstance) { + isSyncing = false + return + } + + if (rightViewerInstance.isDestroyed?.() || leftViewerInstance.isDestroyed?.()) { + isSyncing = false + return + } + const rightCamera = rightViewerInstance.camera leftViewerInstance.camera.setView({ destination: rightCamera.position.clone(), @@ -129,8 +151,13 @@ export function useDualMapCompare() { _moveEndHandler: null, _lastPosition: null, _interval: null, + _stopped: false, // 新增:原子性停止标志 + _moveEndReceived: false, // 新增:标记 moveEnd 是否已触发 start: () => { + // 重置停止标志 + syncController._stopped = false + const controller = rightViewerInstance.scene.screenSpaceCameraController if (!controller) { @@ -142,13 +169,27 @@ export function useDualMapCompare() { if (typeof rightViewerInstance.camera.moveStart?.addEventListener === 'function' && typeof rightViewerInstance.camera.moveEnd?.addEventListener === 'function') { - // 监听相机移动开始(替代所有操作类型监听) + // 监听相机移动开始 const moveStartHandler = rightViewerInstance.camera.moveStart.addEventListener(() => { + if (syncController._stopped) return // 检查停止标志 + syncController._isUserInteracting = true console.log('[useDualMapCompare] 捕获相机操作开始') - // 新增拖动持续监听 + // 改进:只在首次触发时启动递归 const onDragFrame = () => { + // 停止检查 + if (syncController._stopped) { + console.log('[useDualMapCompare] 停止标志已设置,退出递归') + return + } + + // viewer 有效性检查 + if (!rightViewerInstance || rightViewerInstance.isDestroyed?.()) { + console.log('[useDualMapCompare] Viewer已销毁,退出递归') + return + } + if (syncController._isUserInteracting) { console.log('[useDualMapCompare] 主同步触发') handleCameraSync() @@ -158,12 +199,17 @@ export function useDualMapCompare() { requestAnimationFrame(onDragFrame) }) - // 2. 同步逻辑 + syncController._interactionHandlers.push(moveStartHandler) + + // 2. 监听相机移动结束 syncController._moveEndHandler = rightViewerInstance.camera.moveEnd.addEventListener(() => { + if (syncController._stopped) return // 检查停止标志 + if (syncController._isUserInteracting) { - console.log('[useDualMapCompare] 主同步触发') - handleCameraSync() - syncController._isUserInteracting = false + console.log('[useDualMapCompare] moveEnd 事件触发,等待相机完全停止...') + // 不要立即重置 _isUserInteracting,让 setInterval 继续检测 + // 标记为"移动已结束但可能还在惯性滑动" + syncController._moveEndReceived = true } }) @@ -172,62 +218,113 @@ export function useDualMapCompare() { return } - // 3. 保险同步逻辑 + // 3. 保险同步逻辑(setInterval) syncController._lastPosition = new Cesium.Cartesian3() + syncController._moveEndReceived = false // 重置标志 syncController._interval = setInterval(() => { + // 停止检查 + if (syncController._stopped) return + + // viewer 有效性检查 + if (!rightViewerInstance || rightViewerInstance.isDestroyed?.()) { + console.log('[useDualMapCompare] Viewer已销毁,停止定时同步') + if (syncController._interval) { + clearInterval(syncController._interval) + syncController._interval = null + } + return + } + const isMoving = controller.isMoving const currentPosition = rightViewerInstance.camera.position if (syncController._isUserInteracting) { if (isMoving) { + // 相机正在移动,持续更新位置 Cesium.Cartesian3.clone(currentPosition, syncController._lastPosition) + syncController._moveEndReceived = false // 重置标志 } else { - const positionChanged = !Cesium.Cartesian3.equalsEpsilon( - currentPosition, - syncController._lastPosition, - 0.1 - ) - if (positionChanged) { - console.log('[useDualMapCompare] 保险同步触发') - handleCameraSync() + // 相机已停止移动 + if (syncController._moveEndReceived) { + // moveEnd 已触发且相机已停止,执行最终同步 + const positionChanged = !Cesium.Cartesian3.equalsEpsilon( + currentPosition, + syncController._lastPosition, + 0.001 // 更高精度 + ) + + if (positionChanged) { + console.log('[useDualMapCompare] 相机完全停止,执行最终同步') + handleCameraSync() + Cesium.Cartesian3.clone(currentPosition, syncController._lastPosition) + } else { + console.log('[useDualMapCompare] 相机完全停止,位置无变化') + } + + // 重置状态 + syncController._isUserInteracting = false + syncController._moveEndReceived = false + } else { + // 相机停止但 moveEnd 未触发(可能是微小移动后停止) + const positionChanged = !Cesium.Cartesian3.equalsEpsilon( + currentPosition, + syncController._lastPosition, + 0.1 + ) + + if (positionChanged) { + console.log('[useDualMapCompare] 保险同步触发(相机停止但moveEnd未触发)') + handleCameraSync() + Cesium.Cartesian3.clone(currentPosition, syncController._lastPosition) + syncController._isUserInteracting = false + } } - if (!controller.isMoving) { - syncController._isUserInteracting = false - } - Cesium.Cartesian3.clone(currentPosition, syncController._lastPosition) } } }, 300) - console.log('[useDualMapCompare] 智能同步设置完成') + console.log('[useDualMapCompare] 智能同步已启动') }, stop: () => { - const controller = rightViewerInstance.scene.screenSpaceCameraController + // 原子性设置停止标志(最优先) + syncController._stopped = true - // 移除 moveStart 监听 - syncController._interactionHandlers.forEach(handler => { - if (typeof rightViewerInstance.camera.moveStart?.removeEventListener === 'function') { - rightViewerInstance.camera.moveStart.removeEventListener(handler) - } - }) - syncController._interactionHandlers = [] - - // 移除 moveEnd 监听 - if (syncController._moveEndHandler && - typeof rightViewerInstance.camera.moveEnd?.removeEventListener === 'function') { - rightViewerInstance.camera.moveEnd.removeEventListener(syncController._moveEndHandler) - } - - // 清除保险同步 + // 立即清除定时器 if (syncController._interval) { clearInterval(syncController._interval) syncController._interval = null } + // 移除事件监听器(需要检查 viewer 是否仍然有效) + if (rightViewerInstance && !rightViewerInstance.isDestroyed?.()) { + syncController._interactionHandlers.forEach(handler => { + if (typeof rightViewerInstance.camera.moveStart?.removeEventListener === 'function') { + try { + rightViewerInstance.camera.moveStart.removeEventListener(handler) + } catch (e) { + console.warn('[useDualMapCompare] 移除 moveStart 监听器失败:', e) + } + } + }) + + if (syncController._moveEndHandler && + typeof rightViewerInstance.camera.moveEnd?.removeEventListener === 'function') { + try { + rightViewerInstance.camera.moveEnd.removeEventListener(syncController._moveEndHandler) + } catch (e) { + console.warn('[useDualMapCompare] 移除 moveEnd 监听器失败:', e) + } + } + } + + syncController._interactionHandlers = [] + syncController._moveEndHandler = null syncController._isUserInteracting = false + syncController._moveEndReceived = false // 重置标志 syncController._lastPosition = null - console.log('[useDualMapCompare] 智能同步已移除') + + console.log('[useDualMapCompare] 智能同步已完全停止') } }