实现自定义位置点广告牌的显示后,如果需要展示位置点的一些信息,就需要通过鼠标事件来触发自定义弹框的显示。下面通过鼠标左击和移入移出控制弹框的显示与隐藏。
1、鼠标左击LEFT_CLICK触发弹框显示,可参考ScreenSpaceEventHandler - Cesium Documentation
viewer.screenSpaceEventHandler.setInputAction(click => {
const pickedEntity = viewer.scene.pick(click.position)
this.selectedEntity = pickedEntity?.id
// 判断点击物体是否为图标实体
if (Cesium.defined(pickedEntity)) {
this.dialogVisible = true // 显示弹窗
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
2、鼠标移入移出MOUSE_MOVE时触发弹框显示与隐藏:
viewer.screenSpaceEventHandler.setInputAction(movement => {
const pickedEntity = viewer.scene.pick(movement.endPosition);
this.selectedEntity = pickedEntity?.id;
// 判断移入物体是否为图标实体
if (Cesium.defined(pickedEntity) && this.selectedEntity) {
// 鼠标移入图标
this.dialogVisible = true;
} else {
// 鼠标移出图标
this.dialogVisible = false;
this.selectedEntity = null;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
鼠标的事件类型可参考ScreenSpaceEventType - Cesium Documentation
3、问题
有时候弹框会溢出到屏幕外面,导致弹框信息展示不全,因此就需要根据位置点在屏幕中的位置来更新弹框的位置,使得弹框信息不会溢出屏幕;
实现思路:可参考Cesium.js实现显示点位对应的自定义信息弹窗(数据面板)_cesium弹窗-优快云博客
首先,获取当前选择的实体(例如点位或图标)this.selectedEntity在 Cesium 世界坐标系中的位置;
然后,将 3D 世界中的点位转换为 2D 屏幕上的像素位置;
最后,将弹窗的位置设置为计算出的屏幕坐标。
updateDialogPosition () {
// 计算弹窗位置
// 这里获取当前选择的实体(例如点位或图标)在 Cesium 世界坐标系中的位置。
// `this.selectedEntity.position.getValue` 方法根据当前时间返回实体的位置。
const cesiumPosition = this.selectedEntity.position.getValue(
viewer.clock.currentTime
)
// 将 Cesium 世界坐标转换为屏幕坐标。
// 这一步是将 3D 世界中的点位转换为 2D 屏幕上的像素位置。
const canvasPosition = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
viewer.scene,
cesiumPosition
)
// 检查转换是否成功。有时候,如果点位不在当前视图中,则转换可能失败。
if (canvasPosition) {
// 更新弹窗位置
this.dialogPosition = {
x: canvasPosition.x, //距离屏幕上方距离,即top
y: canvasPosition.y, //距离屏幕左边距离,即left
width: document.getElementById('cesiumContainer').offsetWidth,
height: document.getElementById('cesiumContainer').offsetHeight
}
}
},
4、总结
详细完整代码如下:
父组件中的代码
<template>
<div class="visualization">
<div id="cesiumContainer"></div>
<Dialog :position="dialogPosition" :dialogVisible="dialogVisible"
@closeDialog="dialogVisible = false"></Dialog>
</div>
</template>
<script>
import Dialog from './Dialog.vue'
var viewer = null
var tileset = null
var terrainProvider = null
export default {
components:{
Dialog
},
data () {
return {
dialogVisible: false,
dialogPosition: { x: 0, y: 0 }, // 弹窗显示位置
selectedEntity: null, // 选中的实体
}
},
mounted () {
this.initCesium();
},
methods: {
initCesium () {
// Ion是Cesium的在线资源服务,通过使用访问令牌,用户可以访问Ion上的高分辨率的地图和其他资源。
// 获取访问令牌:你需要在Cesium Ion官网上注册账号,并创建一个应用以获取访问令牌。
// 设置访问令牌:在你的代码中,你需要在创建Cesium Viewer之前,设置好这个访问令牌。
Cesium.Ion.defaultAccessToken = '申请自己的access token'
viewer = new Cesium.Viewer('cesiumContainer', {
animation: false, // 是否显示动画控件
shouldAnimate: false,//是否自动播放
homeButton: false, // 是否显示Home按钮
fullscreenButton: false, // 是否显示全屏按钮
baseLayerPicker: false, // 是否显示图层选择控件
geocoder: false, // 是否显示地名查找控件
timeline: false, // 是否显示时间线控件
sceneModePicker: false, // 是否显示投影方式控件
navigationHelpButton: false, // 是否显示帮助信息控件
navigationInstructionsInitiallyVisible: false, // 是否显示导航提示,默认为true,
infoBox: true, // 是否显示点击要素之后显示的信息
requestRenderMode: true, // 启用请求渲染模式
// scene3DOnly: false, //每个几何实例将只能以3D渲染以节省GPU内存
sceneMode: 3, // 初始场景模式 1 2D模式 2 2D循环模式 3 3D模式 Cesium.SceneMode
creditContainer: document.createElement('div'), // 创建一个空的div来包裹版权信息,类似隐藏掉默认的版权信息
// baseLayerPicker: false,//如果设置为false,则不会创建BaseLayerPicker小部件
// 指定Cesium的底图图层实例,默认为ImageryProvider
imageryProvider: new Cesium.WebMapTileServiceImageryProvider({
url: 'http://t0.tianditu.gov.cn/img_w/wmts?tk=申请自己的key',
layer: 'vec',
style: 'default',
tileMatrixSetID: 'w',
format: 'tiles',
maximumLevel: 18
})
});
//加载具有地形高度的数字模型或图像,即抬高三维模型所在区域的地形高度
terrainProvider = new Cesium.CesiumTerrainProvider({
url: 'http://127.0.0.1:10004' + this.detailInfo.terrainPath,
requestVertexNormals: true,
requestWaterMask: true,
tilingScheme: new Cesium.GeographicTilingScheme(),
requestVertexNormals: true
})
viewer.terrainProvider = terrainProvider
var creditElement = document.createElement('div')
creditElement.style.position = 'absolute'
creditElement.style.bottom = '10px'
creditElement.style.right = '10px'
creditElement.style.color = 'white'
creditElement.style.fontSize = '20px'
creditElement.innerHTML = '2024.6'
// 将DOM元素添加到Viewer的容器中
viewer.container.appendChild(creditElement)
tileset = new Cesium.Cesium3DTileset({
url: 'http://127.0.0.1:10004' + this.detailInfo.filePath,
maximumMemoryUsage: 5120//控制 3D Tiles 的最大内存使用量,从而在保证数据流畅的前提下尽可能减小内存占用。
})
viewer.scene.primitives.add(tileset)
viewer.zoomTo(tileset);
//添加自定义广告牌,具体见https://blog.youkuaiyun.com/dengkexin123456/article/details/144631670?spm=1001.2014.3001.5502
this.addPoints();
// 监听点击事件触发弹框显示
viewer.screenSpaceEventHandler.setInputAction(click => {
const pickedEntity = viewer.scene.pick(click.position)
this.selectedEntity = pickedEntity?.id
// 判断点击物体是否为图标实体
if (Cesium.defined(pickedEntity)) {
this.updateDialogPosition() // 更新弹窗的位置
this.dialogVisible = true // 显示弹窗
} else {
this.dialogVisible = false // 隐藏弹窗
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
// 监听鼠标移动事件来更新弹窗位置
viewer.screenSpaceEventHandler.setInputAction(movement => {
if (this.dialogVisible && this.selectedEntity) {
this.updateDialogPosition(); // 更新弹窗的位置
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
viewer.screenSpaceEventHandler.setInputAction(movement => {
const pickedEntity = viewer.scene.pick(movement.endPosition);
this.selectedEntity = pickedEntity?.id;
// 判断移入物体是否为图标实体
if (Cesium.defined(pickedEntity) && this.selectedEntity) {
// 鼠标移入图标
this.updateDialogPosition(); // 更新弹窗的位置
this.dialogVisible = true;
} else {
// 鼠标移出图标
this.dialogVisible = false;
this.selectedEntity = null;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
},
// 更新弹窗的位置
updateDialogPosition () {
// 计算弹窗位置
// 这里获取当前选择的实体(例如点位或图标)在 Cesium 世界坐标系中的位置。
// `this.selectedEntity.position.getValue` 方法根据当前时间返回实体的位置。
const cesiumPosition = this.selectedEntity.position.getValue(
viewer.clock.currentTime
)
// 将 Cesium 世界坐标转换为屏幕坐标。
// 这一步是将 3D 世界中的点位转换为 2D 屏幕上的像素位置。
const canvasPosition = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
viewer.scene,
cesiumPosition
)
// 检查转换是否成功。有时候,如果点位不在当前视图中,则转换可能失败。
if (canvasPosition) {
// 更新弹窗位置
this.dialogPosition = {
x: canvasPosition.x,
y: canvasPosition.y, // 假设弹窗应该在图标上方 50px 的位置
width: document.getElementById('cesiumContainer').offsetWidth,
height: document.getElementById('cesiumContainer').offsetHeight
}
}
},
},
beforeDestroy () {
if (viewer) {
// 组件销毁时,确保Cesium资源被释放
viewer.destroy()
}
}
}
</script>
<style scoped>
.visualization {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 99999;
}
</style>
子组件中设置弹窗的位置:
<template>
<!-- 自定义弹框---!>
<div :style="styleObject" v-if="dialogVisible" class="dialogStyle">
<span>弹框标题</span>
<span>
<i class="el-icon-close" style="color:#fff; font-size: 20px; cursor: pointer;"></i>
</span>
</div> -->
<el-tabs v-model="activeName">
<el-tab-pane label="基本信息" name="first">
<div></div>
</el-tab-pane>
<el-tab-pane label="实时照片" name="second">
<div></div>
</el-tab-pane>
<el-tab-pane label="实时监控" name="third" >
<div></div>
</el-tab-pane>
<el-tab-pane disabled>
<slot slot="label">
<i class="el-icon-close" style="color:#fff; font-size: 20px; cursor: pointer;" @click="closeDialog"></i>
</slot>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
export default {
props: {
dialogVisible: {
type: Boolean,
default: false
},
position: { type: Object, default: () => ({ x: 0, y: 0 }) },
},
data() {
return {
activeName: 'first',
Diawidth: "65%",
}
},
watch: {
dialogVisible: {
handler(newVal) {
if (newVal) {
this.activeName = 'first';
} else {
this.closeDialog();
}
},
immediate: true,
},
},
computed: {
styleObject() {
//设置弹框具体位置
let styleObject = {
position: "absolute",
};
let left= this.position.x;
let right = this.position.width - this.position.x;
let top = this.position.y;
let bottom = this.position.height - this.position.y;
let dialogWidth = this.position.width * 0.45;
let dialogHeight = 450;
if(right < dialogWidth){
styleObject['right'] = `${right+20}px`;
}else{
styleObject['left'] = `${left+20}px`;
}
if(bottom < dialogHeight){
styleObject['bottom'] = `${bottom}px`;
}else{
styleObject['top'] = `${top}px`;
}
return styleObject;
},
},
components: {
},
mounted() {
},
methods: {
closeDialog() {
this.$emit("closeDialog");
},
}
}
</script>
<style lang='scss' scoped>
.dialogStyle {
width: 45%;
height: 450px;
z-index: 9999999 !important;
background-size: 100% 100%;
color: #fff;
padding: 10px
}
/deep/.el-tabs__item {
color: #fff !important;
}
/deep/ .el-tabs__item.is-top:last-child {
float: right;
}
/deep/ .el-tabs__nav {
width: 100%;
}
</style>
走过路过,如果对您有些许帮助,动动您的小手点个赞!