Compare commits
No commits in common. "0dca401b1c9acda368de1e4aabf6bc735dd2e460" and "49e963e196af76f2510b70c4deaa1209f9dea143" have entirely different histories.
0dca401b1c
...
49e963e196
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 2.1 KiB |
@ -108,7 +108,7 @@ const emit = defineEmits(['view-plan', 'start-dispatch'])
|
||||
*/
|
||||
const getLevelText = (level) => {
|
||||
const item = RESPONSE_LEVELS.find(l => l.value === level)
|
||||
return item ? `${item.label}` : '四级'
|
||||
return item ? `${item.label}` : '三级'
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<!-- 触发器 -->
|
||||
<div class="dropdown-trigger" @click="toggleDropdown">
|
||||
<span class="trigger-text"
|
||||
>距离灾害点{{ forcePreset.searchRadius - 20 }}km范围内</span
|
||||
>距离灾害点{{ forcePreset.searchRadius }}km范围内</span
|
||||
>
|
||||
<div class="trigger-icon" :class="{ 'is-open': isDropdownOpen }">
|
||||
<img
|
||||
@ -63,8 +63,6 @@
|
||||
<div class="stat-group">
|
||||
<div class="stat-card stat-card--base">
|
||||
<span class="stat-label flexBox"
|
||||
@click="handleJump"
|
||||
|
||||
>应急基地与预置点
|
||||
|
||||
<img
|
||||
@ -135,28 +133,14 @@ import { ref, inject } from "vue";
|
||||
const { forcePreset } = inject("disasterData");
|
||||
const onDistanceChange = inject("onDistanceChange");
|
||||
|
||||
const viewer = inject('cesiumViewer')
|
||||
const triggerJump = inject('triggerJump')
|
||||
// 触发跳动
|
||||
const handleJump = () => {
|
||||
console.log('viewer',viewer.value);
|
||||
console.log('triggerJump',triggerJump);
|
||||
|
||||
|
||||
if (viewer.value && triggerJump) {
|
||||
|
||||
triggerJump(5, 10) // 触发5秒跳动,高度10像素
|
||||
}
|
||||
}
|
||||
|
||||
// 下拉框状态
|
||||
const isDropdownOpen = ref(false);
|
||||
|
||||
// 距离选项
|
||||
const distanceOptions = [
|
||||
// { value: 10, label: "距离灾害点10km范围内" },
|
||||
{ value: 30, label: "距离灾害点10km范围内" },
|
||||
{ value: 50, label: "距离灾害点30km范围内" },
|
||||
{ value: 30, label: "距离灾害点30km范围内" },
|
||||
{ value: 50, label: "距离灾害点50km范围内" },
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@ -18,8 +18,7 @@
|
||||
<span
|
||||
class="info-source"
|
||||
:style="{ color: getSourceColor(info.source) }"
|
||||
>[{{ info.source }}]</span
|
||||
>
|
||||
>[{{ info.source }}]</span>
|
||||
<span class="info-content">{{ info.content }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -27,61 +26,55 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, ref, computed } from "vue";
|
||||
import { inject, ref, computed } from 'vue'
|
||||
|
||||
const { collaborationInfo } = inject("disasterData");
|
||||
const { collaborationInfo } = inject('disasterData')
|
||||
|
||||
// 折叠状态
|
||||
const isCollapsed = ref(false);
|
||||
const isCollapsed = ref(false)
|
||||
|
||||
// 切换折叠状态
|
||||
const toggleCollapse = () => {
|
||||
isCollapsed.value = !isCollapsed.value;
|
||||
};
|
||||
isCollapsed.value = !isCollapsed.value
|
||||
}
|
||||
|
||||
// 根据折叠状态显示的信息
|
||||
const displayedInfo = computed(() => {
|
||||
if (isCollapsed.value && collaborationInfo.value.length > 0) {
|
||||
// 折叠时只显示第一条
|
||||
return [collaborationInfo.value[0]];
|
||||
return [collaborationInfo.value[0]]
|
||||
}
|
||||
// 展开时显示全部
|
||||
return collaborationInfo.value;
|
||||
});
|
||||
return collaborationInfo.value
|
||||
})
|
||||
|
||||
// 根据信息源返回对应的颜色
|
||||
const getSourceColor = (source) => {
|
||||
const colorMap = {
|
||||
气象预警: "#4A9EFF", // 蓝色
|
||||
气象部门: "#4A9EFF", // 蓝色
|
||||
公安部门: "#FF5555", // 红色
|
||||
交警部门: "#FF5555", // 红色
|
||||
应急管理: "#FF5555", // 红色
|
||||
融媒体中心: "#00D68F", // 绿色
|
||||
新闻中心: "#00D68F", // 绿色
|
||||
宣传部门: "#00D68F", // 绿色
|
||||
};
|
||||
'气象预警': '#4A9EFF', // 蓝色
|
||||
'气象部门': '#4A9EFF', // 蓝色
|
||||
'公安部门': '#FF5555', // 红色
|
||||
'交警部门': '#FF5555', // 红色
|
||||
'应急管理': '#FF5555', // 红色
|
||||
'融媒体中心': '#00D68F', // 绿色
|
||||
'新闻中心': '#00D68F', // 绿色
|
||||
'宣传部门': '#00D68F' // 绿色
|
||||
}
|
||||
|
||||
return colorMap[source] || "#4A9EFF"; // 默认蓝色
|
||||
};
|
||||
return colorMap[source] || '#4A9EFF' // 默认蓝色
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/styles/mixins.scss" as *;
|
||||
@use "../../assets/styles/common.scss" as *;
|
||||
@use '@/styles/mixins.scss' as *;
|
||||
@use '../../assets/styles/common.scss' as *;
|
||||
|
||||
.collaboration-info {
|
||||
position: relative;
|
||||
margin-top: vh(12);
|
||||
background: url("../../assets/images/信息面板bg.png") no-repeat center center;
|
||||
background: url('../../assets/images/信息面板bg.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
padding: 2px;
|
||||
border: 2px solid transparent;
|
||||
&:hover {
|
||||
border: 2px solid rgba(255, 80, 80, 0.6);
|
||||
box-shadow: 0 0 10px rgba(255, 80, 80, 0.6), 0 0 20px rgba(255, 0, 0, 0.4),
|
||||
0 0 35px rgba(255, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
&__header {
|
||||
position: absolute;
|
||||
@ -138,7 +131,7 @@ const getSourceColor = (source) => {
|
||||
font-size: fs(13);
|
||||
font-family: SourceHanSansCN-Bold, sans-serif;
|
||||
font-weight: 700;
|
||||
margin-right: vw(4); // 信息源和内容之间的小间距
|
||||
margin-right: vw(4); // 信息源和内容之间的小间距
|
||||
}
|
||||
|
||||
.info-content {
|
||||
|
||||
@ -143,14 +143,6 @@ onUnmounted(() => {
|
||||
// border-radius: vw(8);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
|
||||
&:hover {
|
||||
border: 2px solid rgba(255, 80, 80, 0.6);
|
||||
box-shadow: 0 0 10px rgba(255, 80, 80, 0.6),
|
||||
0 0 20px rgba(255, 0, 0, 0.4),
|
||||
0 0 35px rgba(255, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
// 收起状态:只显示标题栏
|
||||
&.is-collapsed {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<section
|
||||
class="collapsible-panel"
|
||||
:class="{ 'collapsible-panel--collapsed': isCollapsed }"
|
||||
>
|
||||
<div class="collapsible-panel__header" @click="handleHeaderClick">
|
||||
<section class="collapsible-panel" :class="{ 'collapsible-panel--collapsed': isCollapsed }">
|
||||
<div
|
||||
class="collapsible-panel__header"
|
||||
@click="handleHeaderClick"
|
||||
>
|
||||
<PanelHeader :title="title" :subtitle="subtitle">
|
||||
<template #title-icon>
|
||||
<slot name="title-icon" />
|
||||
@ -48,20 +48,20 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import PanelHeader from "./PanelHeader.vue";
|
||||
import toggleIcon from "../../assets/images/展开icon.png";
|
||||
import { computed, ref } from 'vue'
|
||||
import PanelHeader from './PanelHeader.vue'
|
||||
import toggleIcon from '../../assets/images/展开icon.png'
|
||||
|
||||
const props = defineProps({
|
||||
/** 面板标题 */
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
required: true
|
||||
},
|
||||
/** 面板副标题 */
|
||||
subtitle: {
|
||||
type: String,
|
||||
default: "",
|
||||
default: ''
|
||||
},
|
||||
/**
|
||||
* v-model:collapsed 控制收起状态
|
||||
@ -70,98 +70,97 @@ const props = defineProps({
|
||||
*/
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
default: undefined,
|
||||
default: undefined
|
||||
},
|
||||
/** 是否可收缩 */
|
||||
collapsible: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
/** 初始是否收起(仅在非受控模式下生效) */
|
||||
startCollapsed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(["update:collapsed", "toggle", "title-click"]);
|
||||
const emit = defineEmits(['update:collapsed', 'toggle', 'title-click'])
|
||||
|
||||
// 判断是否受控组件
|
||||
const isControlled = computed(() => props.collapsed !== undefined);
|
||||
const isControlled = computed(() => props.collapsed !== undefined)
|
||||
|
||||
// 内部收起状态
|
||||
const internalCollapsed = ref(!!props.startCollapsed);
|
||||
const internalCollapsed = ref(!!props.startCollapsed)
|
||||
|
||||
// 计算当前收起状态
|
||||
const isCollapsed = computed({
|
||||
get() {
|
||||
return isControlled.value ? !!props.collapsed : internalCollapsed.value;
|
||||
return isControlled.value ? !!props.collapsed : internalCollapsed.value
|
||||
},
|
||||
set(value) {
|
||||
if (isControlled.value) {
|
||||
emit("update:collapsed", value);
|
||||
emit('update:collapsed', value)
|
||||
} else {
|
||||
internalCollapsed.value = value;
|
||||
internalCollapsed.value = value
|
||||
}
|
||||
emit("toggle", value);
|
||||
},
|
||||
});
|
||||
emit('toggle', value)
|
||||
}
|
||||
})
|
||||
|
||||
// 处理标题点击事件
|
||||
function handleHeaderClick() {
|
||||
emit("title-click");
|
||||
emit('title-click')
|
||||
}
|
||||
|
||||
// 切换收起/展开
|
||||
function toggle() {
|
||||
if (!props.collapsible) return;
|
||||
isCollapsed.value = !isCollapsed.value;
|
||||
if (!props.collapsible) return
|
||||
isCollapsed.value = !isCollapsed.value
|
||||
}
|
||||
|
||||
// 改进的过渡动画钩子
|
||||
function onBeforeEnter(el) {
|
||||
el.style.height = "0";
|
||||
el.style.overflow = "hidden";
|
||||
el.style.height = '0'
|
||||
el.style.overflow = 'hidden'
|
||||
}
|
||||
|
||||
function onEnter(el) {
|
||||
// 使用 requestAnimationFrame 确保动画流畅
|
||||
requestAnimationFrame(() => {
|
||||
el.style.height = `${el.scrollHeight}px`;
|
||||
});
|
||||
el.style.height = `${el.scrollHeight}px`
|
||||
})
|
||||
}
|
||||
|
||||
function onAfterEnter(el) {
|
||||
el.style.height = "";
|
||||
el.style.overflow = "";
|
||||
el.style.height = ''
|
||||
el.style.overflow = ''
|
||||
}
|
||||
|
||||
function onBeforeLeave(el) {
|
||||
el.style.height = `${el.scrollHeight}px`;
|
||||
el.style.overflow = "hidden";
|
||||
el.style.height = `${el.scrollHeight}px`
|
||||
el.style.overflow = 'hidden'
|
||||
}
|
||||
|
||||
function onLeave(el) {
|
||||
// 强制重排以确保动画触发
|
||||
el.offsetHeight; // eslint-disable-line no-unused-expressions
|
||||
el.style.height = "0";
|
||||
el.offsetHeight // eslint-disable-line no-unused-expressions
|
||||
el.style.height = '0'
|
||||
}
|
||||
|
||||
function onAfterLeave(el) {
|
||||
el.style.height = "";
|
||||
el.style.overflow = "";
|
||||
el.style.height = ''
|
||||
el.style.overflow = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/styles/mixins.scss" as *;
|
||||
@use "../../assets/styles/common.scss" as *;
|
||||
@use '@/styles/mixins.scss' as *;
|
||||
@use '../../assets/styles/common.scss' as *;
|
||||
|
||||
.collapsible-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: vh(17);
|
||||
border: 2px solid transparent; /* 防止 hover 时抖动 */
|
||||
margin-bottom: vh(20);
|
||||
|
||||
&__header {
|
||||
position: relative;
|
||||
@ -213,24 +212,24 @@ function onAfterLeave(el) {
|
||||
|
||||
// 使用伪元素作为背景层,九宫格实现
|
||||
&::before {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-style: solid;
|
||||
border-width: vh(25) vw(30);
|
||||
border-image-source: url("../../assets/images/通用卡片bg.png");
|
||||
border-image-source: url('../../assets/images/通用卡片bg.png');
|
||||
border-image-slice: 25 30 25 30 fill;
|
||||
border-image-width: vh(25) vw(30);
|
||||
border-image-repeat: stretch;
|
||||
box-sizing: border-box;
|
||||
pointer-events: none; // 不阻挡鼠标事件
|
||||
z-index: -1; // 放在内容下方
|
||||
pointer-events: none; // 不阻挡鼠标事件
|
||||
z-index: -1; // 放在内容下方
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
position: relative;
|
||||
padding: vh(5) vw(20) vh(5); // 留出空间避免被圆角遮挡
|
||||
padding: vh(5) vw(20) vh(5); // 留出空间避免被圆角遮挡
|
||||
z-index: 1;
|
||||
|
||||
// 如果内容需要滚动
|
||||
@ -245,14 +244,8 @@ function onAfterLeave(el) {
|
||||
&--collapsed {
|
||||
.collapsible-panel__body {
|
||||
height: 0;
|
||||
border-width: 0; // 折叠时移除 border
|
||||
border-width: 0; // 折叠时移除 border
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.collapsible-panel:hover {
|
||||
border: 2px solid rgba(255, 80, 80, 0.6); /* 柔和的渐变红基色 */
|
||||
box-shadow: 0 0 10px rgba(255, 80, 80, 0.6), 0 0 20px rgba(255, 0, 0, 0.4),
|
||||
0 0 35px rgba(255, 0, 0, 0.25); /* 多层柔光渐变效果 */
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -7,11 +7,11 @@ export function useDisasterData() {
|
||||
// 灾害基本信息
|
||||
const disasterInfo = ref({
|
||||
type: '边坡垮塌',
|
||||
volume: '1220',
|
||||
volume: '10022',
|
||||
volumeUnit: 'm³',
|
||||
length: '33',
|
||||
length: '13',
|
||||
lengthUnit: 'm',
|
||||
width: '15',
|
||||
width: '5',
|
||||
widthUnit: 'm',
|
||||
casualties: '0',
|
||||
vehicles: '0',
|
||||
@ -43,7 +43,7 @@ export function useDisasterData() {
|
||||
|
||||
// 力量调度信息
|
||||
const forceDispatch = ref({
|
||||
responseLevel: 4, // 四级
|
||||
responseLevel: 3, // 三级
|
||||
estimatedClearTime: getCurrentDateTime('24:00'),
|
||||
plan: {
|
||||
name: '智能应急方案',
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { ref, onUnmounted } from 'vue'
|
||||
import * as Cesium from 'cesium'
|
||||
import { use3DTiles } from './use3DTiles'
|
||||
import { useMapMarkers } from '../composables/useMapMarkers'
|
||||
|
||||
/**
|
||||
* 双地图对比模式
|
||||
@ -30,8 +29,6 @@ export function useDualMapCompare() {
|
||||
|
||||
const { load3DTileset } = use3DTiles()
|
||||
|
||||
const { drawCollapseBoundaryLeft } = useMapMarkers()
|
||||
|
||||
/**
|
||||
* 初始化左侧Viewer(灾前场景)
|
||||
* @param {HTMLElement} container - 容器元素
|
||||
@ -602,13 +599,6 @@ export function useDualMapCompare() {
|
||||
}
|
||||
|
||||
await enableCompareMode(rightViewerInstance, options)
|
||||
const leftContainer = document.getElementById('leftCesiumContainer')
|
||||
if (leftContainer) {
|
||||
console.log('[index.vue] 绘制左侧坍塌边界红色虚线...')
|
||||
drawCollapseBoundaryLeft(leftContainer)
|
||||
|
||||
console.log('[index.vue] 左侧坍塌边界红色虚线已添加')
|
||||
}
|
||||
} else {
|
||||
disableCompareMode()
|
||||
}
|
||||
|
||||
@ -107,12 +107,11 @@ export function useMapMarkers() {
|
||||
const resolveBillboardHeightReference = (samplingSucceeded) => {
|
||||
// 统一使用 CLAMP_TO_GROUND 让 Cesium 自动贴地
|
||||
// 配合禁用的 3D Tiles 动态细化,标记位置会保持稳定
|
||||
// return Cesium.HeightReference.CLAMP_TO_GROUND
|
||||
return Cesium.HeightReference.RELATIVE_TO_GROUND
|
||||
return Cesium.HeightReference.CLAMP_TO_GROUND
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制坍塌边界红色实线(右侧地图)
|
||||
* 绘制坍塌边界蓝色实线(右侧地图)
|
||||
* 在右侧地图上显示灾后坍塌的边界轮廓
|
||||
* @param {Cesium.Viewer} viewer
|
||||
* @returns {Cesium.Entity | null} 返回创建的边界线实体
|
||||
@ -144,12 +143,12 @@ export function useMapMarkers() {
|
||||
|
||||
console.log('[useMapMarkers] 右侧坍塌边界点位数量:', positions.length)
|
||||
|
||||
// 创建红色实线边界
|
||||
// 创建蓝色实线边界
|
||||
const boundaryEntity = viewer.entities.add({
|
||||
polyline: {
|
||||
positions: positions,
|
||||
width: 4, // 增加线宽,更明显
|
||||
material: Cesium.Color.fromCssColorString('#FF0000').withAlpha(1.0), // 纯红色实线
|
||||
material: Cesium.Color.fromCssColorString('#1CA1FF').withAlpha(1.0), // 纯蓝色实线
|
||||
clampToGround: true,
|
||||
classificationType: Cesium.ClassificationType.BOTH, // 同时分类到地形和3D Tiles
|
||||
zIndex: 1 // 提高层级
|
||||
@ -162,7 +161,7 @@ export function useMapMarkers() {
|
||||
})
|
||||
|
||||
collapseBoundaryEntity.value = boundaryEntity
|
||||
console.log('[useMapMarkers] 右侧坍塌边界红色实线绘制完成, entityId:', boundaryEntity.id)
|
||||
console.log('[useMapMarkers] 右侧坍塌边界蓝色实线绘制完成, entityId:', boundaryEntity.id)
|
||||
|
||||
// 强制渲染场景
|
||||
viewer.scene.requestRender()
|
||||
@ -216,7 +215,7 @@ export function useMapMarkers() {
|
||||
positions: positions,
|
||||
width: 4,
|
||||
material: new Cesium.PolylineDashMaterialProperty({
|
||||
color: Cesium.Color.fromCssColorString('#FF0000').withAlpha(1.0), // 红色
|
||||
color: Cesium.Color.fromCssColorString('#1CA1FF').withAlpha(1.0), // 蓝色
|
||||
dashLength: 16.0, // 虚线段长度
|
||||
dashPattern: 255.0 // 虚线模式
|
||||
}),
|
||||
@ -579,18 +578,19 @@ export function useMapMarkers() {
|
||||
},
|
||||
properties: isSoldier
|
||||
? {
|
||||
type: 'soldier',
|
||||
name: soldierNames[Math.floor(Math.random() * soldierNames.length)],
|
||||
department: soldierDepartments[Math.floor(Math.random() * soldierDepartments.length)],
|
||||
location: `目前为止距离现场${locationDistance}公里`
|
||||
}
|
||||
type: 'soldier',
|
||||
name: soldierNames[Math.floor(Math.random() * soldierNames.length)],
|
||||
department: soldierDepartments[Math.floor(Math.random() * soldierDepartments.length)],
|
||||
location: `目前为止距离现场${locationDistance}公里`
|
||||
}
|
||||
: {
|
||||
type: 'device',
|
||||
name: deviceNames[Math.floor(Math.random() * deviceNames.length)],
|
||||
deviceType: deviceTypes[Math.floor(Math.random() * deviceTypes.length)],
|
||||
location: `目前为止距离现场${locationDistance}公里`
|
||||
}
|
||||
type: 'device',
|
||||
name: deviceNames[Math.floor(Math.random() * deviceNames.length)],
|
||||
deviceType: deviceTypes[Math.floor(Math.random() * deviceTypes.length)],
|
||||
location: `目前为止距离现场${locationDistance}公里`
|
||||
}
|
||||
})
|
||||
|
||||
entities.push(entity)
|
||||
}
|
||||
|
||||
@ -849,7 +849,6 @@ export function useMapMarkers() {
|
||||
icon = otherEmergencyIcon
|
||||
type = 'other'
|
||||
levelName = '其他'
|
||||
// return;
|
||||
}
|
||||
|
||||
console.log(`[useMapMarkers] 添加标记: ${item.gl1Yjllmc}, 级别: ${levelString} (${levelName})`)
|
||||
@ -858,7 +857,7 @@ export function useMapMarkers() {
|
||||
position: result.position,
|
||||
billboard: {
|
||||
image: icon,
|
||||
width: 32,
|
||||
width: 29,
|
||||
height: 32,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
heightReference: resolveBillboardHeightReference(result.samplingSucceeded),
|
||||
@ -973,96 +972,6 @@ export function useMapMarkers() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 触发实体像素级跳动动画
|
||||
* @param {Cesium.Viewer} viewer
|
||||
* @param {number} duration 动画持续时间(秒)
|
||||
* @param {number} pixelJumpHeight 像素跳动高度
|
||||
*/
|
||||
const triggerJumpAnimation = (viewer, duration = 5, pixelJumpHeight = 30) => {
|
||||
console.log(`[useMapMarkers] 触发像素跳动动画,持续时间 ${duration} 秒,像素高度 ${pixelJumpHeight}`)
|
||||
if (!viewer) {
|
||||
console.warn('[useMapMarkers] triggerJumpAnimation: viewer 为空')
|
||||
return
|
||||
}
|
||||
|
||||
// 收集所有需要动画的实体(包含所有类型)
|
||||
const allEntities = [
|
||||
...markerEntities.value,
|
||||
...emergencyResourceEntities.value,
|
||||
...reserveCenterEntities.value
|
||||
].filter(e => e.billboard) // 只需要有billboard的实体
|
||||
|
||||
// 存储原始像素偏移量
|
||||
const entitiesData = allEntities.map(entity => ({
|
||||
entity,
|
||||
originalOffset: entity.billboard.pixelOffset
|
||||
? new Cesium.Cartesian2(
|
||||
entity.billboard.pixelOffset.x,
|
||||
entity.billboard.pixelOffset.y
|
||||
)
|
||||
: new Cesium.Cartesian2(0, 0)
|
||||
}))
|
||||
|
||||
// 动画参数
|
||||
const startTime = Date.now()
|
||||
const jumpDuration = 800 // 单次跳动周期(毫秒)
|
||||
let animationActive = true
|
||||
|
||||
// 定义动画函数
|
||||
const animate = () => {
|
||||
if (!animationActive) return
|
||||
|
||||
const elapsed = Date.now() - startTime
|
||||
const progress = (elapsed % jumpDuration) / jumpDuration
|
||||
|
||||
// 使用正弦波计算当前像素偏移
|
||||
const currentOffset = Math.sin(progress * Math.PI * 2) * pixelJumpHeight
|
||||
|
||||
// 应用像素偏移到所有实体
|
||||
entitiesData.forEach(({ entity, originalOffset }) => {
|
||||
try {
|
||||
entity.billboard.pixelOffset = new Cesium.Cartesian2(
|
||||
originalOffset.x,
|
||||
originalOffset.y - currentOffset // Y轴负方向为上移
|
||||
)
|
||||
} catch (error) {
|
||||
console.warn('[useMapMarkers] 更新实体像素偏移失败:', error)
|
||||
}
|
||||
})
|
||||
|
||||
// 继续动画直到持续时间结束
|
||||
if (elapsed < duration * 1000) {
|
||||
requestAnimationFrame(animate)
|
||||
} else {
|
||||
// 恢复原始像素偏移
|
||||
entitiesData.forEach(({ entity, originalOffset }) => {
|
||||
entity.billboard.pixelOffset = originalOffset
|
||||
})
|
||||
animationActive = false
|
||||
console.log('[useMapMarkers] 像素跳动动画结束')
|
||||
}
|
||||
}
|
||||
|
||||
// 启动动画
|
||||
animate()
|
||||
|
||||
// 提供手动停止方法
|
||||
return () => {
|
||||
if (animationActive) {
|
||||
animationActive = false
|
||||
entitiesData.forEach(({ entity, originalOffset }) => {
|
||||
entity.billboard.pixelOffset = originalOffset
|
||||
})
|
||||
console.log('[useMapMarkers] 像素跳动动画已手动停止')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return {
|
||||
collapseAreaEntities,
|
||||
markerEntities,
|
||||
@ -1087,8 +996,7 @@ export function useMapMarkers() {
|
||||
clearEmergencyResourceMarkers,
|
||||
addReserveCenterMarkers,
|
||||
clearReserveCenterMarkers,
|
||||
getCollapseCenter: calculateCollapseCenter, // 提前获取中心点,不依赖标记初始化
|
||||
triggerJumpAnimation,
|
||||
getCollapseCenter: calculateCollapseCenter // 提前获取中心点,不依赖标记初始化
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -64,20 +64,6 @@ export function useRangeCircle() {
|
||||
outlineWidth: style.outlineWidth,
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
},
|
||||
label: {
|
||||
text: `${radiusKm.toFixed(1) - 20} km`, // 实际是30 / 50 km 临时修改减少20km 后续调整
|
||||
font: 'bold 24px sans-serif',
|
||||
fillColor: Cesium.Color.WHITE,
|
||||
outlineColor: Cesium.Color.BLACK,
|
||||
outlineWidth: 2,
|
||||
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
||||
verticalOrigin: Cesium.VerticalOrigin.CENTER,
|
||||
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
||||
pixelOffset: new Cesium.Cartesian2(0, 0),
|
||||
backgroundColor: Cesium.Color.fromCssColorString('#333').withAlpha(0.7),
|
||||
backgroundPadding: new Cesium.Cartesian2(18, 12),
|
||||
showBackground: true
|
||||
}
|
||||
})
|
||||
|
||||
// 禁用范围圈的鼠标交互,让点击可以穿透到下面的标记点
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user