cesium 实现自定义弹窗并跟随场景移动

cesium 添加点位自定义弹窗跟随场景移动 完整代码演示可直接copy使用

1 效果图:

2 深入理解

就是原始点位的数据

id>property

点位真实渲染到球体上的笛卡尔坐标系

id>_polyline 的路径下 可以通过

3 代码示例

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link href="https://cesium.com/downloads/cesiumjs/releases/1.85/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
  <script src="https://unpkg.com/@turf/turf/turf.min.js"></script>
  <script src="https://cesium.com/downloads/cesiumjs/releases/1.85/Build/Cesium/Cesium.js"></script>
  <script src="https://cdn.staticfile.net/vue/2.7.0/vue.min.js"></script>
</head>

<body>
  <div id="app">
    <button @click="mapClick">主动触发点位弹窗</button>
    <button @click="removeLayer('site_layer')">清除点位图层</button>
    <div id="cesiumContainer">
      <div id="popup">
        <div class="cesium-popup" @closeMap="closeMapPop" v-if="popVisible" ref="overlay">
          <div class="pop_header">
            <p class="rhpk_info"> {{ overlayChartObj.name }}基本信息</p>
            <div class="map_close" @click="closeMapPop">x</div>
          </div>
          <sitePop>
            <div class="button_box">
              <div>自动监测</div>
            </div>
          </sitePop>
        </div>
      </div>
    </div>
  </div>

  <script>
    var viewer = null
    new Vue({
      el: '#app',
      data: {
        popVisible: false,
        overlayChartObj: {},
        list: [
          { longitude: 109.306058, latitude: 30.86751, id: 0, name: "标点一" },
          { longitude: 109.306058, latitude: 30.56751, id: 1, name: "标点二" },
        ]
      },
      mounted() {
        this.mapCreate()
      },
      methods: {
        async mapCreate() {
          Cesium.Ion.defaultAccessToken =
            "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2ZjZlMzAxNS1jY2ZmLTQ0MzctYTE1MS0zNzMxZjhkZjlhYTgiLCJpZCI6MTIyNTg4LCJpYXQiOjE2NzQ5NzMxNTB9.GKIVHIsKN-AWd4vjLOIPm9Fji8tIEvZd5AXf-rTvFk0";
          viewer = new Cesium.Viewer('cesiumContainer', {
            geocoder: false,
            homeButton: false, 
            animation: false, 
            fullscreenButton: false,
            sceneModePicker: false,
            timeline: true, 。
            navigationHelpButton: false, 
            baseLayerPicker: false, 
            infoBox: false, 
            scene3DOnly: false, 
            selectionIndicator: false,
            baselLayerPicker: false,
          });
          let terrainProvider = Cesium.createWorldTerrain({ //添加世界地形能看到山脉的高低起伏
            requestVertexNormals: true, //开启地形光照
            requestWaterMask: true, // 开启水面波纹
          });
          viewer.terrainProvider = terrainProvider;
          viewer.debugShowFramesPerSecond = true//显示当前的帧率信息,这样你就可以实时观察到地图的渲染性能
          viewer.scene.screenSpaceCameraController.minimumZoomDistance = 1;// 设置了摄像机的最小缩放距离
          viewer.camera.setView({ // 相机视口
            destination: Cesium.Cartesian3.fromDegrees(109.306058, 30.86751, 8000),
            orientation: {
              heading: Cesium.Math.toRadians(0), //控制视口左右旋转,也就是沿Y轴旋转,0时为正北方向
              pitch: Cesium.Math.toRadians(-25), // 控制视口上下旋转,也就是沿X轴旋转,-90时为俯视地面
              roll: 0, // 控制视口翻转角度,也就是沿Z轴旋转,0时为不翻转
            },
            //奉节 坐标
          });
          this.handlePinClick(); // 注册点位点击事件
          this.bindMapMove(); // 注册场景移动事件
          this.renderSiteCesiumPoint(this.list)
        },
      }
    })
  </script>

</body>

</html>

完整代码示例

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link href="https://cesium.com/downloads/cesiumjs/releases/1.85/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
  <script src="https://unpkg.com/@turf/turf/turf.min.js"></script>
  <script src="https://cesium.com/downloads/cesiumjs/releases/1.85/Build/Cesium/Cesium.js"></script>
  <script src="https://cdn.staticfile.net/vue/2.7.0/vue.min.js"></script>
  <style>
    #cesiumContainer {
      width: 1920px;
      height: 1080px;
    }

    #cesiumContainer .cesium-widget-credits {
      display: none;
    }

    .cesium-popup {
      position: relative;
      width: 267px;
      min-height: 152px;
      background: pink;
      /* background: url(~@/assets/images/mapPop.png) no-repeat; */
      background-size: cover;
    }

    .cesium-popup .pop_header {
      width: 431px;
      height: 57px;
      line-height: 57px;
      /* background: url(~@/assets/images/mapHeader.png) no-repeat 100% 100%; */
    }

    .cesium-popup .rhpk_info {
      font-size: 18px;
      font-weight: bold;
      color: #fff;
      padding-left: 70px;
      box-sizing: border-box;
    }

    .cesium-popup .map_close {
      position: absolute;
      top: 16px;
      right: 20px;
      width: 29px;
      height: 29px;
      /* background: url(~@/assets/images/mapClose.png) no-repeat 100% 100%; */
      cursor: pointer;
    }

    .cesium-popup .map_con {
      padding: 20px 20px;
      width: 100%;
      box-sizing: border-box;
    }

    .cesium-popup .info {
      display: flex;
      font-size: 16px;
      color: #fff;
      margin-bottom: 20px;
    }

    .cesium-popup .title {
      width: 85px;
      font-weight: 400;
      color: #57bcd2;
    }

    .cesium-popup .button_box {
      position: absolute;
      bottom: 30px;
      left: 0px;
      width: 100%;
      display: flex;
      justify-content: flex-end;
    }

    .cesium-popup .button_box div {
      width: 100px;
      height: 27px;
      /* background: url(~@/assets/images/mapButton.png) no-repeat 100% 100%; */
      line-height: 27px;
      text-align: center;
      cursor: pointer;
      margin: 0 10px;
    }
  </style>
</head>

<body>
  <div id="app">
    <button @click="mapClick">主动触发点位弹窗</button>
    <button @click="removeLayer('site_layer')">清除点位图层</button>
    <div id="cesiumContainer">
      <div id="popup">
        <div class="cesium-popup" @closeMap="closeMapPop" v-if="popVisible" ref="overlay">
          <div class="pop_header">
            <p class="rhpk_info"> {{ overlayChartObj.name }}基本信息</p>
            <div class="map_close" @click="closeMapPop">x</div>
          </div>
          <sitePop>
            <div class="button_box">
              <div>自动监测</div>
            </div>
          </sitePop>
        </div>
      </div>
    </div>
  </div>

  <script>
    var viewer = null
    new Vue({
      el: '#app',
      data: {
        popVisible: false,
        overlayChartObj: {},
        list: [
          { longitude: 109.306058, latitude: 30.86751, id: 0, name: "标点一" },
          { longitude: 109.306058, latitude: 30.56751, id: 1, name: "标点二" },
        ]
      },
      mounted() {
        this.mapCreate()
      },
      methods: {
        async mapCreate() {
          Cesium.Ion.defaultAccessToken =
            "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2ZjZlMzAxNS1jY2ZmLTQ0MzctYTE1MS0zNzMxZjhkZjlhYTgiLCJpZCI6MTIyNTg4LCJpYXQiOjE2NzQ5NzMxNTB9.GKIVHIsKN-AWd4vjLOIPm9Fji8tIEvZd5AXf-rTvFk0";
          viewer = new Cesium.Viewer('cesiumContainer', {
             geocoder: false,
            homeButton: false, 
            animation: false, 
            fullscreenButton: false,
            sceneModePicker: false,
            timeline: true, 。
            navigationHelpButton: false, 
            baseLayerPicker: false, 
            infoBox: false, 
            scene3DOnly: false, 
            selectionIndicator: false,
            baselLayerPicker: false,
          });
          let terrainProvider = Cesium.createWorldTerrain({ //添加世界地形能看到山脉的高低起伏
            requestVertexNormals: true, //开启地形光照
            requestWaterMask: true, // 开启水面波纹
          });
          viewer.terrainProvider = terrainProvider;
          var lastCameraHeight = 0;
          var heightStableCount = 0;
          var threshold = 5; // 高度稳定的阈值
          var maxStableCount = 30; // 达到稳定的帧数

          function isTerrainProvider() {
            var cameraHeight =
              viewer.camera.positionCartographic.height;
            if (Math.abs(cameraHeight - lastCameraHeight) < threshold) {
              heightStableCount++;
            } else {
              heightStableCount = 0;
            }
            if (heightStableCount >= maxStableCount) {
              console.log("地形加载完成");
              // 在这里执行地形加载完成后的操作
              viewer.scene.preRender.removeEventListener(
                isTerrainProvider
              );
            }

            lastCameraHeight = cameraHeight;
          }
          viewer.debugShowFramesPerSecond = true//显示当前的帧率信息,这样你就可以实时观察到地图的渲染性能
          viewer.scene.screenSpaceCameraController.minimumZoomDistance = 1;// 设置了摄像机的最小缩放距离
          viewer.camera.setView({ // 相机视口
            destination: Cesium.Cartesian3.fromDegrees(109.306058, 30.86751, 8000),
            orientation: {
              heading: Cesium.Math.toRadians(0), //控制视口左右旋转,也就是沿Y轴旋转,0时为正北方向
              pitch: Cesium.Math.toRadians(-25), // 控制视口上下旋转,也就是沿X轴旋转,-90时为俯视地面
              roll: 0, // 控制视口翻转角度,也就是沿Z轴旋转,0时为不翻转
            },
            //奉节 坐标
          });


          this.handlePinClick(); // 注册点位点击事件
          this.bindMapMove(); // 注册场景移动事件
          this.renderSiteCesiumPoint(this.list)


        },
        renderSiteCesiumPoint(projectList) {
          this.removeLayer('site_layer')
          for (let i = 0; i < projectList.length; i++) {
            const pro = projectList[i];
            let longitude = pro.longitude || 0
            let latitude = pro.latitude || 0
            let imgUrl = `./site.png`;
            viewer.entities.add({
              position: Cesium.Cartesian3.fromDegrees( // 设置点位的经纬度和高度
                longitude,
                latitude,
                240
              ),
              name: pro.name,
              id: `site_layer_${pro.id}`, // site_layer就是自定义layerName后续可通过这个移除
              property: pro,
              polyline: {
                show: true, //是否显示,默认显示
                // 关键点在设置一条竖直的线
                positions: Cesium.Cartesian3.fromDegreesArrayHeights([
                  longitude,
                  latitude,
                  0,
                  longitude,
                  latitude,
                  240,
                ]),
                width: 1, //线的宽度(像素),默认为1
                material: Cesium.Color.fromCssColorString("#069BBF").withAlpha(1),
              },
              billboard: {
                width: 33, // 点位的大小
                height: 33,
                image: imgUrl,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 设置垂直方向的对齐方式
              },
            });
          }
        },
        removeLayer(layerName) { // 根据图层名移除对应的图层组
          let entityList = [];
          let count = 0
          while (count < 8) {
            viewer.entities.values.forEach(function (entity) {
              if (entity._id.includes(layerName)) {
                viewer.entities.remove(entity);
              }
            });
            count++;
          }
        },

        // 点击地图上的点
        handlePinClick(data = null, typePop = null) {
          const cesiumViewer = viewer;
          const tt = this;
          if (data != null && typePop != null) {
            console.log('主动触发', data, typePop)

            tt.overlayChartObj = Object.assign(data, {
              layer: typePop
            });
            tt.getProjDetailPop({
              x: 951.25,
              y: 600
            }, data);
            return
          }
          const handle3D = new Cesium.ScreenSpaceEventHandler(
            cesiumViewer.scene.canvas
          );
          handle3D.setInputAction((movement) => {
            const pick = cesiumViewer.scene.pick(movement.position);
            console.log("pick===>", pick, pick.id); // pick.id是点击后获取对应的实体
            console.log("pick===>",pick.id.polyline.positions.value.getValue());
            if (!pick.id) {
              console.log("清除监听事件===>", pick, pick.id);
              this.popVisible = false;
              // 清除监听事件
              cesiumViewer.scene.postRender.removeEventListener(this.bindMapMove);
              return;
            }
            const obj = pick.id.property;
            // 解决点击不同点数据不更换问题
            if (tt.overlayChartObj != {}) {
              tt.popVisible = false;
              tt.overlayChartObj = {};
            }

            const coordinate = movement.position;
            console.log("coordinate===>", coordinate);
            if (pick.id._id.includes("rhpwk_layer")) {
              tt.overlayChartObj = Object.assign(obj, {
                layer: "rhpwk_layer"
              });
              tt.getProjDetailPop(coordinate, obj);
            } else if (pick.id._id.includes("site_layer")) {
              tt.overlayChartObj = Object.assign(obj, {
                layer: "site_layer" //使用site_layer类型的点位弹窗
              });
              tt.getProjDetailPop(coordinate, obj);
            } else if (pick.id._id.includes("camera_layer")) {
              tt.overlayChartObj = Object.assign(obj, {
                layer: "camera_layer"
              });
              tt.getProjDetailPop(coordinate, obj);
            } else if (pick.id._id.includes("wry_layer")) {
              tt.overlayChartObj = Object.assign(obj, {
                layer: "wry_layer"
              });
              tt.getProjDetailPop(coordinate, obj);
            }
          }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
        },
        getProjDetailPop(coordinate, obj) {
          this.showOverlayChart(coordinate, obj);
        },
        // 显示
        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 = data;
          this.popVisible = true;
        },
        bindMapMove() {
          const cesiumViewer = viewer;
          console.log('cesiumViewer', cesiumViewer)

          const tt = this;
          cesiumViewer.scene.preRender.addEventListener(() => {
            if (!tt.popVisible) return;
            const scratch = Cesium.Cartesian2();
            const position = Cesium.Cartesian3.fromDegrees(
              Number(tt.overlayChartObj.longitude),
              Number(tt.overlayChartObj.latitude),
              300
            );
            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";
        },
        closeMapPop() {
          this.popVisible = false;
          // 清除监听事件
          viewer.scene.postRender.removeEventListener(
            this.bindMapMove
          );
        },
        // 主动触发点位弹窗
        mapClick() {
          this.handlePinClick(this.list[0], 'site_layer') // 点位数据 及弹窗类型
        },
      }
    })
  </script>

</body>

</html>

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Cesium点位弹窗跟随点位移动是指在Cesium地图上,当我们点击一个具体的点位时,会弹出一个弹窗,同时该弹窗会随着点位移动而保持在该点位附近。这个功能在展示地理信息的应用中非常常用。 实现这个功能,我们可以借助Cesium的实体(Entity)功能。首先,我们需要创建一个实体来表示点位。实体可以包含很多属性,比如位置、图标、标签等。接下来,在点击该点位时,我们可以通过监听该实体的点击事件来触发弹窗的显示和隐藏。同时,我们还需要监听实体的移动事件,以便在点位移动时,及时更新弹窗位置。 具体地,我们可以使用Cesium的Viewer对象来创建地图,然后创建一个Cesium.Entity对象来表示我们的点位。接着,我们可以通过添加监听事件来实现点击弹窗跟随移动的功能。当点击该点位时,我们可以通过设置弹窗的CSS样式来实现显示,同时设置弹窗位置为当前点位位置。当点位移动时,我们可以监听实体的位置属性的改变,通过更新弹窗位置属性来实现跟随移动,即弹窗位置始终保持在点位的附近。 总结来说,实现Cesium点位弹窗跟随点位移动的功能,需要通过监听实体的点击事件来显示和隐藏弹窗,同时通过监听实体的位置属性的改变来更新弹窗位置。这样就可以实现一个点位弹窗,在点位移动时保持在点位的附近随着点位移动的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值