irpas技术客

cesium实现自定义弹窗_Dlma__cesium 弹窗

大大的周 2280

目录

前言

一、创建点

二、创建弹窗

1.创建弹窗div

2.绑定到点击事件

?3.使弹窗跟随点移动

方式一:LEFT_DOWN+MOUSE_MOVE

?方式二:利用相机的changed事件

方式三:相机的moveStart和moveEnd事件中加入帧率监听事件

4.点转到地球背后弹窗隐藏?

总结



前言

? ? ? ? 公司有项目涉及cesium的部分,前辈便让我自己建个cesium的项目练练手,前两天让我尝试了一下自定义弹框的实现。

? ? ? ? 官方api中对点的描述中只有label这一属性可实现(不知是否是我看漏了啊哈哈哈哈哈哈),于是开始思考通过添加div的方式,来实现cesium自定义的弹窗效果。

? ? ? ? 本篇文章讲述从创建点开始到实现弹窗的过程。

一、创建点

????????通过对cesium的学习,绘制对象有Entity和Primitive两种方式,简述两者的区别就是前者调用简单,后者适用于绘制对象多的时候,绘制对象越多,primitive方式显现出的执行效率优势就越明显,此处不再赘述。

创建点代码如下:

addPoint(pointGeo) { this.viewer.entities.add({ id: 'pick_id_' + this.pick_id++, name: 'point', position: Cesium.Cartesian3.fromDegrees(pointGeo.longitude, pointGeo.latitude), point: { //点 pixelSize: 5, color: Cesium.Color.RED, outlineColor: Cesium.Color.WHITE, outlineWidth: 2 }, label: { //文字标签 text: 'I am a point', font: '14pt monospace', style: Cesium.LabelStyle.FILL_AND_OUTLINE, outlineWidth: 2, verticalOrigin: Cesium.VerticalOrigin.BOTTOM, //垂直方向以底部来计算标签的位置 pixelOffset: new Cesium.Cartesian2(0, -9) //偏移量 }, billboard: { //图标 image: '/img/leaf-green.png', width: 64, height: 64, pixelOffset: new Cesium.Cartesian2(0, -32) //偏移量 }, } ); },

代码中使用entities的方式创建点,addPoint方法传入的pointGeo是点坐标。? ? ? ?

为了方便测试,我通过鼠标左键点击监听来动态添加点的坐标,代码如下:

//点击地图事件 const handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas); handler.setInputAction((click) => { // 屏幕坐标转世界坐标——关键点 const ellipsoid = this.viewer.scene.globe.ellipsoid; const cartesian = this.viewer.camera.pickEllipsoid(click.position, ellipsoid); if (cartesian) { //判断点击的是否是地球 //将笛卡尔坐标转换为地理坐标 const cartographic = Cesium.Cartographic.fromCartesian(cartesian); //将弧度转为度的十进制度表示 const lon = Cesium.Math.toDegrees(cartographic.longitude); const lat = Cesium.Math.toDegrees(cartographic.latitude); const click_point = {longitude: lon, latitude: lat}; this.addPoint(click_point); //将点击点的经纬度传入addPoint console.log(click_point) } }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

?点的样式如图所示:

二、创建弹窗

? ? ? ? 弹窗的实现效果是点击某个存在的点模型后在点的右侧打开,原理是通过获取点击点的屏幕坐标,将坐标的y和x分别赋值给div的top和lieft属性。实现过程如下。

1.创建弹窗div

代码如下:

initTool(frameDiv) { if (this.isInit) { return 0; } //弹窗容器div const rightdiv = document.createElement('DIV'); rightdiv.className = "tooltipdiv-right"; rightdiv.style = ` position:absolute; width:200px; min-height:100px; max-height:300px; background:#fff; border-radius:4px; box-shadow: 2px 4px 5px #888888; `; //弹窗箭头div const arrow = document.createElement('DIV'); arrow.className = "tooltip-arrow"; arrow.style = ` position:absolute; left:-24px; top:38px; width:0; height:0; border-top: 12px solid transparent; border-right: 12px solid #fff; border-bottom: 12px solid transparent; border-left: 12px solid transparent;`; rightdiv.appendChild(arrow); //标题div const title = document.createElement('DIV'); title.className = "tooltipdiv-inner"; title.style = ` width:100%; height:25px; line-height:25px; text-align:center; background:red; ` rightdiv.appendChild(title); //内容div const content = document.createElement('DIV'); content.className = "tooltipdiv-content"; content.style = ` width:200px; box-sizing:border-box; padding:10px 0 10px 10px; overflow-y:scroll; word-break:break-all; ` rightdiv.appendChild(content); this.addDiv = rightdiv; this.addtitle = title; this.addcontent = content; frameDiv.appendChild(rightdiv); this.isInit = true; },

调用方法创建div:

//初始化弹窗div this.initTool(this.viewer.cesiumWidget.container); 2.绑定到点击事件

点击时打开弹窗,在methods中新增了如下方法:

//控制弹窗显隐 setVisible: function (visible) { if (!this.isInit) { return 0; } this.addDiv.style.display = visible ? 'block' : 'none'; }, //控制弹窗定位 showAt: function (position, message, content) { if (!this.isInit) { return 0; } if (position && message && content) { this.setVisible(true); this.addtitle.innerHTML = message; this.addcontent.innerHTML = content; this.addDiv.style.left = position.x + 30 + "px"; this.addDiv.style.top = (position.y - 50) + "px"; } }

以上方法在左键点击监听事件中调用,在点击事件中通过pick来判断是否选中点对象(该方法可在官方api中学习到),代码中的几个全局变量就不展示了,过度作用。

以下代码是在上文点击监听事件的基础上进行了扩展,通过pick判断是否选中对象,选中后打开弹窗,展示传入的信息,没有选中对象则创建新的点对象,点击至地球外部则没有操作。

//点击地图事件 const handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas); handler.setInputAction((click) => { const pick = this.viewer.scene.pick(click.position); console.log(pick) //选中某模型 pick选中的对象 if (pick && pick.id) { // 屏幕坐标转世界坐标——关键点 const ellipsoid = this.viewer.scene.globe.ellipsoid; const cartesian = this.viewer.camera.pickEllipsoid(click.position, ellipsoid); //将笛卡尔坐标转换为地理坐标 const cartographic = Cesium.Cartographic.fromCartesian(cartesian); //将弧度转为度的十进制度表示 const lon = Cesium.Math.toDegrees(cartographic.longitude); const lat = Cesium.Math.toDegrees(cartographic.latitude); const point2 = {longitude: lon, latitude: lat}; this.click_point = Cesium.Cartesian3.fromDegrees(point2.longitude, point2.latitude); console.log(this.click_point) this.c = new Cesium.Cartesian2(click.position.x, click.position.y); this.target_position = click.position this.cartesian_2 = cartesian if (cartesian) { this.showAt(click.position, 'LEFT_CLICK', 'i am content'); } else { this.setVisible(false); } } else { // 屏幕坐标转世界坐标——关键点 const ellipsoid = this.viewer.scene.globe.ellipsoid; const cartesian = this.viewer.camera.pickEllipsoid(click.position, ellipsoid); if (cartesian) { //将笛卡尔坐标转换为地理坐标 const cartographic = Cesium.Cartographic.fromCartesian(cartesian); //将弧度转为度的十进制度表示 const lon = Cesium.Math.toDegrees(cartographic.longitude); const lat = Cesium.Math.toDegrees(cartographic.latitude); const click_point = {longitude: lon, latitude: lat}; this.addPoint(click_point); console.log(click_point) } } console.log(click.position) }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

?实现后弹窗效果如图所示:

?3.使弹窗跟随点移动

不论缩放地图或者移动点,都会造成弹窗的移动的需求,那么就要通过监听来完成弹窗移动的效果。本次测试中共使用了三种方法来对移动进行监听,下面逐一介绍。

方式一:LEFT_DOWN+MOUSE_MOVE

? ? ? ? 原理是通过左键按下事件和鼠标移动事件进行嵌套,来模拟鼠标的拖拽事件,该方法可以实现弹窗的跟随移动但是不连贯。

代码如下所示:

handler.setInputAction(()=>{ handler.setInputAction(()=>{ if (this.c) { this.isVisible = new Cesium.EllipsoidalOccluder(Cesium.Ellipsoid.WGS84, this.viewer.camera.position).isPointVisible(this.click_point); if (this.isVisible === false){ this.setVisible(false) }else { this.changedC = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, this.cartesian_2); this.showAt(this.changedC, 'i have moved','yeap') this.c = this.changedC } } }, Cesium.ScreenSpaceEventType.MOUSE_MOVE) }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

效果如图所示:

?方式二:利用相机的changed事件 this.viewer.scene.camera.changed.addEventListener()

这个changed事件由于存在延迟,相机移动一定程度后才有变化,所以效果比方式一还差……就不展示了。

方式三:相机的moveStart和moveEnd事件中加入帧率监听事件

通过相机的moveStart和moveEnd事件中加入帧率监听事件,可以做到弹窗位置逐帧改变,视觉效果最佳,原理是moveStart中加入事件,moveEnd中结束事件。

代码如下:

//相机移动开始事件 this.viewer.scene.camera.moveStart.addEventListener(() => { this.removeHandler = this.viewer.scene.postRender.addEventListener(() => { console.log("start") if (this.c) { this.isVisible = new Cesium.EllipsoidalOccluder(Cesium.Ellipsoid.WGS84, this.viewer.camera.position).isPointVisible(this.click_point); if (this.isVisible === false) { this.setVisible(false) } else { this.changedC = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, this.cartesian_2); this.showAt(this.changedC, 'i have moved', 'yeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeapyeap') this.c = this.changedC } } }) }) //相机移动结束事件 this.viewer.scene.camera.moveEnd.addEventListener(() => { console.log("end") this.removeHandler.call(); })

?效果如图:

视觉效果上的最佳方案。

4.点转到地球背后弹窗隐藏?

一句代码搞定

this.isVisible = new Cesium.EllipsoidalOccluder(Cesium.Ellipsoid.WGS84,this.viewer.camera.position).isPointVisible(this.click_point);

效果如图

?是不是很简单(? ?_?)?


总结

? ? ? ??以上就是本文内容,从点的创建到弹框的移动和隐藏,都做了基础的分析和展示,代码完整,因为只是测试,所以也不纠结样式等,可以随意改造,主要完成功能方面。

????????因为网上的有效资源太少了,所以在此记录一下学习过程,希望对其他人也能有帮助,那当然再好不过啦。

本文方法中div构建方法由某公众号中内容改进,在此附上公众号链接,值得学习:

Cesium学习系列汇总 (qq.com)


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #cesium #弹窗 #前言