项目场景:
需求:项目中使用Cesium
制作三维地图,在地图上铺点,对点位进行点击,弹出自定义弹窗(form
+charts
),且弹窗随地图一起动,思路及代码如下:
思路:
- 地图初始化时隐藏
cesium
自带的弹窗; - 写一个弹窗的
vue
文件(子),在地图vue
文件(父)中引入,通过v-if
控制参数popVisible
为false
默认隐藏,并用一个div(弹窗父元素)包起来; - 地图初始化铺点后给点添加点击事件,获取该点的相关属性(也可再调用接口获取更多弹窗需要数据),并获取点击坐标(如代码中的
handlePinClick
方法); - 弹窗所需数据获取完成之后,找到弹窗父元素,设置其样式:绝对定位,
top
和left
为点击事件获取到的屏幕坐标,同时设置弹窗子组件展示(popVisible=true
);
==================这个时候点击地图的点设置自定义弹窗功能就完成了,但是发现地图移动的时候,地图上的点移动了,弹窗没有移动。接下来解决:
- 在添加点击事件的时候同时添加一个地图监听(如代码中的
bindMapMove
方法),监听地图移动之后对应点击的点的地图坐标,转换成新的屏幕坐标,再重新给弹窗vue
组件设置定位即可。
代码(详解看代码注释):
html代码:
<div
id="cesiumContainer"
class="boxs"
/>
<div id="popup">
<!--
OverlayChart 是引入的弹窗组件,样式自己随便写,也可自己重新命名;
overlayChartObj是传给弹窗的参数,自行进行引入和注册使用,代码中没有涉及到这一块
-->
<OverlayChart
v-if="popVisible"
ref="overlay"
:overlay-chart-obj="overlayChartObj"
/>
</div>
mounted代码:
this.cesiumViewer = this.mapCreate();
this.queryData();
this.gotoCenter();
methods代码:
// 底图初始化
mapCreate() {
var viewer = new Cesium.Viewer("cesiumContainer", {
infoBox: false//隐藏cesium自带的弹窗
});
return viewer;
},
// 回中心点
gotoCenter() {
const height = 1100000;
const viewer=this.cesiumViewer
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(gisServer.center[0], gisServer.center[1], height),
orientation: {
// heading: Cesium.Math.toRadians(0),
// pitch: Cesium.Math.toRadians(-45)
},
duration: 5
});
},
// 获取聚合图层数据
async queryData() {
const emstTypeSub = this.emstTypeSub;
const result = await getEmstWqListByEmstTypeSub({
params: {
emstTypeSub
}
});
this.loading = false;
//加载点位
this.renderCesiumPoint(this.cesiumViewer, result.data.data);
//添加点击事件
this.handlePinClick();
//添加地图移动监听:使地图移动弹窗跟着移动
this.bindMapMove();
},
// 加载点位
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.emstLong, pro.emstLat, 1000),
name: pro.emstName,
id: pro.emstCode,
property: pro,//自己加的相关属性,弹窗里需要用到
billboard: {
scale: 1,
image: "这里是图片地址",
verticalOrigin: Cesium.VerticalOrigin.BOTTOM
},
label: { // 文字标签
text: pro.emstName,
font: "400 30px MicroSoft YaHei", // 15pt monospace
scale: 0.6,
fillColor: "#e4393c",
show: false,
outlineWidth: 3,
outlineColor: "#000",
pixelOffset: Cesium.Cartesian2(0, -60) // 偏移量
}
});
}
},
// 点击地图上的点
handlePinClick() {
const cesiumViewer = this.cesiumViewer
const tt = this;
const handle3D = new Cesium.ScreenSpaceEventHandler(cesiumViewer.scene.canvas);
handle3D.setInputAction((movement) => {
//movement.position 是点击的屏幕坐标
const pick = cesiumViewer.scene.pick(movement.position);
if (!pick) {
return
}
const obj = pick.id.property;
// 解决点击不同点数据不更换问题
if (tt.overlayChartObj.popForm != {}) {
tt.popVisible = false;
tt.overlayChartObj.popForm = {}
}
const coordinate = movement.position;
/*overlayChartObj是传给子组件(弹窗)的数据:
{
popForm:点渲染的时候就有的数据,
chartData:点击调用接口获取的数据
}
*/
tt.overlayChartObj.popForm = obj;
tt.getProjDetailPop(coordinate, obj);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
},
// 点击地图图标展示
async getProjDetailPop(coordinate, params) {
const tt = this;
const result = await getWqgStateLast7Day({
params: {
emstCode: params.emstCode
}
});
tt.showOverlayChart(coordinate, result.data.data);
},
// 显示图表
showOverlayChart (position, data) {
const pop = document.getElementById("popup");
pop.style.position = "absolute";
pop.style.top = position.y + "px";
pop.style.left = position.x + "px";
pop.style.zIndex = 99;
this.overlayChartObj.chartData = data;
this.popVisible = true;
},
// 地图移动时弹窗跟随
bindMapMove() {
const cesiumViewer = this.cesiumViewer
const tt = this
cesiumViewer.scene.preRender.addEventListener(() => { // 一直触发
//如果弹窗没有展示,不进行任何操作,只有弹窗本身展示的时候才进行监听
if (!tt.popVisible) return
const scratch = Cesium.Cartesian2()
const position = Cesium.Cartesian3.fromDegrees(Number(tt.overlayChartObj.popForm.emstLong),
Number(tt.overlayChartObj.popForm.emstLat), 2500)
// cartesianToCanvasCoordinates 转换三维空间坐标到canvas坐标(窗口坐标)
const canvasPosition = cesiumViewer.scene.cartesianToCanvasCoordinates(position, scratch)
if (Cesium.defined(canvasPosition)) {
tt.setPopPosition(canvasPosition)
}
})
},
setPopPosition(position) {
const pop = document.getElementById("popup");
pop.style.top = position.y + "px";
pop.style.left = position.x + "px";
},
总结:
写博客是为了记笔记!