Compare commits
No commits in common. "278ed183cccd8ff2e0a2486d18d309296492ef87" and "a84c1d905fa63d0d922905d74c9bb5dac31deadf" have entirely different histories.
278ed183cc
...
a84c1d905f
@ -42,7 +42,7 @@ export function useEmergencyDispatch({
|
|||||||
// 初始化路由相关 composables
|
// 初始化路由相关 composables
|
||||||
const { calculateRoutes, isCalculating } = useAmapRouting()
|
const { calculateRoutes, isCalculating } = useAmapRouting()
|
||||||
const { selectByType } = useEmergencyRouteSelection()
|
const { selectByType } = useEmergencyRouteSelection()
|
||||||
const { drawMultipleRoutes, clearRoutes, clearRoute, getRoutesBounds } = useRouteVisualization()
|
const { drawMultipleRoutes, clearRoutes, getRoutesBounds } = useRouteVisualization()
|
||||||
|
|
||||||
// Loading 状态
|
// Loading 状态
|
||||||
const showLoading = ref(false)
|
const showLoading = ref(false)
|
||||||
@ -164,13 +164,8 @@ export function useEmergencyDispatch({
|
|||||||
hideEmergencyPointMarkers(viewer)
|
hideEmergencyPointMarkers(viewer)
|
||||||
|
|
||||||
startMultipleAnimationsWithRoutes(viewer, routes, {
|
startMultipleAnimationsWithRoutes(viewer, routes, {
|
||||||
speedMultiplier: 40,
|
speedMultiplier: 10,
|
||||||
disablePulse: true, // 禁用脉冲效果
|
disablePulse: true // 禁用脉冲效果
|
||||||
// 新增:实体到达时清除对应路线的回调
|
|
||||||
onEntityComplete: (routeId) => {
|
|
||||||
console.log(`[useEmergencyDispatch] 实体到达目标,清除路线: ${routeId}`)
|
|
||||||
clearRoute(viewer, routeId)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 7. 相机飞向全景位置
|
// 7. 相机飞向全景位置
|
||||||
|
|||||||
@ -176,17 +176,17 @@ export function useEntityAnimation() {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
// 添加发光轨迹线
|
// 添加发光轨迹线
|
||||||
// path: {
|
path: {
|
||||||
// resolution: 1,
|
resolution: 1,
|
||||||
// material: new Cesium.PolylineGlowMaterialProperty({
|
material: new Cesium.PolylineGlowMaterialProperty({
|
||||||
// glowPower: 0.4, // 发光强度
|
glowPower: 0.4, // 发光强度
|
||||||
// taperPower: 0.5, // 渐变效果
|
taperPower: 0.5, // 渐变效果
|
||||||
// color: Cesium.Color.CYAN // 青色轨迹
|
color: Cesium.Color.CYAN // 青色轨迹
|
||||||
// }),
|
}),
|
||||||
// width: 8,
|
width: 8,
|
||||||
// leadTime: 0, // 前方不显示轨迹
|
leadTime: 0, // 前方不显示轨迹
|
||||||
// trailTime: config.duration // 后方显示完整轨迹
|
trailTime: config.duration // 后方显示完整轨迹
|
||||||
// },
|
},
|
||||||
properties: {
|
properties: {
|
||||||
type: 'animatedSoldier',
|
type: 'animatedSoldier',
|
||||||
name: config.personnelName,
|
name: config.personnelName,
|
||||||
@ -342,17 +342,17 @@ export function useEntityAnimation() {
|
|||||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||||
scaleByDistance: new Cesium.NearFarScalar(1000, 1.5, 50000, 0.8)
|
scaleByDistance: new Cesium.NearFarScalar(1000, 1.5, 50000, 0.8)
|
||||||
},
|
},
|
||||||
// path: {
|
path: {
|
||||||
// resolution: 1,
|
resolution: 1,
|
||||||
// material: new Cesium.PolylineGlowMaterialProperty({
|
material: new Cesium.PolylineGlowMaterialProperty({
|
||||||
// glowPower: 0.4,
|
glowPower: 0.4,
|
||||||
// taperPower: 0.5,
|
taperPower: 0.5,
|
||||||
// color: trailColor
|
color: trailColor
|
||||||
// }),
|
}),
|
||||||
// width: 8,
|
width: 8,
|
||||||
// leadTime: 0,
|
leadTime: 0,
|
||||||
// trailTime: config.duration
|
trailTime: config.duration
|
||||||
// },
|
},
|
||||||
properties: {
|
properties: {
|
||||||
type: config.type === 'device' ? 'animatedDevice' : 'animatedSoldier',
|
type: config.type === 'device' ? 'animatedDevice' : 'animatedSoldier',
|
||||||
name: config.name,
|
name: config.name,
|
||||||
@ -483,11 +483,6 @@ export function useEntityAnimation() {
|
|||||||
|
|
||||||
// 创建位置属性
|
// 创建位置属性
|
||||||
const positionProperty = new Cesium.SampledPositionProperty()
|
const positionProperty = new Cesium.SampledPositionProperty()
|
||||||
|
|
||||||
// 关键修复:设置外推类型,确保在采样范围外保持最后位置
|
|
||||||
positionProperty.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD
|
|
||||||
positionProperty.backwardExtrapolationType = Cesium.ExtrapolationType.HOLD
|
|
||||||
|
|
||||||
const timeInterval = animationDuration / (positions.length - 1)
|
const timeInterval = animationDuration / (positions.length - 1)
|
||||||
|
|
||||||
positions.forEach((position, index) => {
|
positions.forEach((position, index) => {
|
||||||
@ -530,6 +525,9 @@ export function useEntityAnimation() {
|
|||||||
|
|
||||||
// 创建动画实体
|
// 创建动画实体
|
||||||
return viewer.entities.add({
|
return viewer.entities.add({
|
||||||
|
availability: new Cesium.TimeIntervalCollection([
|
||||||
|
new Cesium.TimeInterval({ start: startTime, stop: stopTime })
|
||||||
|
]),
|
||||||
position: positionProperty,
|
position: positionProperty,
|
||||||
orientation: new Cesium.VelocityOrientationProperty(positionProperty),
|
orientation: new Cesium.VelocityOrientationProperty(positionProperty),
|
||||||
billboard: {
|
billboard: {
|
||||||
@ -542,23 +540,22 @@ export function useEntityAnimation() {
|
|||||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||||
scaleByDistance: new Cesium.NearFarScalar(1000, 1.5, 50000, 0.8)
|
scaleByDistance: new Cesium.NearFarScalar(1000, 1.5, 50000, 0.8)
|
||||||
},
|
},
|
||||||
// path: {
|
path: {
|
||||||
// resolution: 1,
|
resolution: 1,
|
||||||
// material: new Cesium.PolylineGlowMaterialProperty({
|
material: new Cesium.PolylineGlowMaterialProperty({
|
||||||
// glowPower: 0.4,
|
glowPower: 0.4,
|
||||||
// taperPower: 0.5,
|
taperPower: 0.5,
|
||||||
// color: trailColor
|
color: trailColor
|
||||||
// }),
|
}),
|
||||||
// width: 8,
|
width: 8,
|
||||||
// leadTime: 0,
|
leadTime: 0,
|
||||||
// trailTime: animationDuration
|
trailTime: animationDuration
|
||||||
// },
|
},
|
||||||
properties: {
|
properties: {
|
||||||
type: route.type === 'equipment' ? 'animatedDevice' : 'animatedSoldier',
|
type: route.type === 'equipment' ? 'animatedDevice' : 'animatedSoldier',
|
||||||
name: route.name,
|
name: route.name,
|
||||||
routeId: route.id,
|
routeId: route.id,
|
||||||
isAnimating: true,
|
isAnimating: true,
|
||||||
isCompleted: false, // 新增:标记动画是否完成
|
|
||||||
scaleState: scaleState, // 新增:保存状态引用
|
scaleState: scaleState, // 新增:保存状态引用
|
||||||
animationDuration: animationDuration, // 新增:保存动画时长
|
animationDuration: animationDuration, // 新增:保存动画时长
|
||||||
animationStartTime: startTime.clone() // 新增:保存开始时间
|
animationStartTime: startTime.clone() // 新增:保存开始时间
|
||||||
@ -579,13 +576,10 @@ export function useEntityAnimation() {
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解构回调参数
|
console.log(`[useEntityAnimation] 启动 ${routes.length} 条路线的动画 (disablePulse: ${options.disablePulse || false})`)
|
||||||
const { speedMultiplier: baseSpeedMultiplier, disablePulse, onEntityComplete } = options
|
|
||||||
|
|
||||||
console.log(`[useEntityAnimation] 启动 ${routes.length} 条路线的动画 (disablePulse: ${disablePulse || false})`)
|
|
||||||
|
|
||||||
// 计算最大动画时长
|
// 计算最大动画时长
|
||||||
const speedMultiplier = baseSpeedMultiplier || 10
|
const speedMultiplier = options.speedMultiplier || 10
|
||||||
const durations = routes.map(r =>
|
const durations = routes.map(r =>
|
||||||
Math.min(Math.max(r.duration / speedMultiplier, 30), 120)
|
Math.min(Math.max(r.duration / speedMultiplier, 30), 120)
|
||||||
)
|
)
|
||||||
@ -603,7 +597,7 @@ export function useEntityAnimation() {
|
|||||||
viewer.clock.startTime = startTime.clone()
|
viewer.clock.startTime = startTime.clone()
|
||||||
viewer.clock.stopTime = stopTime.clone()
|
viewer.clock.stopTime = stopTime.clone()
|
||||||
viewer.clock.currentTime = startTime.clone()
|
viewer.clock.currentTime = startTime.clone()
|
||||||
viewer.clock.clockRange = Cesium.ClockRange.UNBOUNDED // 允许时间继续前进,支持不同时长动画独立完成
|
viewer.clock.clockRange = Cesium.ClockRange.CLAMPED // 动画到达终点后停止,不循环
|
||||||
viewer.clock.multiplier = 1
|
viewer.clock.multiplier = 1
|
||||||
viewer.clock.shouldAnimate = true
|
viewer.clock.shouldAnimate = true
|
||||||
|
|
||||||
@ -633,15 +627,7 @@ export function useEntityAnimation() {
|
|||||||
|
|
||||||
// 监听时钟,检测动画完成
|
// 监听时钟,检测动画完成
|
||||||
const clockListener = viewer.clock.onTick.addEventListener((clock) => {
|
const clockListener = viewer.clock.onTick.addEventListener((clock) => {
|
||||||
checkAnimationsCompletion(viewer, clock.currentTime, onEntityComplete)
|
checkAnimationsCompletion(viewer, clock.currentTime)
|
||||||
|
|
||||||
// 注释掉自动停止时钟的逻辑,让时钟继续运行
|
|
||||||
// 原因:时钟停止会导致 Cesium 停止更新实体位置,导致前面完成的实体消失
|
|
||||||
// if (areAllAnimationsCompleted()) {
|
|
||||||
// console.log('[useEntityAnimation] 所有动画已完成,停止时钟')
|
|
||||||
// viewer.clock.shouldAnimate = false
|
|
||||||
// clockListener() // 移除监听器
|
|
||||||
// }
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听动画结束
|
// 监听动画结束
|
||||||
@ -658,53 +644,31 @@ export function useEntityAnimation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查动画是否完成,如果完成则停止脉冲并清除路线
|
* 检查动画是否完成,如果完成则停止脉冲
|
||||||
* @param {Cesium.Viewer} viewer
|
* @param {Cesium.Viewer} viewer
|
||||||
* @param {Cesium.JulianDate} currentTime
|
* @param {Cesium.JulianDate} currentTime
|
||||||
* @param {Function} onEntityComplete - 完成回调 (routeId) => void
|
|
||||||
*/
|
*/
|
||||||
const checkAnimationsCompletion = (viewer, currentTime, onEntityComplete) => {
|
const checkAnimationsCompletion = (viewer, currentTime) => {
|
||||||
animatedEntities.value.forEach(entity => {
|
animatedEntities.value.forEach(entity => {
|
||||||
if (!entity || !entity.properties) return
|
if (!entity || !entity.properties) return
|
||||||
|
|
||||||
const scaleState = entity.properties.scaleState?.getValue()
|
const scaleState = entity.properties.scaleState?.getValue()
|
||||||
const startTime = entity.properties.animationStartTime?.getValue()
|
const startTime = entity.properties.animationStartTime?.getValue()
|
||||||
const duration = entity.properties.animationDuration?.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)
|
const elapsed = Cesium.JulianDate.secondsDifference(currentTime, startTime)
|
||||||
|
|
||||||
// 如果动画已完成(留0.5秒容差)
|
// 如果动画已完成(留0.5秒容差)
|
||||||
if (elapsed >= duration - 0.5) {
|
if (elapsed >= duration - 0.5) {
|
||||||
const entityName = entity.properties.name?.getValue() || '未知实体'
|
console.log(`[useEntityAnimation] 实体 ${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. 停止脉冲效果
|
|
||||||
scaleState.isPulsing = false
|
scaleState.isPulsing = false
|
||||||
scaleState.fixedScale = 1.0
|
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 回调不存在或不是函数`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -729,19 +693,6 @@ export function useEntityAnimation() {
|
|||||||
console.log('[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 {
|
return {
|
||||||
animatedEntity,
|
animatedEntity,
|
||||||
animatedEntities,
|
animatedEntities,
|
||||||
|
|||||||
@ -65,7 +65,7 @@ export function useRouteVisualization() {
|
|||||||
polyline: {
|
polyline: {
|
||||||
positions: positions,
|
positions: positions,
|
||||||
width: styleOptions.width || style.width,
|
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,
|
clampToGround: true,
|
||||||
classificationType: Cesium.ClassificationType.TERRAIN
|
classificationType: Cesium.ClassificationType.TERRAIN
|
||||||
},
|
},
|
||||||
@ -82,7 +82,7 @@ export function useRouteVisualization() {
|
|||||||
|
|
||||||
routeEntities.value.push(entity)
|
routeEntities.value.push(entity)
|
||||||
|
|
||||||
console.log(`[useRouteVisualization] 绘制路线: ${routeData.name} (${type}), routeId: ${routeData.id}`)
|
console.log(`[useRouteVisualization] 绘制路线: ${routeData.name} (${type})`)
|
||||||
|
|
||||||
return entity
|
return entity
|
||||||
}
|
}
|
||||||
@ -224,52 +224,27 @@ export function useRouteVisualization() {
|
|||||||
* @param {string} routeId - 路线 ID
|
* @param {string} routeId - 路线 ID
|
||||||
*/
|
*/
|
||||||
const clearRoute = (viewer, routeId) => {
|
const clearRoute = (viewer, routeId) => {
|
||||||
if (!viewer || !routeId) {
|
if (!viewer || !routeId) return
|
||||||
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
|
|
||||||
|
|
||||||
// 清除路线实体
|
// 清除路线实体
|
||||||
routeEntities.value = routeEntities.value.filter(entity => {
|
routeEntities.value = routeEntities.value.filter(entity => {
|
||||||
if (entity && entity.properties) {
|
if (entity && entity.properties && entity.properties.routeId === routeId) {
|
||||||
// 关键修复:使用 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)
|
viewer.entities.remove(entity)
|
||||||
removedRoutes++
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
// 清除标签实体
|
// 清除标签实体
|
||||||
labelEntities.value = labelEntities.value.filter(entity => {
|
labelEntities.value = labelEntities.value.filter(entity => {
|
||||||
if (entity && entity.properties) {
|
if (entity && entity.properties && entity.properties.routeId === routeId) {
|
||||||
// 关键修复:使用 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)
|
viewer.entities.remove(entity)
|
||||||
removedLabels++
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(`[useRouteVisualization] 已清除路线: ${routeId}, 移除了 ${removedRoutes} 条路线和 ${removedLabels} 个标签`)
|
console.log(`[useRouteVisualization] 已清除路线: ${routeId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -11,18 +11,25 @@ import * as Cesium from 'cesium'
|
|||||||
export const ROUTE_STYLES = {
|
export const ROUTE_STYLES = {
|
||||||
// 人员路线样式
|
// 人员路线样式
|
||||||
personnel: {
|
personnel: {
|
||||||
color: Cesium.Color.RED,
|
color: Cesium.Color.CYAN,
|
||||||
width: 5,
|
width: 5,
|
||||||
material: Cesium.Color.RED,
|
material: (viewer) => new Cesium.PolylineGlowMaterialProperty({
|
||||||
labelColor: Cesium.Color.RED
|
glowPower: 0.3,
|
||||||
|
color: Cesium.Color.CYAN
|
||||||
|
}),
|
||||||
|
labelColor: Cesium.Color.CYAN
|
||||||
},
|
},
|
||||||
|
|
||||||
// 装备路线样式
|
// 装备路线样式
|
||||||
equipment: {
|
equipment: {
|
||||||
color: Cesium.Color.RED,
|
color: Cesium.Color.ORANGE,
|
||||||
width: 6,
|
width: 6,
|
||||||
material: Cesium.Color.RED,
|
material: (viewer) => new Cesium.PolylineDashMaterialProperty({
|
||||||
labelColor: Cesium.Color.RED
|
color: Cesium.Color.ORANGE,
|
||||||
|
dashLength: 20.0,
|
||||||
|
dashPattern: 255
|
||||||
|
}),
|
||||||
|
labelColor: Cesium.Color.ORANGE
|
||||||
},
|
},
|
||||||
|
|
||||||
// 降级直线样式(API 失败时使用)
|
// 降级直线样式(API 失败时使用)
|
||||||
@ -58,7 +65,7 @@ export const LABEL_STYLE = {
|
|||||||
*/
|
*/
|
||||||
export const ROUTE_ANIMATION_CONFIG = {
|
export const ROUTE_ANIMATION_CONFIG = {
|
||||||
// 动画速度倍数(相对于真实时间)
|
// 动画速度倍数(相对于真实时间)
|
||||||
speedMultiplier: 40,
|
speedMultiplier: 10,
|
||||||
|
|
||||||
// 最小动画时长(秒)
|
// 最小动画时长(秒)
|
||||||
minDuration: 30,
|
minDuration: 30,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user