需求:点击三维地图上的标注展示弹窗,随着地图移动弹窗也需要移动,缩小地图比例要隐藏,反之显示。
效果图如下:
话不多说,直接上代码:
父组件:
<template>
<div id="cesiumContainer" />
<div id="popup">
<OverlayChart
v-if="cesiumGather.popVisible"
:overlay-chart-obj="cesiumGather.overlayChartObj"
/>
</div>
</template>
<script setup>
import * as Cesium from "cesium";
import { reactive, onMounted } from "vue";
import OverlayChart from "../../components/overlayChart.vue";
const cesiumGather = reactive({
popVisible: false,
overlayChartObj: {},
cesiumViewer: null,
});
onMounted(() => {
cesiumGather.cesiumViewer = mapCreate();
mapCesiumData();
});
// 初始化地图
const mapCreate = () => {
// 添加图层
const viewer = new Cesium.Viewer("cesiumContainer", {
geocoder: false, //搜索框
homeButton: false, //home键
animation: false, //动画控件
fullscreenButton: false, //全屏按钮
sceneModePicker: false, //场景模式选择器
timeline: false, //时间轴
navigationHelpButton: false, //导航提示
baseLayerPicker: false, //地图选择器
infoBox: false, //是否显示信息框
scene3DOnly: false, //如果设置为true,则所有几何图形以3D模式绘制以节约GPU资源
selectionIndicator: false, //是否显示选取指示器组件
baselLayerPicker: false, // 将图层选择的控件关掉,才能添加其他影像数据
});
viewer._cesiumWidget._creditContainer.style.display = "none";
return viewer;
};
// 获取聚合数据
const mapCesiumData = () => {
let queryData = [
{ lng: 104.023442, lat: 23.44321, name: "标点一" },
{ lng: 134.023442, lat: 27.44321, name: "标点二" },
{ lng: 105.023442, lat: 32.44321, name: "标点三" },
{ lng: 104.023442, lat: 23.44221, name: "标点四" },
{ lng: 105.023442, lat: 13.44321, name: "标点五" },
{ lng: 114.023442, lat: 33.44321, name: "标点六" },
{ lng: 124.023442, lat: 43.42321, name: "标点七" },
{ lng: 134.023442, lat: 33.42321, name: "标点八" },
{ lng: 144.023442, lat: 53.14321, name: "标点九" },
{ lng: 101.023442, lat: 23.44321, name: "标点十" },
];
// 加载点位
renderCesiumPoint(cesiumGather.cesiumViewer, queryData);
// 添加点击事件
handlePinClick();
};
// 加载点位
const renderCesiumPoint = (cesiumViewer, projectList) => {
cesiumViewer.entities.removeAll();
for (let i = 0; i < projectList.length; i++) {
const pro = projectList[i];
cesiumViewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(pro.lng, pro.lat, 1000),
name: pro.name,
property: pro, //自己加的相关属性,弹窗里需要用到
billboard: {
//图标
image: "./static/images/markers/4.png",
scale: 1, //图标比例
width: 36,
height: 36,
// 垂直方向
verticalOrigin: Cesium.VerticalOrigin.BASELINE,
//水平方向
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
label: {
// 文字标签
text: pro.name,
scale: 1,
font: "16px monospace",
fillColor: Cesium.Color.WHITE,
showBackground: true, //设置背景颜色
pixelOffset: new Cesium.Cartesian2(0, -44), //设置左右、上下移动
// 垂直方向
verticalOrigin: Cesium.VerticalOrigin.BASELINE,
//水平方向
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
});
}
};
// 点击地图上的点
const handlePinClick = () => {
const cesiumViewer = cesiumGather.cesiumViewer;
const handle3D = new Cesium.ScreenSpaceEventHandler(
cesiumViewer.scene.canvas
);
handle3D.setInputAction((movement) => {
const pick = cesiumViewer.scene.pick(movement.position);
console.log(pick);
if (!pick) {
cesiumGather.popVisible = false;
// 清除监听事件
cesiumViewer.scene.postRender.removeEventListener(infoWindowPostRender);
return;
}
const obj = pick.id.property;
const coordinate = movement.position;
// 解决点击不同点数据不更换问题
if (cesiumGather.overlayChartObj != {}) {
cesiumGather.popVisible = false;
cesiumGather.overlayChartObj = {};
}
/*overlayChartObj是传给子组件(弹窗)的数据*/
cesiumGather.overlayChartObj = obj;
showOverlayChart(coordinate);
// 添加地图移动监听:使地图移动弹窗跟着移动
cesiumViewer.scene.postRender.addEventListener(infoWindowPostRender);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
//显示图表
const showOverlayChart = (position) => {
const pop = document.getElementById("popup");
pop.style.position = "absolute";
pop.style.top = position.y - 100 + "px";
pop.style.left = position.x - 120 + "px";
pop.style.zIndex = 99;
cesiumGather.popVisible = true;
};
// 地图移动时弹窗跟随
const infoWindowPostRender = () => {
const cesiumViewer = cesiumGather.cesiumViewer;
//经纬度转为世界坐标
const gisPosition = Cesium.Cartesian3.fromDegrees(
Number(cesiumGather.overlayChartObj.lng),
Number(cesiumGather.overlayChartObj.lat),
2500
);
//转化为屏幕坐标
var windowPosition = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
cesiumViewer.scene,
gisPosition
);
//解决滚动不隐藏问题
const camerPosition = cesiumViewer.camera.position;
let height =
cesiumViewer.scene.globe.ellipsoid.cartesianToCartographic(
camerPosition
).height;
height += cesiumViewer.scene.globe.ellipsoid.maximumRadius;
if (
!(Cesium.Cartesian3.distance(camerPosition, gisPosition) > height) &&
cesiumViewer.camera.positionCartographic.height < 50000000
) {
cesiumGather.popVisible = true;
} else {
cesiumGather.popVisible = false;
}
if (Cesium.defined(windowPosition)) {
setPopPosition(windowPosition);
}
};
const setPopPosition = (position) => {
const pop = document.getElementById("popup");
pop.style.top = position.y - 100 + "px";
pop.style.left = position.x - 120 + "px";
};
</script>
<style scoped></style>
子组件:
<template>
<div class="cesium-popup">
<span>姓名:{{ overlayChartObj.name }}</span><br/>
<span>坐标:X:{{ overlayChartObj.lat }},Y:{{ overlayChartObj.lng }}</span>
</div>
</template>
<script setup>
// props父传子
const props = defineProps({
overlayChartObj: {
type: Object,
default: function () {
return {};
},
},
});
</script>
<style lang="scss" scoped>
.cesium-popup{
width: 240px;
height: 50px;
background-color: #3df;
span{
font-size: 14px;
}
}
</style>
这里的弹窗样式笔者随便弄了一下,你们可以自行发挥,有哪块不懂的随时联系笔者。
借鉴文章:cesium自定义弹窗