This commit is contained in:
huangchenhao 2025-11-27 22:44:31 +08:00
commit 0dca401b1c
3 changed files with 189 additions and 59 deletions

View File

@ -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 事件
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) {
if (error.message && error.message.includes('超时')) {
// 超时但继续执行(保持原有行为)
console.warn(`[use3DTiles] ${error.message},继续执行`)
} else {
console.error('[use3DTiles] 等待 Tileset 就绪失败:', error)
}
// 即使失败也不抛出异常,允许程序继续执行
} finally {
cleanup() // 保险清理
}
}

View File

@ -105,10 +105,32 @@ export function useDualMapCompare() {
let isSyncing = false
const handleCameraSync = () => {
if (isSyncing) return
// 防御检查1viewer 存在性检查
if (!rightViewerInstance || !leftViewerInstance) {
return
}
// 防御检查2viewer 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(),
@ -132,8 +154,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) {
@ -145,13 +172,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()
@ -161,12 +202,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
}
})
@ -175,62 +221,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 {
// 相机已停止移动
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] 保险同步触发')
console.log('[useDualMapCompare] 保险同步触发相机停止但moveEnd未触发')
handleCameraSync()
}
if (!controller.isMoving) {
Cesium.Cartesian3.clone(currentPosition, syncController._lastPosition)
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] 智能同步已完全停止')
}
}

View File

@ -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