修改了动画逻辑 在动画结束的时候 删除掉新增的动画实体
This commit is contained in:
parent
539f1b16db
commit
dd74af0930
@ -302,8 +302,15 @@ export function useEntityAnimation() {
|
||||
* @returns {Cesium.Entity}
|
||||
*/
|
||||
const createMovingEntity = (viewer, pathCoordinates, config, startTime, stopTime) => {
|
||||
try {
|
||||
const positionProperty = new Cesium.SampledPositionProperty()
|
||||
const numberOfPoints = pathCoordinates.length
|
||||
|
||||
if (numberOfPoints === 0) {
|
||||
console.warn('[useEntityAnimation] 路径坐标为空')
|
||||
return null
|
||||
}
|
||||
|
||||
const timeInterval = config.duration / (numberOfPoints - 1)
|
||||
|
||||
pathCoordinates.forEach((coord, index) => {
|
||||
@ -324,9 +331,8 @@ export function useEntityAnimation() {
|
||||
}, false)
|
||||
|
||||
const icon = config.type === 'device' ? deviceIcon : soldierIcon
|
||||
const trailColor = config.type === 'device' ? Cesium.Color.ORANGE : Cesium.Color.CYAN
|
||||
|
||||
return viewer.entities.add({
|
||||
const entity = viewer.entities.add({
|
||||
availability: new Cesium.TimeIntervalCollection([
|
||||
new Cesium.TimeInterval({ start: startTime, stop: stopTime })
|
||||
]),
|
||||
@ -342,17 +348,6 @@ export function useEntityAnimation() {
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
scaleByDistance: new Cesium.NearFarScalar(1000, 1.5, 50000, 0.8)
|
||||
},
|
||||
// path: {
|
||||
// resolution: 1,
|
||||
// material: new Cesium.PolylineGlowMaterialProperty({
|
||||
// glowPower: 0.4,
|
||||
// taperPower: 0.5,
|
||||
// color: trailColor
|
||||
// }),
|
||||
// width: 8,
|
||||
// leadTime: 0,
|
||||
// trailTime: config.duration
|
||||
// },
|
||||
properties: {
|
||||
type: config.type === 'device' ? 'animatedDevice' : 'animatedSoldier',
|
||||
name: config.name,
|
||||
@ -360,7 +355,16 @@ export function useEntityAnimation() {
|
||||
isAnimating: true
|
||||
}
|
||||
})
|
||||
|
||||
console.log(`[useEntityAnimation] 创建${config.type === 'device' ? '设备' : '人员'}实体: ${config.name}`)
|
||||
return entity
|
||||
|
||||
} catch (error) {
|
||||
console.error('[useEntityAnimation] 创建移动实体失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 启动多组移动动画(设备组 + 人员组2)
|
||||
@ -373,6 +377,9 @@ export function useEntityAnimation() {
|
||||
return []
|
||||
}
|
||||
|
||||
// 先停止所有现有动画
|
||||
stopAllMovements(viewer)
|
||||
|
||||
const config = {
|
||||
duration: options.duration ?? 60
|
||||
}
|
||||
@ -382,17 +389,15 @@ export function useEntityAnimation() {
|
||||
const startTime = Cesium.JulianDate.now()
|
||||
const stopTime = Cesium.JulianDate.addSeconds(startTime, config.duration, new Cesium.JulianDate())
|
||||
|
||||
// 配置 viewer 时钟
|
||||
viewer.clock.startTime = startTime.clone()
|
||||
viewer.clock.stopTime = stopTime.clone()
|
||||
viewer.clock.currentTime = startTime.clone()
|
||||
viewer.clock.clockRange = Cesium.ClockRange.CLAMPED // 动画到达终点后停止,不循环
|
||||
viewer.clock.clockRange = Cesium.ClockRange.CLAMPED
|
||||
viewer.clock.multiplier = 1
|
||||
viewer.clock.shouldAnimate = true
|
||||
|
||||
// 清除之前的动画实体
|
||||
animatedEntities.value.forEach(entity => {
|
||||
if (entity) viewer.entities.remove(entity)
|
||||
})
|
||||
// 确保实体数组是空的
|
||||
animatedEntities.value = []
|
||||
|
||||
// 创建设备组移动实体
|
||||
@ -400,31 +405,47 @@ export function useEntityAnimation() {
|
||||
duration: config.duration,
|
||||
type: 'device',
|
||||
name: '应急设备车',
|
||||
department: '应急装备队'
|
||||
department: '应急装备队',
|
||||
disablePulse: options.disablePulse
|
||||
}, startTime, stopTime)
|
||||
|
||||
if (deviceEntity) {
|
||||
animatedEntities.value.push(deviceEntity)
|
||||
}
|
||||
|
||||
// 创建人员组2移动实体
|
||||
const personnel2Entity = createMovingEntity(viewer, PERSONNEL_PATH_COORDINATES_2, {
|
||||
duration: config.duration,
|
||||
type: 'soldier',
|
||||
name: '应急救援队员',
|
||||
department: '应急救援队'
|
||||
department: '应急救援队',
|
||||
disablePulse: options.disablePulse
|
||||
}, startTime, stopTime)
|
||||
|
||||
if (personnel2Entity) {
|
||||
animatedEntities.value.push(personnel2Entity)
|
||||
}
|
||||
|
||||
isAnimating.value = true
|
||||
|
||||
// 设置超时清理
|
||||
const timeoutId = setTimeout(() => {
|
||||
console.log('[useEntityAnimation] 多组动画超时,强制清理')
|
||||
stopAllMovements(viewer)
|
||||
}, (config.duration + 5) * 1000) // 动画时长+5秒缓冲
|
||||
|
||||
const removeListener = viewer.clock.onStop.addEventListener(() => {
|
||||
console.log('[useEntityAnimation] 多组动画已结束')
|
||||
clearTimeout(timeoutId)
|
||||
isAnimating.value = false
|
||||
removeListener()
|
||||
})
|
||||
|
||||
console.log('[useEntityAnimation] 已启动 2 组额外移动动画')
|
||||
console.log('[useEntityAnimation] 已启动 2 组移动动画')
|
||||
return animatedEntities.value
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 停止所有移动动画
|
||||
* @param {Cesium.Viewer} viewer
|
||||
@ -432,24 +453,63 @@ export function useEntityAnimation() {
|
||||
const stopAllMovements = (viewer) => {
|
||||
if (!viewer) return
|
||||
|
||||
console.log('[useEntityAnimation] 停止所有移动动画')
|
||||
|
||||
// 停止时钟动画
|
||||
viewer.clock.shouldAnimate = false
|
||||
|
||||
// 取消相机跟随
|
||||
if (viewer.trackedEntity) {
|
||||
viewer.trackedEntity = undefined
|
||||
}
|
||||
|
||||
// 停止所有脉冲效果
|
||||
stopAllPulses(viewer)
|
||||
|
||||
// 移除单个动画实体
|
||||
if (animatedEntity.value) {
|
||||
try {
|
||||
viewer.entities.remove(animatedEntity.value)
|
||||
} catch (e) {
|
||||
console.warn('[useEntityAnimation] 移除单个实体时出错:', e)
|
||||
}
|
||||
animatedEntity.value = null
|
||||
}
|
||||
|
||||
// 移除所有动画实体
|
||||
animatedEntities.value.forEach(entity => {
|
||||
if (entity) viewer.entities.remove(entity)
|
||||
if (entity) {
|
||||
try {
|
||||
viewer.entities.remove(entity)
|
||||
} catch (e) {
|
||||
console.warn('[useEntityAnimation] 移除实体时出错:', e)
|
||||
}
|
||||
}
|
||||
})
|
||||
animatedEntities.value = []
|
||||
|
||||
isAnimating.value = false
|
||||
console.log('[useEntityAnimation] 已停止所有移动动画')
|
||||
// 额外清理:移除所有类型为 animatedSoldier 和 animatedDevice 的实体
|
||||
const entitiesToRemove = []
|
||||
viewer.entities.values.forEach(entity => {
|
||||
const entityType = entity.properties?.type?.getValue?.()
|
||||
if (entityType === 'animatedSoldier' || entityType === 'animatedDevice') {
|
||||
entitiesToRemove.push(entity)
|
||||
}
|
||||
})
|
||||
|
||||
entitiesToRemove.forEach(entity => {
|
||||
try {
|
||||
viewer.entities.remove(entity)
|
||||
console.log('[useEntityAnimation] 清理残留实体:', entity.properties?.name?.getValue?.() || '未知')
|
||||
} catch (e) {
|
||||
console.warn('[useEntityAnimation] 清理残留实体时出错:', e)
|
||||
}
|
||||
})
|
||||
|
||||
isAnimating.value = false
|
||||
console.log('[useEntityAnimation] 已停止所有移动动画,并清理残留实体')
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据动态路线创建动画实体
|
||||
@ -633,15 +693,26 @@ export function useEntityAnimation() {
|
||||
|
||||
// 监听时钟,检测动画完成
|
||||
const clockListener = viewer.clock.onTick.addEventListener((clock) => {
|
||||
checkAnimationsCompletion(viewer, clock.currentTime, onEntityComplete)
|
||||
checkAnimationsCompletion(viewer, clock.currentTime, (routeId, entity) => {
|
||||
// 调用原有的路线清除回调
|
||||
if (options.onEntityComplete) {
|
||||
options.onEntityComplete(routeId)
|
||||
}
|
||||
|
||||
// 注释掉自动停止时钟的逻辑,让时钟继续运行
|
||||
// 原因:时钟停止会导致 Cesium 停止更新实体位置,导致前面完成的实体消失
|
||||
// if (areAllAnimationsCompleted()) {
|
||||
// console.log('[useEntityAnimation] 所有动画已完成,停止时钟')
|
||||
// viewer.clock.shouldAnimate = false
|
||||
// clockListener() // 移除监听器
|
||||
// }
|
||||
// 延迟移除实体
|
||||
setTimeout(() => {
|
||||
if (viewer && !viewer.isDestroyed() && entity) {
|
||||
viewer.entities.remove(entity)
|
||||
console.log(`[useEntityAnimation] 动画完成,已移除实体`)
|
||||
|
||||
// 从动画实体列表中移除
|
||||
const index = animatedEntities.value.indexOf(entity)
|
||||
if (index > -1) {
|
||||
animatedEntities.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}, 1000) // 延迟1秒移除
|
||||
})
|
||||
})
|
||||
|
||||
// 监听动画结束
|
||||
@ -652,16 +723,68 @@ export function useEntityAnimation() {
|
||||
clockListener() // 移除 tick 监听器
|
||||
})
|
||||
|
||||
// 新增:60秒超时强制清除所有实体
|
||||
const timeoutId = setTimeout(() => {
|
||||
console.log('[useEntityAnimation] 60秒超时,强制清除所有动画实体')
|
||||
forceCleanupAllAnimations(viewer, options.onEntityComplete)
|
||||
}, 60000) // 60秒超时
|
||||
|
||||
console.log(`[useEntityAnimation] 成功启动 ${animatedEntities.value.length} 个动画实体`)
|
||||
|
||||
return animatedEntities.value
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查动画是否完成,如果完成则停止脉冲并清除路线
|
||||
* 强制清理所有动画实体和路线
|
||||
* @param {Cesium.Viewer} viewer
|
||||
* @param {Function} onEntityComplete - 路线清除回调
|
||||
*/
|
||||
const forceCleanupAllAnimations = (viewer, onEntityComplete) => {
|
||||
if (!viewer) return
|
||||
|
||||
// 停止时钟
|
||||
viewer.clock.shouldAnimate = false
|
||||
|
||||
// 停止所有脉冲
|
||||
stopAllPulses(viewer)
|
||||
|
||||
// 移除所有实体并触发回调
|
||||
animatedEntities.value.forEach(entity => {
|
||||
if (!entity || !entity.properties) return
|
||||
|
||||
const routeId = entity.properties.routeId?.getValue()
|
||||
|
||||
// 触发路线清除回调
|
||||
if (onEntityComplete && typeof onEntityComplete === 'function' && routeId) {
|
||||
console.log(`[useEntityAnimation] 超时清除路线: ${routeId}`)
|
||||
onEntityComplete(routeId)
|
||||
}
|
||||
|
||||
// 移除实体
|
||||
if (viewer && !viewer.isDestroyed()) {
|
||||
viewer.entities.remove(entity)
|
||||
}
|
||||
})
|
||||
|
||||
// 清空实体列表
|
||||
animatedEntities.value = []
|
||||
|
||||
// 清除单个动画实体
|
||||
if (animatedEntity.value) {
|
||||
viewer.entities.remove(animatedEntity.value)
|
||||
animatedEntity.value = null
|
||||
}
|
||||
|
||||
isAnimating.value = false
|
||||
console.log('[useEntityAnimation] 强制清理完成')
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查动画是否完成,如果完成则停止脉冲、清除路线并移除实体
|
||||
* @param {Cesium.Viewer} viewer
|
||||
* @param {Cesium.JulianDate} currentTime
|
||||
* @param {Function} onEntityComplete - 完成回调 (routeId) => void
|
||||
* @param {Function} onEntityComplete - 完成回调 (routeId, entity) => void
|
||||
*/
|
||||
const checkAnimationsCompletion = (viewer, currentTime, onEntityComplete) => {
|
||||
animatedEntities.value.forEach(entity => {
|
||||
@ -698,17 +821,25 @@ export function useEntityAnimation() {
|
||||
// 2. 标记为已完成(使用 ConstantProperty)
|
||||
entity.properties.isCompleted = new Cesium.ConstantProperty(true)
|
||||
|
||||
// 3. 触发回调清除对应路线
|
||||
// 3. 触发回调清除对应路线,并传递实体信息以便移除
|
||||
if (onEntityComplete && typeof onEntityComplete === 'function') {
|
||||
console.log(`[useEntityAnimation] 触发 onEntityComplete 回调,routeId: ${routeId}`)
|
||||
onEntityComplete(routeId)
|
||||
onEntityComplete(routeId, entity) // 传递实体信息
|
||||
} else {
|
||||
console.warn(`[useEntityAnimation] onEntityComplete 回调不存在或不是函数`)
|
||||
// 如果没有回调,直接移除实体
|
||||
setTimeout(() => {
|
||||
if (viewer && !viewer.isDestroyed()) {
|
||||
viewer.entities.remove(entity)
|
||||
console.log(`[useEntityAnimation] 已自动移除实体: ${entityName}`)
|
||||
}
|
||||
}, 1000) // 延迟1秒移除,确保动画完全结束
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 停止所有实体的脉冲效果
|
||||
* @param {Cesium.Viewer} viewer
|
||||
@ -762,7 +893,9 @@ export function useEntityAnimation() {
|
||||
startMultipleAnimationsWithRoutes,
|
||||
// 新增:脉冲控制方法
|
||||
checkAnimationsCompletion,
|
||||
stopAllPulses
|
||||
stopAllPulses,
|
||||
// 新增:强制清理方法
|
||||
forceCleanupAllAnimations
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -32,45 +32,45 @@ export function useSimulatedMarkers() {
|
||||
const newMarkers = []
|
||||
|
||||
// 1. Create 2 center markers (static, no animation)
|
||||
const centerPersonnelCoords = applyRandomOffset(disasterCenter, 10, 30)
|
||||
const centerPersonnel = viewer.entities.add({
|
||||
position: Cesium.Cartesian3.fromDegrees(centerPersonnelCoords.lon, centerPersonnelCoords.lat, 0),
|
||||
billboard: {
|
||||
image: soldierIcon,
|
||||
width: 36,
|
||||
height: 40,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY
|
||||
},
|
||||
properties: new Cesium.PropertyBag({
|
||||
type: 'centerPersonnel',
|
||||
name: '应急人员',
|
||||
isSimulated: true,
|
||||
isStatic: true
|
||||
})
|
||||
})
|
||||
newMarkers.push(centerPersonnel)
|
||||
// const centerPersonnelCoords = applyRandomOffset(disasterCenter, 10, 30)
|
||||
// const centerPersonnel = viewer.entities.add({
|
||||
// position: Cesium.Cartesian3.fromDegrees(centerPersonnelCoords.lon, centerPersonnelCoords.lat, 0),
|
||||
// billboard: {
|
||||
// image: soldierIcon,
|
||||
// width: 36,
|
||||
// height: 40,
|
||||
// verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
// heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
// disableDepthTestDistance: Number.POSITIVE_INFINITY
|
||||
// },
|
||||
// properties: new Cesium.PropertyBag({
|
||||
// type: 'centerPersonnel',
|
||||
// name: '应急人员',
|
||||
// isSimulated: true,
|
||||
// isStatic: true
|
||||
// })
|
||||
// })
|
||||
// newMarkers.push(centerPersonnel)
|
||||
|
||||
const centerEquipmentCoords = applyRandomOffset(disasterCenter, 10, 30)
|
||||
const centerEquipment = viewer.entities.add({
|
||||
position: Cesium.Cartesian3.fromDegrees(centerEquipmentCoords.lon, centerEquipmentCoords.lat, 0),
|
||||
billboard: {
|
||||
image: deviceIcon,
|
||||
width: 36,
|
||||
height: 40,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY
|
||||
},
|
||||
properties: new Cesium.PropertyBag({
|
||||
type: 'centerEquipment',
|
||||
name: '应急装备',
|
||||
isSimulated: true,
|
||||
isStatic: true
|
||||
})
|
||||
})
|
||||
newMarkers.push(centerEquipment)
|
||||
// const centerEquipmentCoords = applyRandomOffset(disasterCenter, 10, 30)
|
||||
// const centerEquipment = viewer.entities.add({
|
||||
// position: Cesium.Cartesian3.fromDegrees(centerEquipmentCoords.lon, centerEquipmentCoords.lat, 0),
|
||||
// billboard: {
|
||||
// image: deviceIcon,
|
||||
// width: 36,
|
||||
// height: 40,
|
||||
// verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
// heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
// disableDepthTestDistance: Number.POSITIVE_INFINITY
|
||||
// },
|
||||
// properties: new Cesium.PropertyBag({
|
||||
// type: 'centerEquipment',
|
||||
// name: '应急装备',
|
||||
// isSimulated: true,
|
||||
// isStatic: true
|
||||
// })
|
||||
// })
|
||||
// newMarkers.push(centerEquipment)
|
||||
|
||||
// 2. Create 4 emergency point markers (will be animated later)
|
||||
emergencyPoints.slice(0, 2).forEach((point, index) => {
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
v-if="isCompareMode"
|
||||
class="situational-awareness__sync-btn"
|
||||
@click="toggleCameraSync"
|
||||
:text="isCameraSyncEnabled ? '同步' : '不同步'"
|
||||
:text="isCameraSyncEnabled ? '同步灾前灾后实景' : '未同步灾前灾后实景'"
|
||||
>
|
||||
</ActionButton>
|
||||
<!-- 灾前现场实景标签 - 在中间分割线左侧 -->
|
||||
@ -1824,10 +1824,10 @@ provide("triggerJump", (duration = 5, height = 30) => {
|
||||
|
||||
.situational-awareness__sync-btn {
|
||||
position: absolute;
|
||||
left: 40%;
|
||||
top: 133px;
|
||||
left: 32%;
|
||||
top: calc(var(--sa-header-height));
|
||||
height: 31px;
|
||||
transform: translateX(calc(-100% - vw(10)));
|
||||
|
||||
pointer-events: auto; // 恢复按钮的交互能力
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user