当前位置:   article > 正文

cesium自定义的弹窗 Popup弹窗(可随球放大缩小,移动)_cesium popup

cesium popup

#.效果

图中效果源代码在下面的封装栏中 

基本思路 

 添加一个鼠标左键点击事件,当鼠标点击时,利用vue2.0中 Vue.extend() 动态添加一个dom元素,将DOM元素渲染到cesium容器中,并利用cesium中提供的 viewer.scene.postRender 实时更新坐标位置。思路很简单,接下来我们进行实现。

实现方法 

1. 首先我们需要生成一个球体做我们标记的容器 

  1. viewer = new Cesium.Viewer('cesiumContainer',{
  2. // terrainProvider: Cesium.createWorldTerrain(),
  3. // animation: false, // 控制场景动画的播放速度控件
  4. // baseLayerPicker: true, // 底图切换控件
  5. // baselLayerPicker:false,// 将图层选择的控件关掉,才能添加其他影像数据
  6. // // fullscreenButton: false, // 全屏控件
  7. // geocoder: false, // 地理位置查询定位控件
  8. // homeButton: true, // 默认相机位置控件
  9. // timeline: false, // 时间滚动条控件
  10. // infoBox: false, //是否显示信息框
  11. // sceneModePicker: false, //是否显示3D/2D选择器
  12. // selectionIndicator: false, // 点击点绿色弹出 是否显示选取指示器组件
  13. // sceneMode: Cesium.SceneMode.SCENE3D, //设定3维地图的默认场景模式:Cesium.SceneMode.SCENE2D、Cesium.SceneMode.SCENE3D、Cesium.SceneMode.MORPHING
  14. // navigationHelpButton: false, // 默认的相机控制提示控件
  15. // scene3DOnly: true, // 每个几何实例仅以3D渲染以节省GPU内存
  16. // navigationInstructionsInitiallyVisible: false,
  17. // showRenderLoopErrors: false, //是否显示渲染错误
  18. // orderIndependentTranslucency:false,//设置背景透明
  19. });

 2. 然后利用cesium中 billboard 来添加目标点位

添加点位的数据格式 

  1. poin : [{id:'12321321' , name: "北京西路测试点", type: "固定枪机", state: "在线", position: { x: 116.4568, y: 39.8926} ,text:'X'},
  2. {id:'43244324' , name: "阿乐修理厂门口", type: "固定枪机", state: "在线", position: { x: 116.4568, y: 39.8944 } ,text:'+'},
  3. {id:'43764324', name: "裕华路加油站", type: "固定枪机", state: "在线", position: { x: 116.4566, y: 39.8923 } ,text:'?'},
  4. {id:'437543345', name: "康佳大药房", type: "固定枪机", state: "在线", position: { x: 116.4513, y: 39.8923 } ,text:'!'},],

 添加点位先上代码(class封装)

  1. //加载点
  2. dragEntity(){
  3. let drag = new DragEntity({
  4. viewer:this.$store.state.viewer,
  5. })
  6. let _this = this
  7. // this.poin = [{id:234,position:[122.8,39.9],text:"L"},{id:432,position:[122,39],text:"C"}]
  8. this.poin.forEach(item => {
  9. let entity = drag.addEntity(item);
  10. _this.poinEntity[item.id] = entity;
  11. })
  12. },
  1. /**
  2. * @param {Viewer} viewer
  3. *
  4. */
  5. export default class DragEntity{
  6. constructor(val){
  7. this.viewer = val.viewer,
  8. }
  9. addEntity(value){
  10. //数据格式{id:543595234324_432423,position:[122.8,39.9],text:"L"}
  11. let pinBuilder = new Cesium.PinBuilder();
  12. let poin = this.viewer.entities.add({
  13. id:value.id,
  14. name: value.name,
  15. position: Cesium.Cartesian3.fromDegrees(value.position.x, value.position.y),
  16. billboard: {
  17. image: pinBuilder.fromText(value.text,Cesium.Color.ROYALBLUE, 48).toDataURL(),
  18. verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
  19. },
  20. monitoItems:{
  21. data:value
  22. },
  23. });
  24. return poin
  25. }
  26. }

解读一下,我们封装了一个class类,将添加点的方法封装到类中方便调用,我们用“billboard”方法与cesium中PinBuilder来进行添加目标点,"PinBuilder"不会的可以看一下官网https://sandcastle.cesium.com/?src=Map%20Pins.html&label=All,然后我们将创建好的实体,return出来,用一个对象来进行接收,用对象的目的就是为了以后方便查找。来看一眼效果

3. 第三步我们来添加一个左键点击事件,当点击时获取实体,在methods()中添加leftDownAction()方法,mounted掉用 

  1. leftDownAction(){
  2. let viewer = this.$store.state.viewer
  3. this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  4. let _this = this
  5. let id
  6. _this.handler.setInputAction(function (movement) {
  7. let pick = viewer.scene.pick(movement.position);
  8. if (Cesium.defined(pick) && (pick.id.id) ) {
  9. // _this.leftDownFlag = true;
  10. id= pick.id.id;
  11. console.log(id)
  12. }
  13. }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  14. },

 代码解读,我们先创建一个LEFT_CLICK 事件,返回值为经纬度,运用 viewer.scene.pick判断拿到实体,打印到控制台;

4. 第四步我们来完成弹窗部分(下面代码没有优化,优化的代码在封装部分) 

  1. export default class Bubble {
  2. constructor(val){
  3. this.viewer = val.viewer
  4. this.div=document.createElement("div");
  5. // this.addDynamicLabel({id:1,position:val.position,title:"cl弹窗"});
  6. }
  7. addDynamicLabel(data){
  8. let div = this.div
  9. div.id = data.id;
  10. // div.style.display="inline"
  11. div.style.position = "absolute";
  12. div.style.width = "300px";
  13. div.style.height = "30px";
  14. let HTMLTable = `
  15. <div style="background:#00ffef66;height:200px;border:"1px soild #08f8a7">${data.text}
  16. <div style="">
  17. </div>
  18. `;
  19. div.innerHTML = HTMLTable;
  20. this.viewer.cesiumWidget.container.appendChild(div);
  21. let gisPosition = data.position._value
  22. this.viewer.scene.postRender.addEventListener(() => {
  23. const canvasHeight = this.viewer.scene.canvas.height;
  24. const windowPosition = new Cesium.Cartesian2();
  25. Cesium.SceneTransforms.wgs84ToWindowCoordinates(
  26. this.viewer.scene,
  27. gisPosition,
  28. windowPosition
  29. );
  30. div.style.bottom = canvasHeight - windowPosition.y +220 + "px";
  31. const elWidth = div.offsetWidth;
  32. div.style.left = windowPosition.x - elWidth / 2 + "px";
  33. }, this);
  34. }
  35. clearDiv(id){
  36. if(this.div){
  37. var parent = this.div.parentElement;
  38. parent.removeChild(this.div);
  39. // this.div.removeNode(true);
  40. this.viewer.scene.postRender.removeEventListener(this.addDynamicLabel,this)
  41. }
  42. }
  43. }

 修改点击事件代码

  1. import Bubble from './bubble/index.js'
  2. leftDownAction(){
  3. let viewer = this.$store.state.viewer
  4. let bubble = new Bubble({
  5. viewer:viewer
  6. })
  7. this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  8. let _this = this
  9. let id
  10. _this.handler.setInputAction(function (movement) {
  11. let pick = viewer.scene.pick(movement.position);
  12. if (Cesium.defined(pick) && (pick.id.id) ) {
  13. // _this.leftDownFlag = true;
  14. id= pick.id.id;
  15. let entiy = this.poinEntity[id];
  16. bubble.addDynamicLabel(entiy);
  17. }else{
  18. bubble.clearDiv();
  19. }
  20. }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  21. },

 第四步的完整解读在cesium 自定义动态标记_cesium 标记特效_GIS-CL的博客-CSDN博客 cesium自定义标记中,本文不做多讲解。当点击时显示弹窗,当点击空白初,删除弹窗。

封装(完整代码) 

1、在vue中

  1. import Bubble from './bubble/index.js'
  2. import DragEntity from './dragentity.js'
  3. data(){
  4. return{
  5. fullSizenum:'fullSize',
  6. poinEntity:{},
  7. poin : [{id:'12321321' , name: "北京西路测试点", type: "固定枪机", state: "在线", position: { x: 116.4568, y: 39.8926} ,text:'X'},
  8. {id:'43244324' , name: "阿乐修理厂门口", type: "固定枪机", state: "在线", position: { x: 116.4568, y: 39.8944 } ,text:'+'},
  9. {id:'43764324', name: "裕华路加油站", type: "固定枪机", state: "在线", position: { x: 116.4566, y: 39.8923 } ,text:'?'},
  10. {id:'437543345', name: "康佳大药房", type: "固定枪机", state: "在线", position: { x: 116.4513, y: 39.8923 } ,text:'!'},],
  11. }
  12. },
  13. mounted(){
  14. this.dragEntity()
  15. this.leftDownAction()
  16. },
  17. methods:{
  18. leftDownAction(){
  19. let viewer = this.$store.state.viewer
  20. this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  21. let _this = this
  22. let id
  23. _this.handler.setInputAction(function (movement) {
  24. let pick = viewer.scene.pick(movement.position);
  25. if (Cesium.defined(pick) && (pick.id.id) ) {
  26. // _this.leftDownFlag = true;
  27. id= pick.id.id;
  28. _this.bubble(id)
  29. }else{
  30. // console.log(_this.bubbles)
  31. if(_this.bubbles){
  32. _this.bubbles.windowClose()
  33. }
  34. }
  35. }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  36. },
  37. bubble(id){
  38. if(this.bubbles){
  39. this.bubbles.windowClose()
  40. }
  41. console.log(this.poinEntity[id])
  42. this.bubbles = new Bubble(Object.assign(this.poinEntity[id],{
  43. viewer:this.$store.state.viewer
  44. }))
  45. },
  46. //加载点
  47. dragEntity(){
  48. let drag = new DragEntity({
  49. viewer:this.$store.state.viewer,
  50. })
  51. let _this = this
  52. // this.poin = [{id:234,position:[122.8,39.9],text:"L"},{id:432,position:[122,39],text:"C"}]
  53. this.poin.forEach(item => {
  54. let entity = drag.addEntity(item);
  55. _this.poinEntity[item.id] = entity;
  56. })
  57. },
  58. }

2、创建dragentity.js文件

  1. /**
  2. * @param {Viewer} viewer
  3. *
  4. */
  5. export default class DragEntity{
  6. constructor(val){
  7. this.viewer = val.viewer,
  8. }
  9. addEntity(value){
  10. let pinBuilder = new Cesium.PinBuilder();
  11. let poin = this.viewer.entities.add({
  12. id:value.id,
  13. name: value.name,
  14. position: Cesium.Cartesian3.fromDegrees(value.position.x, value.position.y),
  15. billboard: {
  16. image: pinBuilder.fromText(value.text,Cesium.Color.ROYALBLUE, 48).toDataURL(),
  17. verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
  18. },
  19. monitoItems:{
  20. data:value
  21. },
  22. });
  23. return poin
  24. }
  25. }

 3、创建以bubble命名文件夹,里面分别创建一个index.js文件,和index.vue文件
index.js

  1. /**
  2. * @descripion:
  3. * @param {Viewer} viewer
  4. * @param {Cartesian2} position
  5. * @param {String} title
  6. * @param {String} id
  7. * @return {*}
  8. */
  9. import Vue from "vue";
  10. import Label from "./index.vue";
  11. let WindowVm = Vue.extend(Label);
  12. export default class Bubble {
  13. constructor(val) {
  14. console.log(val.monitoItems.data.name)
  15. this.viewer = val.viewer;
  16. // this.height = val.height;
  17. this.position = val.position._value;
  18. let title = val.monitoItems.data.name;
  19. let state = val.monitoItems.data.state;
  20. let id = val.id
  21. this.vmInstance = new WindowVm({
  22. propsData: {
  23. title,
  24. state,
  25. id
  26. }
  27. }).$mount(); //根据模板创建一个面板
  28. this.vmInstance.closeEvent = e => {
  29. this.windowClose();
  30. }
  31. val.viewer.cesiumWidget.container.appendChild(this.vmInstance.$el); //将字符串模板生成的内容添加到DOM上
  32. this.addPostRender();
  33. }
  34. //添加场景事件
  35. addPostRender() {
  36. this.viewer.scene.postRender.addEventListener(this.postRender, this);
  37. }
  38. //场景渲染事件 实时更新窗口的位置 使其与笛卡尔坐标一致
  39. postRender() {
  40. if (!this.vmInstance.$el || !this.vmInstance.$el.style) return;
  41. const canvasHeight = this.viewer.scene.canvas.height;
  42. const windowPosition = new Cesium.Cartesian2();
  43. Cesium.SceneTransforms.wgs84ToWindowCoordinates(
  44. this.viewer.scene,
  45. this.position,
  46. windowPosition
  47. );
  48. this.vmInstance.$el.style.bottom =
  49. canvasHeight - windowPosition.y +260+ "px";
  50. const elWidth = this.vmInstance.$el.offsetWidth;
  51. this.vmInstance.$el.style.left = windowPosition.x - elWidth / 2 +110 + "px";
  52. const camerPosition = this.viewer.camera.position;
  53. let height = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(camerPosition).height;
  54. height += this.viewer.scene.globe.ellipsoid.maximumRadius;
  55. if((!(Cesium.Cartesian3.distance(camerPosition,this.position) > height))&&this.viewer.camera.positionCartographic.height<50000000){
  56. this.vmInstance.$el.style.display = "block";
  57. }else{
  58. this.vmInstance.$el.style.display = "none";
  59. }
  60. }
  61. //关闭
  62. windowClose() {
  63. if(this.vmInstance){
  64. this.vmInstance.$el.remove();
  65. this.vmInstance.$destroy();
  66. }
  67. //this.vmInstance.$el.style.display = "none"; //删除dom
  68. this.viewer.scene.postRender.removeEventListener(this.postRender, this); //移除事件监听
  69. }
  70. }

index.vue

  1. <template>
  2. <div :id="id" class="box">
  3. <div class="pine"></div>
  4. <div class="box-wrap">
  5. <div class="close" @click="closeClick">X</div>
  6. <div class="area">
  7. <div class="area-title fontColor">{{ title }}</div>
  8. </div>
  9. <div class="content">
  10. <div class="data-li">
  11. <div class="data-label textColor">状态:</div>
  12. <div class="data-value">
  13. <span class="label-num yellowColor">{{state}}</span>
  14. </div>
  15. </div>
  16. <div class="data-li">
  17. <div class="data-label textColor">实时水位:</div>
  18. <div class="data-value">
  19. <span class="label-num yellowColor">100</span>
  20. <span class="label-unit textColor">m³/s</span>
  21. </div>
  22. </div>
  23. </div>
  24. </div>
  25. <!-- <img src="./layer_border.png" alt="Norway"> -->
  26. </div>
  27. </template>
  28. <script>
  29. export default {
  30. name: "DynamicLabel",
  31. data() {
  32. return {
  33. show: true,
  34. };
  35. },
  36. props: {
  37. title: {
  38. type: String,
  39. default: "标题",
  40. },
  41. id: {
  42. type: String,
  43. default: "001",
  44. },
  45. state:{
  46. type: String,
  47. default: "001",
  48. }
  49. },
  50. methods:{
  51. closeClick(){
  52. if(this.closeEvent){
  53. this.closeEvent();
  54. }
  55. }
  56. }
  57. };
  58. </script>
  59. <style lang="scss">
  60. .box {
  61. width: 200px;
  62. position: relative;
  63. bottom: 0;
  64. left: 0;
  65. }
  66. .close{
  67. position: absolute;
  68. color: #fff;
  69. top: 1px;
  70. right: 10px;
  71. text-shadow: 2px 2px 2px #022122;
  72. cursor: pointer;
  73. animation: fontColor 1s;
  74. }
  75. .box-wrap {
  76. position: absolute;
  77. left: 21%;
  78. top: 0;
  79. width: 100%;
  80. height: 163px;
  81. border-radius: 50px 0px 50px 0px;
  82. border: 1px solid #38e1ff;
  83. background-color: #38e1ff4a;
  84. box-shadow: 0 0 10px 2px #29baf1;
  85. animation: slide 2s;
  86. }
  87. .box-wrap .area {
  88. position: absolute;
  89. top: 20px;
  90. right: 0;
  91. width: 95%;
  92. height: 30px;
  93. background-image: linear-gradient(to left, #4cdef9, #4cdef96b);
  94. border-radius: 30px 0px 0px 0px;
  95. animation: area 1s;
  96. }
  97. .pine {
  98. position: absolute;
  99. // left: 0;
  100. // bottom: -83px;
  101. width: 100px;
  102. height: 100px;
  103. box-sizing: border-box;
  104. line-height: 120px;
  105. text-indent: 5px;
  106. }
  107. .pine::before {
  108. content: "";
  109. position: absolute;
  110. left: 0;
  111. bottom: -83px;
  112. width: 40%;
  113. height: 60px;
  114. box-sizing: border-box;
  115. border-bottom: 1px solid #38e1ff;
  116. transform-origin: bottom center;
  117. transform: rotateZ(135deg) scale(1.5);
  118. animation: slash 0.5s;
  119. filter: drop-shadow(1px 0px 2px #03abb4);
  120. /* transition: slash 2s; */
  121. }
  122. .area .area-title {
  123. text-align: center;
  124. line-height: 30px;
  125. }
  126. .textColor {
  127. font-size: 14px;
  128. font-weight: 600;
  129. color: #ffffff;
  130. text-shadow: 1px 1px 5px #002520d2;
  131. animation: fontColor 1s;
  132. }
  133. .yellowColor {
  134. font-size: 14px;
  135. font-weight: 600;
  136. color: #f09e28;
  137. text-shadow: 1px 1px 5px #002520d2;
  138. animation: fontColor 1s;
  139. }
  140. .fontColor {
  141. font-size: 16px;
  142. font-weight: 800;
  143. color: #ffffff;
  144. text-shadow: 1px 1px 5px #002520d2;
  145. animation: fontColor 1s;
  146. }
  147. .content {
  148. padding: 55px 10px 10px 10px;
  149. }
  150. .content .data-li {
  151. display: flex;
  152. }
  153. @keyframes fontColor {
  154. 0% {
  155. color: #ffffff00;
  156. text-shadow: 1px 1px 5px #00252000;
  157. }
  158. 40% {
  159. color: #ffffff00;
  160. text-shadow: 1px 1px 5px #00252000;
  161. }
  162. 100% {
  163. color: #ffffff;
  164. text-shadow: 1px 1px 5px #002520d2;
  165. }
  166. }
  167. @keyframes slide {
  168. 0% {
  169. border: 1px solid #38e1ff00;
  170. background-color: #38e1ff00;
  171. box-shadow: 0 0 10px 2px #29baf100;
  172. }
  173. 100% {
  174. border: 1px solid #38e1ff;
  175. background-color: #38e1ff4a;
  176. box-shadow: 0 0 10px 2px #29baf1;
  177. }
  178. }
  179. @keyframes area {
  180. 0% {
  181. width: 0%;
  182. }
  183. 25% {
  184. width: 0%;
  185. }
  186. 100% {
  187. width: 95%;
  188. }
  189. }
  190. /* img{
  191. position:absolute;
  192. left:30%;
  193. top:0;
  194. width: 100%;
  195. box-shadow: 0 0 10px 2px #29baf1;
  196. } */
  197. @keyframes slash {
  198. 0% {
  199. transform: rotateZ(135deg) scale(0);
  200. }
  201. 100% {
  202. transform: rotateZ(135deg) scale(1.5);
  203. }
  204. }
  205. </style>

 然后在封装栏中第一步调用即可,最终效果与展示效果一样。

————————————————

原文链接:https://blog.csdn.net/weixin_46730573/article/details/119852888

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/564076
推荐阅读
相关标签
  

闽ICP备14008679号