当前位置: 首页 > news >正文

cesium 实现批量divpoint气泡,及气泡碰撞测试与自动避让

  • 需求背景
  • 解决效果
  • index.vue

需求背景

需要实现一个上百点批量同时存在的 popup 弹框,为了提高用户体验
1.重叠的弹框,需要隐藏下一层级的 popup
2.为了让用户尽可能看到较全的弹框,需要做弹框的自动避让

解决效果

index.vue

<!--/**
* @author: liuk
* @date: 2024-08-20
* @describe:数值
*/-->
<template><div class="numericalValue-wrap"><teleport to="body"><ul v-show="showTip && item.visible"v-for="(item,index) in listData" :key="index":class="['surveyStation-popup','sectionEntityDom'+index,'section-popup',item.offsetPopupBoxType,item?.levelOverflow >= 0.01 ? 'waterlevel-overflow' : '']":style="{transform: `translate(${item.AABB?.offsetX || 0}px, ${item.AABB?.offsetY ||0}px)`}"><li>名称:<span class="label">{{ index }}</span></li><li>编号:<span class="label">{{ index }}</span></li><li>水位:<span class="num">{{ item.waterLevel }}</span>m<span style="color:red" v-if="item.levelOverflow>= 0.01">{{ item.levelOverflow.toFixed(2) }}</span></li><li>流量:<span class="num">{{ item.flow }}</span> mm</li></ul></teleport></div>
</template><script lang="ts" setup>
import {onMounted, onUnmounted, reactive, toRefs} from "vue";const model = reactive({showTip: true,listData: [],popupPoss: [],curId: "",dialogVisible: false
})
const {showTip, showGrid, popupPoss, listData, curId, dialogVisible} = toRefs(model)onMounted(() => {getlist()viewer.dataSources.add(sectionDatasource);handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);handler.setInputAction(onMouseMove, Cesium.ScreenSpaceEventType.MOUSE_MOVE);handler.setInputAction(onMouseClick, Cesium.ScreenSpaceEventType.LEFT_CLICK);viewer.camera.percentageChanged = 0;viewer.scene.camera.changed.addEventListener(showPopupBox);
})onUnmounted(() => {sectionDatasource.entities.removeAll()handler.destroy()viewer.dataSources.remove(sectionDatasource);viewer.scene.camera.changed.removeEventListener(showPopupBox);
})const getlist = () => {const data = [{"ctr_points_lonlat": [[113.04510386306632,25.748247970488464],[113.04619931039747,25.746722270257674]],},/* ... */]setTimeout(() => {model.listData = data || []model.popupPoss = new Array(data.length).fill("").map(() => ({}))addTip(data)}, 500)
}// 地图逻辑
import {usemapStore} from "@/store/modules/cesiumMap";
import mittBus from "@/utils/mittBus";const sectionDatasource = new Cesium.CustomDataSource("section");const mapStore = usemapStore()
let handler, PreSelEntity
const viewer = mapStore.getCesiumViewer();
const addTip = (data) => {data.forEach(item => {sectionDatasource.entities.add({customType: "sectionEntity",id: item.label,data: item,polyline: {positions: Cesium.Cartesian3.fromDegreesArray(item.ctr_points_lonlat.flat()),material: Cesium.Color.fromCssColorString("yellow").withAlpha(1),width: 5,}})})
}const onMouseMove = (movement) => {if (PreSelEntity) {PreSelEntity.polyline.material = Cesium.Color.fromCssColorString("yellow").withAlpha(1)PreSelEntity = null}const pickedObject = viewer.scene.pick(movement.endPosition);if (!Cesium.defined(pickedObject) || !Cesium.defined(pickedObject.id)) returnconst entity = pickedObject.id;if (!(entity instanceof Cesium.Entity) || entity.customType !== "sectionEntity") returnentity.polyline.material = Cesium.Color.fromCssColorString("red").withAlpha(1)if (entity !== PreSelEntity) PreSelEntity = entity;
}const onMouseClick = (movement) => {const pickedObject = viewer.scene.pick(movement.position);if (!Cesium.defined(pickedObject) || !Cesium.defined(pickedObject.id)) returnconst entity = pickedObject.id;if (!(entity instanceof Cesium.Entity) || entity.customType !== "sectionEntity") returnmodel.curId = entity.id
}const offsetPopupBoxOptions = {top: [-0.5, -1],bottom: [-0.5, 0],right: [0, -0.5],left: [-1, -0.5],
}
const showPopupBox = () => {if (!model.showTip) return// 碰撞检测const {left, top, bottom, right} = viewer.container.getBoundingClientRect()model.listData.forEach(async (item, index) => {const curIndex = model.listData.findIndex(x => x.name === item.name)let width, height, areaif (!item.AABB) {const dom = document.querySelector(`.sectionEntityDom${curIndex}`)width = parseInt(getComputedStyle(dom).width) + 2 * parseInt(getComputedStyle(dom).padding.split(" ")[1])height = parseInt(getComputedStyle(dom).height) + 2 * parseInt(getComputedStyle(dom).padding.split(" ")[0])area = width * heightitem.AABB = {width, height, area: width * height}} else {width = item.AABB.widthheight = item.AABB.heightarea = item.AABB.area}const longitude = (item.ctr_points_lonlat[0][0] + item.ctr_points_lonlat[1][0]) / 2const latitude = (item.ctr_points_lonlat[0][1] + item.ctr_points_lonlat[1][1]) / 2const curPosition = Cesium.Cartesian3.fromDegrees(longitude, latitude, item.heightZ);const {x, y} = viewer.scene.cartesianToCanvasCoordinates(curPosition)if (index === 0) {item.offsetPopupBoxType = "top";item.AABB.offsetX = x + offsetPopupBoxOptions["top"] * widthitem.AABB.offsetY = y + offsetPopupBoxOptions["top"] * height}const offsetPopupBoxKeys = Object.keys(offsetPopupBoxOptions)const toChecks = model.listData.slice(0, index) // 需要测试碰撞的单位offsetPopupBoxKeys.some((type) => {item.offsetPopupBoxType = ""item.AABB.offsetX = x + offsetPopupBoxOptions[type][0] * widthitem.AABB.offsetY = y + offsetPopupBoxOptions[type][1] * heightconst check = toChecks.every(checkItem => {const box1 = checkItem.AABBconst box2 = item.AABBlet intersectionArea = 0 // 相交面积// 计算在每个轴上的重叠部分const overlapX = Math.min(box1.offsetX + box1.width, box2.offsetX + box2.width) - Math.max(box1.offsetX, box2.offsetX);const overlapY = Math.min(box1.offsetY + box1.height, box2.offsetY + box2.height) - Math.max(box1.offsetY, box2.offsetY);// 如果在两个轴上都有重叠,则计算相交区域的面积if (overlapX > 0 && overlapY > 0) intersectionArea = overlapX * overlapY;return intersectionArea <= area * 0.05;});if (check) {item.offsetPopupBoxType = type}return check})switch (true) { // 屏幕边界限制case item.AABB.offsetX + width <= right && item.AABB.offsetX >= left && item.AABB.offsetY >= top && item.AABB.offsetY + height <= bottom:item.visible = !!item.offsetPopupBoxType;breakdefault:item.visible = false;break}model.listData[curIndex] = item})
}
</script><style lang="scss">
.surveyStation-popup {position: fixed;top: 0;left: 0;z-index: 3;margin: 0;padding: 7px 15px;list-style: none;background: rgba(5, 9, 9, 0.6);border-radius: 4px;font-size: 14px;color: #fff;cursor: default;--w: 24px;--h: 10px;&::before {content: "";background-color: rgba(0, 0, 0, 0.7);position: absolute;bottom: 0;left: 50%;width: var(--w);height: var(--h);transform: translate(-50%, 100%) translateY(-0.5px);clip-path: polygon(50% 100%, 0 0, 100% 0);}&.ponint-list::before{display: none;}&.map2d {margin-left: -15px; // 二维图片底座尺寸大小margin-top: -50px;}&.map3d {margin-left: -15px; // 三维图片底座尺寸大小margin-top: -100px;}.ponint-list-li {cursor: pointer;&:hover {background: rgba(204, 204, 204, .6);}}
}.section-popup {--w: 24px;--h: 10px;width: 150px;height: 80px;margin-top: -10px;&::before {content: "";background-color: rgba(0, 0, 0, 0.7);position: absolute;bottom: 0;left: 50%;width: 24px;height: 10px;transform: translate(-50%, 100%) translateY(-0.5px);clip-path: polygon(50% 100%, 0 0, 100% 0);}&.top {margin-top: calc(var(--h) * -1);&::before {top: auto;bottom: 0;right: auto;left: 50%;width: var(--w);height: var(--h);transform: translate(-50%, 100%) translateY(-0.5px);clip-path: polygon(50% 100%, 0 0, 100% 0);}}&.bottom {margin-top: var(--h);&::before {top: 0;bottom: auto;right: auto;left: 50%;width: var(--w);height: var(--h);transform: translate(-50%, -100%) translateY(0.5px);clip-path: polygon(50% 0, 0 100%, 100% 100%);}}&.right {margin-left: var(--h);&::before {top: 50%;bottom: auto;right: auto;left: 0;width: var(--h);height: var(--w);transform: translate(-100%, -50%) translateX(0.5px);clip-path: polygon(100% 0, 0 50%, 100% 100%);}}&.left {margin-left: calc(var(--h) * -1);&::before {top: 50%;bottom: auto;right: 0;left: auto;width: var(--h);height: var(--w);transform: translate(100%, -50%) translateX(-0.5px);clip-path: polygon(0 100%, 0 0, 100% 50%);}}&.waterlevel-overflow {animation: dm-yj-breathe 800ms ease-in-out infinite;animation-direction: alternate;}.label {color: #00ff00;}.num {color: orange;}
}
</style>

http://www.mrgr.cn/news/4898.html

相关文章:

  • 【Kubernetes】k8s集群之包管理器Helm
  • Linux驱动学习之点灯(五,设备树没用平台设备总线)
  • 基于STM32开发的智能家居照明控制系统
  • ansible模块+playbook
  • C++STL初阶(12):stack和queue的初阶实现
  • leetcode-494. 目标和
  • 【LeetCode:3】无重复字符串的最长子串(Java)
  • 【精品实战项目】深度学习预测、深度强化学习优化、附源码数据手把手教学
  • 设计模式的七大原则
  • 06 Oracle数据是怎么存储的
  • 数据库机器上停service360safe
  • BUG——imx6u开发_结构体导致的死机问题(未解决)
  • 动态规划(二)
  • 03:电容的充放电特性及应用举例
  • VMware vSphere Replication 虚拟机备份及迁移实践
  • LeetCode面试题Day13|LC383 赎金信、LC290 单词规律
  • Java数组07:稀疏数组
  • 算法-矩阵置零(73)
  • C++ //练习 17.14 编写几个正则表达式,分别触发不同错误。运行你的程序,观察编译器对每个错误的输出。
  • 使用 Vue 2 搭建大屏可视化系统