1. 实现效果
实现的弹窗效果如下:点击
Entity
弹窗展示相关信息,点击空白处或者点击关闭按钮即可关闭弹窗。
2. 封装一个弹窗组件
这里我们直接将代码贴出来,样式省略:这里实现的关键要点如下:
-
首先,需要监听鼠标点击的位置变化,因此需要监听
position
属性;
-
其次,需要给
Scene
的
preRender
添加一个监听器,以便在渲染场景(Scene)之前改变弹窗的位置,有关
preRender
请看下图,翻译一下就是:获取在
更新场景之后
和
渲染场景之前
将引发的事件。事件的订阅者接收
Scene 实例
作为第一个参数,将
当前时间
作为第二个参数。
-
我们在关闭弹窗后则需要解除上述监听器,具体请看
setPopupUndefined()
。
-
我们通过鼠标点击获取的是笛卡尔坐标,需要将其转换为屏幕坐标,借助
SceneTransforms.worldToWindowCoordinates()
方法即可实现。
<template>
<div class="cs-popup" ref="popupDivRef" v-show="show">
<div class="cs-popup-header">
<div class="cs-popup-title">
{{ title }}
div>
<div class="cs-popup-close">
<i-ep-Close @click="close" />
div>
div>
<div class="cs-popup-content">
<slot>slot>
div>
div>
template>
<script setup>
import { SceneTransforms, defined } from "cesium";
import useCSViewerStore from "@/stores/csViewer.js";
const csViewerStore = useCSViewerStore();
let popupDivRef = ref();
const show = ref(false);
const props = defineProps({
title: {
type: String,
default: "标题",
},
position: {
type: Object,
default: () => {},
},
yOffset: {
type: Number,
default: 10,
},
});
const emits = defineEmits(["close"]);
watch(
() => props.position,
(val) => {
setPosition(val);
},
{ deep: true }
);
onBeforeUnmount(() => {
setPopupUndefined();
});
function setPosition(val) {
if (Object.keys(val).length) {
setPopupPositon();
} else {
setPopupUndefined();
}
}
function setPopupPositon() {
csViewerStore.viewer.scene.preRender.addEventListener(setPositionListener);
}
function setPositionListener() {
const canvasPosition = getCanvasPos(props.position);
if (defined(canvasPosition)) {
if (!show.value) show.value = true;
setPopupDiv(canvasPosition.x, canvasPosition.y - props.yOffset);
}
}
function getCanvasPos(cartesian3Pos) {
return Object.keys(cartesian3Pos).length > 0
? SceneTransforms.worldToWindowCoordinates(
csViewerStore.viewer.scene,
cartesian3Pos
)
: undefined;
}
function setPopupUndefined() {
if (show.value) show.value = false;
csViewerStore.viewer.scene.preRender.removeEventListener(setPositionListener);
}
function setPopupDiv(left, top) {
popupDivRef.value.style.left = left + "px";
popupDivRef.value.style.top = top + "px";
}
function close() {
emits("close");
}
script>
3. 使用弹窗
本文使用天地图作为底图,这里针对添加天地图的方法不再赘述,感兴趣的自己看代码或看往期文章
Cesium 加载天地图
即可。
-
首先,我们在地图组件中添加一个
ScreenSpaceEventType.LEFT_CLICK
事件,然后在父组件中可以具体书写要实现的功能,这里只做一个触发(
emits
)。
代码如下:
function initEvt() {
viewerRef.value.screenSpaceEventHandler.setInputAction((e) => {
emits("leftClick", e);
}, ScreenSpaceEventType.LEFT_CLICK);
}
-
而在父组件我们需要添加一个
Entity
,代码如下:
function addEntities() {
const lngLat = [113.553696883309, 34.8244199026567];
cvs.viewer.entities.add({
name: "我是一个红色的长方体",
position: Cartesian3.fromDegrees(...lngLat),
box: {
dimensions: new Cartesian3(100.0, 100.0, 1000.0),
material: Color.RED,
},
properties: {
lngLat,
},
});
cvs.viewer.zoomTo(cvs.viewer.entities);
}
-
引入
Popup
组件:由于我们在
vite.config.js
中已经配置
components
目录自动引入,这里我们直接使用即可。