cesium's customized pop-up window (can be zoomed in, out, and moved with the ball)

#.Effect

 

 

The source code of the effect in the picture is in the package column below 

#Basic  idea 

 Add a left mouse button click event. When the mouse clicks, use  Vue.extend()  in vue2.0 to dynamically add a dom element, render the DOM element into the cesium container, and use  the viewer.scene.postRender  provided in cesium in real time. Update the coordinate position. The idea is very simple, let’s implement it next.

#Implementation  method 

1.  First we need to generate a sphere as the container for our marking . 

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

 2.  Then use billboard in cesium to add target points  

Add point data format 

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

 Add the code first (class encapsulation)

//加载点
 dragEntity(){
   let drag = new DragEntity({
     viewer:this.$store.state.viewer, 
   })
   let _this = this
   // this.poin = [{id:234,position:[122.8,39.9],text:"L"},{id:432,position:[122,39],text:"C"}]
   this.poin.forEach(item => {
     let entity = drag.addEntity(item);
     _this.poinEntity[item.id] = entity;
   })
 },
 
/**
 * @param {Viewer} viewer
 * 
*/
export default class DragEntity{
    constructor(val){
        this.viewer = val.viewer,
    }
    addEntity(value){
        //数据格式{id:543595234324_432423,position:[122.8,39.9],text:"L"}
        let pinBuilder = new Cesium.PinBuilder();
        let poin = this.viewer.entities.add({
            id:value.id,
            name: value.name,
            position: Cesium.Cartesian3.fromDegrees(value.position.x, value.position.y),
            billboard: {
              image: pinBuilder.fromText(value.text,Cesium.Color.ROYALBLUE, 48).toDataURL(),
              verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            },
            monitoItems:{
                    data:value
                },
        });
        return poin
    }
    
}

To explain, we have encapsulated a class class and encapsulated the method of adding points into the class for easy calling. We use the "billboard" method and PinBuilder in cesium to add target points. If you don't know "PinBuilder", you can check the official website https: //sandcastle.cesium.com/?src=Map%20Pins.html&label=All, then we will return the created entity and use an object to receive it. The purpose of using the object is to facilitate search in the future. Let’s take a look at the effect

3.  In the third step, we add a left-click event, get the entity when clicked, add the leftDownAction() method in methods(), and use mounted 

leftDownAction(){
      let viewer = this.$store.state.viewer
        this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
        let _this = this
        let id
        _this.handler.setInputAction(function (movement) {
            let pick = viewer.scene.pick(movement.position); 
            if (Cesium.defined(pick) && (pick.id.id) ) {
                // _this.leftDownFlag = true;
                id= pick.id.id;
                 console.log(id)
            }
            
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
        
    },

 Code interpretation, we first create a LEFT_CLICK  event, the return value is the latitude and longitude, use  viewer.scene.pick to determine the entity, and print it to the console;

4.  In the fourth step, we complete the pop-up part (the code below is not optimized, the optimized code is in the packaging part) . 

export default class Bubble {
    constructor(val){
        this.viewer = val.viewer
        this.div=document.createElement("div");
        // this.addDynamicLabel({id:1,position:val.position,title:"cl弹窗"});
    }
    addDynamicLabel(data){
        let div = this.div
        div.id = data.id;
        // div.style.display="inline"
        div.style.position = "absolute";
        div.style.width = "300px";
        div.style.height = "30px";
        let HTMLTable = `
            <div style="background:#00ffef66;height:200px;border:"1px soild #08f8a7">${data.text}
                <div style="">
            </div>
        `;
        div.innerHTML = HTMLTable;
        this.viewer.cesiumWidget.container.appendChild(div);
        let gisPosition = data.position._value
        this.viewer.scene.postRender.addEventListener(() => {
            const canvasHeight = this.viewer.scene.canvas.height;
            const windowPosition = new Cesium.Cartesian2();
            Cesium.SceneTransforms.wgs84ToWindowCoordinates(
                this.viewer.scene,
                gisPosition,
                windowPosition
            );
            div.style.bottom = canvasHeight - windowPosition.y +220 + "px";
            const elWidth = div.offsetWidth;
            div.style.left = windowPosition.x - elWidth / 2 + "px";
        }, this);
    }
    clearDiv(id){
        if(this.div){
            var parent = this.div.parentElement;
            parent.removeChild(this.div);
            // this.div.removeNode(true);
            this.viewer.scene.postRender.removeEventListener(this.addDynamicLabel,this)
        }
        
    }
}

 Modify click event code

import Bubble from './bubble/index.js'
leftDownAction(){
      	let viewer = this.$store.state.viewer
      	let bubble = new  Bubble({
			viewer:viewer 
		})
        this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
        let _this = this
        let id
        _this.handler.setInputAction(function (movement) {
            let pick = viewer.scene.pick(movement.position); 
            if (Cesium.defined(pick) && (pick.id.id) ) {
                // _this.leftDownFlag = true;
                id= pick.id.id;
                let entiy = this.poinEntity[id];
                bubble.addDynamicLabel(entiy);
            }else{
				bubble.clearDiv();
			}
            
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
        
    },

 The complete explanation of the fourth step is in cesium custom dynamic tag_cesium tag special effects_GIS-CL's blog-CSDN blog  cesium custom tag. This article will not explain more. When clicked, a pop-up window is displayed. When a blank space is clicked, the pop-up window is deleted.

#Encapsulation  (complete code) 

1. In vue

import Bubble from './bubble/index.js'
import DragEntity from './dragentity.js'
data(){
    return{
      fullSizenum:'fullSize',
      poinEntity:{},
      poin :  [{id:'12321321' , name: "北京西路测试点", type: "固定枪机", state: "在线", position: { x: 116.4568, y: 39.8926} ,text:'X'},
            {id:'43244324' , name: "阿乐修理厂门口", type: "固定枪机", state: "在线", position: {  x: 116.4568, y: 39.8944 } ,text:'+'},
            {id:'43764324', name: "裕华路加油站", type: "固定枪机", state: "在线", position: { x: 116.4566, y: 39.8923 } ,text:'?'},
            {id:'437543345', name: "康佳大药房", type: "固定枪机", state: "在线", position: { x: 116.4513, y: 39.8923 }  ,text:'!'},],
    }
},
mounted(){
    this.dragEntity()
    this.leftDownAction()
 },
methods:{
	leftDownAction(){
      let viewer = this.$store.state.viewer
        this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
        let _this = this
        let id
        _this.handler.setInputAction(function (movement) {
            let pick = viewer.scene.pick(movement.position); 
            if (Cesium.defined(pick) && (pick.id.id) ) {
                // _this.leftDownFlag = true;
                id= pick.id.id;
                 _this.bubble(id)
            }else{
              // console.log(_this.bubbles)
              if(_this.bubbles){
                _this.bubbles.windowClose()
              }
              
            }
            
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
        
    },
    bubble(id){
      if(this.bubbles){
        this.bubbles.windowClose()
      }
      console.log(this.poinEntity[id])
      this.bubbles = new Bubble(Object.assign(this.poinEntity[id],{
        viewer:this.$store.state.viewer
      }))
      
    },
    //加载点
    dragEntity(){
      let drag = new DragEntity({
        viewer:this.$store.state.viewer, 
      })
      let _this = this
      // this.poin = [{id:234,position:[122.8,39.9],text:"L"},{id:432,position:[122,39],text:"C"}]
      this.poin.forEach(item => {
        let entity = drag.addEntity(item);
        _this.poinEntity[item.id] = entity;
      })
    },
}
	

2. Create dragentity.js file

/**
 * @param {Viewer} viewer
 * 
*/
export default class DragEntity{
    constructor(val){
        this.viewer = val.viewer,
    }
    addEntity(value){
        let pinBuilder = new Cesium.PinBuilder();
        let poin = this.viewer.entities.add({
            id:value.id,
            name: value.name,
            position: Cesium.Cartesian3.fromDegrees(value.position.x, value.position.y),
            billboard: {
              image: pinBuilder.fromText(value.text,Cesium.Color.ROYALBLUE, 48).toDataURL(),
              verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            },
            monitoItems:{
                    data:value
                },
        });
        return poin
    }
    
}

 3. Create a folder named bubble, in which create an index.js file and an index.vue file
index.js.

/**
 * @descripion:
 * @param {Viewer} viewer
 * @param {Cartesian2} position
 * @param {String} title
 * @param {String} id
 * @return {*}
 */

 import Vue from "vue";
 import Label from "./index.vue";
 let WindowVm = Vue.extend(Label);
 export default class Bubble {
     
     constructor(val) {
       console.log(val.monitoItems.data.name)
         this.viewer = val.viewer;
        //  this.height = val.height;
         this.position = val.position._value;
         let title = val.monitoItems.data.name;
         let state = val.monitoItems.data.state;
         let id = val.id
         this.vmInstance = new WindowVm({
           propsData: {
            title,
            state,
             id
           }
         }).$mount(); //根据模板创建一个面板

         this.vmInstance.closeEvent = e => {
           this.windowClose();
         }

         val.viewer.cesiumWidget.container.appendChild(this.vmInstance.$el); //将字符串模板生成的内容添加到DOM上
         
         this.addPostRender();
     }
     
   //添加场景事件
   addPostRender() {
     this.viewer.scene.postRender.addEventListener(this.postRender, this);
   }
 
   //场景渲染事件 实时更新窗口的位置 使其与笛卡尔坐标一致
   postRender() {
     if (!this.vmInstance.$el || !this.vmInstance.$el.style) return;
     const canvasHeight = this.viewer.scene.canvas.height;
     const windowPosition = new Cesium.Cartesian2();
     Cesium.SceneTransforms.wgs84ToWindowCoordinates(
       this.viewer.scene,
       this.position,
       windowPosition
     );
     this.vmInstance.$el.style.bottom =
       canvasHeight - windowPosition.y  +260+ "px";
     const elWidth = this.vmInstance.$el.offsetWidth;
     this.vmInstance.$el.style.left = windowPosition.x - elWidth / 2 +110 + "px";
 
     const camerPosition = this.viewer.camera.position;
     let height = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(camerPosition).height;
     height += this.viewer.scene.globe.ellipsoid.maximumRadius;
     if((!(Cesium.Cartesian3.distance(camerPosition,this.position) > height))&&this.viewer.camera.positionCartographic.height<50000000){
         this.vmInstance.$el.style.display = "block";
     }else{
       this.vmInstance.$el.style.display = "none";
     }
   }
   //关闭 
   windowClose() {
   if(this.vmInstance){
			this.vmInstance.$el.remove();
			this.vmInstance.$destroy();
	}
     //this.vmInstance.$el.style.display = "none"; //删除dom
        this.viewer.scene.postRender.removeEventListener(this.postRender, this); //移除事件监听
    }
 }

index.vue

<template>
  <div :id="id" class="box">
    <div class="pine"></div>
    <div class="box-wrap">
      <div class="close" @click="closeClick">X</div>
      <div class="area">
        <div class="area-title fontColor">{
   
   { title }}</div>
        
      </div>
      <div class="content">
        <div class="data-li">
          <div class="data-label textColor">状态:</div>
          <div class="data-value">
            <span class="label-num yellowColor">{
   
   {state}}</span>
          </div>
        </div>
        <div class="data-li">
          <div class="data-label textColor">实时水位:</div>
          <div class="data-value">
            <span class="label-num yellowColor">100</span>
            <span class="label-unit textColor">m³/s</span>
          </div>
        </div>
      </div>
    </div>

    <!-- <img src="./layer_border.png" alt="Norway"> -->
  </div>
</template>

<script>
export default {
  name: "DynamicLabel",
  data() {
    return {
      show: true,
    };
  },
  props: {
    title: {
      type: String,
      default: "标题",
    },
    id: {
      type: String,
      default: "001",
    },
    state:{
      type: String,
      default: "001",
    }
  },
  methods:{
    closeClick(){
      if(this.closeEvent){
        this.closeEvent();
      }
    }
  }
};
</script>


<style lang="scss">
.box {
  width: 200px;
  position: relative;
  bottom: 0;
  left: 0;
}
.close{
  position: absolute;
  color: #fff;
  top: 1px;
  right: 10px;
  text-shadow: 2px 2px 2px #022122;
  cursor: pointer;
  animation: fontColor 1s;
}
.box-wrap {
  position: absolute;
  left: 21%;
  top: 0;
  width: 100%;
  height: 163px;
  border-radius: 50px 0px 50px 0px;
  border: 1px solid #38e1ff;
  background-color: #38e1ff4a;
  box-shadow: 0 0 10px 2px #29baf1;
  animation: slide 2s;
}
.box-wrap .area {
  position: absolute;
  top: 20px;
  right: 0;
  width: 95%;
  height: 30px;
  background-image: linear-gradient(to left, #4cdef9, #4cdef96b);
  border-radius: 30px 0px 0px 0px;
  animation: area 1s;
}
.pine {
  position: absolute;
  // left: 0;
  // bottom: -83px;
  width: 100px;
  height: 100px;
  box-sizing: border-box;
  line-height: 120px;
  text-indent: 5px;
}

.pine::before {
  content: "";
  position: absolute;
  left: 0;
  bottom: -83px;
  width: 40%;
  height: 60px;
  box-sizing: border-box;
  border-bottom: 1px solid #38e1ff;
  transform-origin: bottom center;
  transform: rotateZ(135deg) scale(1.5);
  animation: slash 0.5s;
  filter: drop-shadow(1px 0px 2px #03abb4);
  /* transition: slash 2s; */
}

.area .area-title {
  text-align: center;
  line-height: 30px;
}
.textColor {
  font-size: 14px;
  font-weight: 600;
  color: #ffffff;
  text-shadow: 1px 1px 5px #002520d2;
  animation: fontColor 1s;
}
.yellowColor {
  font-size: 14px;
  font-weight: 600;
  color: #f09e28;
  text-shadow: 1px 1px 5px #002520d2;
  animation: fontColor 1s;
}

.fontColor {
  font-size: 16px;
  font-weight: 800;
  color: #ffffff;
  text-shadow: 1px 1px 5px #002520d2;
  animation: fontColor 1s;
}
.content {
  padding: 55px 10px 10px 10px;
}
.content .data-li {
  display: flex;
}

@keyframes fontColor {
  0% {
    color: #ffffff00;
    text-shadow: 1px 1px 5px #00252000;
  }
  40% {
    color: #ffffff00;
    text-shadow: 1px 1px 5px #00252000;
  }
  100% {
    color: #ffffff;
    text-shadow: 1px 1px 5px #002520d2;
  }
}

@keyframes slide {
  0% {
    border: 1px solid #38e1ff00;
    background-color: #38e1ff00;
    box-shadow: 0 0 10px 2px #29baf100;
  }

  100% {
    border: 1px solid #38e1ff;
    background-color: #38e1ff4a;
    box-shadow: 0 0 10px 2px #29baf1;
  }
}
@keyframes area {
  0% {
    width: 0%;
  }
  25% {
    width: 0%;
  }

  100% {
    width: 95%;
  }
}

/* img{
            position:absolute;
            left:30%;
            top:0;
            width: 100%;
            box-shadow: 0 0 10px 2px #29baf1;
        } */

@keyframes slash {
  0% {
    transform: rotateZ(135deg) scale(0);
  }

  100% {
    transform: rotateZ(135deg) scale(1.5);
  }
}
</style>

 Then just call it in the first step in the package column, and the final effect will be the same as the display effect.

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

Original link: https://blog.csdn.net/weixin_46730573/article/details/119852888

 

 

Guess you like

Origin blog.csdn.net/qq_37312180/article/details/130202355