Compare commits

..

No commits in common. "278ed183cccd8ff2e0a2486d18d309296492ef87" and "a84c1d905fa63d0d922905d74c9bb5dac31deadf" have entirely different histories.

4 changed files with 74 additions and 146 deletions

View File

@ -42,7 +42,7 @@ export function useEmergencyDispatch({
// 初始化路由相关 composables
const { calculateRoutes, isCalculating } = useAmapRouting()
const { selectByType } = useEmergencyRouteSelection()
const { drawMultipleRoutes, clearRoutes, clearRoute, getRoutesBounds } = useRouteVisualization()
const { drawMultipleRoutes, clearRoutes, getRoutesBounds } = useRouteVisualization()
// Loading 状态
const showLoading = ref(false)
@ -164,13 +164,8 @@ export function useEmergencyDispatch({
hideEmergencyPointMarkers(viewer)
startMultipleAnimationsWithRoutes(viewer, routes, {
speedMultiplier: 40,
disablePulse: true, // 禁用脉冲效果
// 新增:实体到达时清除对应路线的回调
onEntityComplete: (routeId) => {
console.log(`[useEmergencyDispatch] 实体到达目标,清除路线: ${routeId}`)
clearRoute(viewer, routeId)
}
speedMultiplier: 10,
disablePulse: true // 禁用脉冲效果
})
// 7. 相机飞向全景位置

View File

@ -176,17 +176,17 @@ export function useEntityAnimation() {
)
},
// 添加发光轨迹线
// path: {
// resolution: 1,
// material: new Cesium.PolylineGlowMaterialProperty({
// glowPower: 0.4, // 发光强度
// taperPower: 0.5, // 渐变效果
// color: Cesium.Color.CYAN // 青色轨迹
// }),
// width: 8,
// leadTime: 0, // 前方不显示轨迹
// trailTime: config.duration // 后方显示完整轨迹
// },
path: {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.4, // 发光强度
taperPower: 0.5, // 渐变效果
color: Cesium.Color.CYAN // 青色轨迹
}),
width: 8,
leadTime: 0, // 前方不显示轨迹
trailTime: config.duration // 后方显示完整轨迹
},
properties: {
type: 'animatedSoldier',
name: config.personnelName,
@ -342,17 +342,17 @@ 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
// },
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,
@ -483,11 +483,6 @@ export function useEntityAnimation() {
// 创建位置属性
const positionProperty = new Cesium.SampledPositionProperty()
// 关键修复:设置外推类型,确保在采样范围外保持最后位置
positionProperty.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD
positionProperty.backwardExtrapolationType = Cesium.ExtrapolationType.HOLD
const timeInterval = animationDuration / (positions.length - 1)
positions.forEach((position, index) => {
@ -530,6 +525,9 @@ export function useEntityAnimation() {
// 创建动画实体
return viewer.entities.add({
availability: new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({ start: startTime, stop: stopTime })
]),
position: positionProperty,
orientation: new Cesium.VelocityOrientationProperty(positionProperty),
billboard: {
@ -542,23 +540,22 @@ 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: animationDuration
// },
path: {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.4,
taperPower: 0.5,
color: trailColor
}),
width: 8,
leadTime: 0,
trailTime: animationDuration
},
properties: {
type: route.type === 'equipment' ? 'animatedDevice' : 'animatedSoldier',
name: route.name,
routeId: route.id,
isAnimating: true,
isCompleted: false, // 新增:标记动画是否完成
scaleState: scaleState, // 新增:保存状态引用
animationDuration: animationDuration, // 新增:保存动画时长
animationStartTime: startTime.clone() // 新增:保存开始时间
@ -579,13 +576,10 @@ export function useEntityAnimation() {
return []
}
// 解构回调参数
const { speedMultiplier: baseSpeedMultiplier, disablePulse, onEntityComplete } = options
console.log(`[useEntityAnimation] 启动 ${routes.length} 条路线的动画 (disablePulse: ${disablePulse || false})`)
console.log(`[useEntityAnimation] 启动 ${routes.length} 条路线的动画 (disablePulse: ${options.disablePulse || false})`)
// 计算最大动画时长
const speedMultiplier = baseSpeedMultiplier || 10
const speedMultiplier = options.speedMultiplier || 10
const durations = routes.map(r =>
Math.min(Math.max(r.duration / speedMultiplier, 30), 120)
)
@ -603,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.UNBOUNDED // 允许时间继续前进,支持不同时长动画独立完成
viewer.clock.clockRange = Cesium.ClockRange.CLAMPED // 动画到达终点后停止,不循环
viewer.clock.multiplier = 1
viewer.clock.shouldAnimate = true
@ -633,15 +627,7 @@ export function useEntityAnimation() {
// 监听时钟,检测动画完成
const clockListener = viewer.clock.onTick.addEventListener((clock) => {
checkAnimationsCompletion(viewer, clock.currentTime, onEntityComplete)
// 注释掉自动停止时钟的逻辑,让时钟继续运行
// 原因:时钟停止会导致 Cesium 停止更新实体位置,导致前面完成的实体消失
// if (areAllAnimationsCompleted()) {
// console.log('[useEntityAnimation] 所有动画已完成,停止时钟')
// viewer.clock.shouldAnimate = false
// clockListener() // 移除监听器
// }
checkAnimationsCompletion(viewer, clock.currentTime)
})
// 监听动画结束
@ -658,53 +644,31 @@ export function useEntityAnimation() {
}
/**
* 检查动画是否完成如果完成则停止脉冲并清除路线
* 检查动画是否完成如果完成则停止脉冲
* @param {Cesium.Viewer} viewer
* @param {Cesium.JulianDate} currentTime
* @param {Function} onEntityComplete - 完成回调 (routeId) => void
*/
const checkAnimationsCompletion = (viewer, currentTime, onEntityComplete) => {
const checkAnimationsCompletion = (viewer, currentTime) => {
animatedEntities.value.forEach(entity => {
if (!entity || !entity.properties) return
const scaleState = entity.properties.scaleState?.getValue()
const startTime = entity.properties.animationStartTime?.getValue()
const duration = entity.properties.animationDuration?.getValue()
const isCompleted = entity.properties.isCompleted?.getValue()
const routeId = entity.properties.routeId?.getValue()
if (!scaleState || !startTime || !duration || !routeId) return
if (!scaleState || !startTime || !duration) return
// 如果已经完成,跳过(避免重复触发)
if (isCompleted) return
// 如果已经停止脉冲,跳过
if (!scaleState.isPulsing) return
// 计算已过时间
const elapsed = Cesium.JulianDate.secondsDifference(currentTime, startTime)
// 如果动画已完成留0.5秒容差)
if (elapsed >= duration - 0.5) {
const entityName = entity.properties.name?.getValue() || '未知实体'
console.log(`[useEntityAnimation] ========================================`)
console.log(`[useEntityAnimation] 实体 "${entityName}" 动画完成`)
console.log(`[useEntityAnimation] - routeId: ${routeId}`)
console.log(`[useEntityAnimation] - elapsed: ${elapsed.toFixed(2)}`)
console.log(`[useEntityAnimation] - duration: ${duration}`)
console.log(`[useEntityAnimation] ========================================`)
// 1. 停止脉冲效果
console.log(`[useEntityAnimation] 实体 ${entity.properties.name.getValue()} 动画完成,停止脉冲`)
scaleState.isPulsing = false
scaleState.fixedScale = 1.0
// 2. 标记为已完成(使用 ConstantProperty
entity.properties.isCompleted = new Cesium.ConstantProperty(true)
// 3. 触发回调清除对应路线
if (onEntityComplete && typeof onEntityComplete === 'function') {
console.log(`[useEntityAnimation] 触发 onEntityComplete 回调routeId: ${routeId}`)
onEntityComplete(routeId)
} else {
console.warn(`[useEntityAnimation] onEntityComplete 回调不存在或不是函数`)
}
scaleState.fixedScale = 1.0 // 设置为正常大小
}
})
}
@ -729,19 +693,6 @@ export function useEntityAnimation() {
console.log('[useEntityAnimation] 已停止所有脉冲效果')
}
/**
* 检查所有动画是否都已完成
* @returns {boolean}
*/
const areAllAnimationsCompleted = () => {
if (animatedEntities.value.length === 0) return false
return animatedEntities.value.every(entity => {
const isCompleted = entity.properties?.isCompleted?.getValue()
return isCompleted === true
})
}
return {
animatedEntity,
animatedEntities,

View File

@ -65,7 +65,7 @@ export function useRouteVisualization() {
polyline: {
positions: positions,
width: styleOptions.width || style.width,
material: styleOptions.material || (typeof style.material === 'function' ? style.material(viewer) : style.material),
material: styleOptions.material || style.material(viewer),
clampToGround: true,
classificationType: Cesium.ClassificationType.TERRAIN
},
@ -82,7 +82,7 @@ export function useRouteVisualization() {
routeEntities.value.push(entity)
console.log(`[useRouteVisualization] 绘制路线: ${routeData.name} (${type}), routeId: ${routeData.id}`)
console.log(`[useRouteVisualization] 绘制路线: ${routeData.name} (${type})`)
return entity
}
@ -224,52 +224,27 @@ export function useRouteVisualization() {
* @param {string} routeId - 路线 ID
*/
const clearRoute = (viewer, routeId) => {
if (!viewer || !routeId) {
console.warn(`[useRouteVisualization] clearRoute 参数无效: viewer=${!!viewer}, routeId=${routeId}`)
return
}
console.log(`[useRouteVisualization] 开始清除路线: ${routeId}`)
console.log(`[useRouteVisualization] 当前路线实体数量: ${routeEntities.value.length}`)
console.log(`[useRouteVisualization] 当前标签实体数量: ${labelEntities.value.length}`)
let removedRoutes = 0
let removedLabels = 0
if (!viewer || !routeId) return
// 清除路线实体
routeEntities.value = routeEntities.value.filter(entity => {
if (entity && entity.properties) {
// 关键修复:使用 getValue() 获取 Cesium Property 的实际值
const entityRouteId = entity.properties.routeId?.getValue ? entity.properties.routeId.getValue() : entity.properties.routeId
console.log(`[useRouteVisualization] 检查路线实体 routeId: "${entityRouteId}", 目标: "${routeId}", 匹配: ${entityRouteId === routeId}`)
if (entityRouteId === routeId) {
console.log(`[useRouteVisualization] ✅ 匹配成功,移除路线实体: ${routeId}`)
viewer.entities.remove(entity)
removedRoutes++
return false
}
if (entity && entity.properties && entity.properties.routeId === routeId) {
viewer.entities.remove(entity)
return false
}
return true
})
// 清除标签实体
labelEntities.value = labelEntities.value.filter(entity => {
if (entity && entity.properties) {
// 关键修复:使用 getValue() 获取 Cesium Property 的实际值
const entityRouteId = entity.properties.routeId?.getValue ? entity.properties.routeId.getValue() : entity.properties.routeId
if (entityRouteId === routeId) {
console.log(`[useRouteVisualization] 移除标签实体: ${routeId}`)
viewer.entities.remove(entity)
removedLabels++
return false
}
if (entity && entity.properties && entity.properties.routeId === routeId) {
viewer.entities.remove(entity)
return false
}
return true
})
console.log(`[useRouteVisualization] 已清除路线: ${routeId}, 移除了 ${removedRoutes} 条路线和 ${removedLabels} 个标签`)
console.log(`[useRouteVisualization] 已清除路线: ${routeId}`)
}
/**

View File

@ -11,18 +11,25 @@ import * as Cesium from 'cesium'
export const ROUTE_STYLES = {
// 人员路线样式
personnel: {
color: Cesium.Color.RED,
color: Cesium.Color.CYAN,
width: 5,
material: Cesium.Color.RED,
labelColor: Cesium.Color.RED
material: (viewer) => new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.3,
color: Cesium.Color.CYAN
}),
labelColor: Cesium.Color.CYAN
},
// 装备路线样式
equipment: {
color: Cesium.Color.RED,
color: Cesium.Color.ORANGE,
width: 6,
material: Cesium.Color.RED,
labelColor: Cesium.Color.RED
material: (viewer) => new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.ORANGE,
dashLength: 20.0,
dashPattern: 255
}),
labelColor: Cesium.Color.ORANGE
},
// 降级直线样式API 失败时使用)
@ -58,7 +65,7 @@ export const LABEL_STYLE = {
*/
export const ROUTE_ANIMATION_CONFIG = {
// 动画速度倍数(相对于真实时间)
speedMultiplier: 40,
speedMultiplier: 10,
// 最小动画时长(秒)
minDuration: 30,