vue + cesium 实现 Entity实例生命周期和vue组件生命周期融合

本文介绍如何将Cesium中的Entity实例生命周期与Vue组件生命周期相结合,通过封装Vue组件实现弹窗展示及交互功能,提高代码可读性和维护性。

🚀 个人简介:某大型测绘遥感企业资深Webgis开发工程师,软件设计师(中级)、优快云优质创作者
💟 作 者:柳晓黑胡椒❣️
📝 专 栏:cesium实践(原生)
🌈 若有帮助,还请关注点赞收藏,不行的话我再努努力💪💪💪

场景

ceisum中Entity实例的生成和销毁,大部分逻辑和vue代码分离,导致不好阅读和维护

解决方案

ceisum 中实例 Entity 的生命周期,和vue的生命周期’相似’,把两个生命周期结合(把entity封装为vue组件)

实现方案

  1. vue中 template 中内容可以放置 弹框的内容,通过ceisum 获取屏幕坐标,进行渲染
  2. 通过代码逻辑使 vue组件的 mountedbeforeDestroy 来和 entity 实例的生成 add,和销毁destroy 相关联
  3. 通过 Cesium.ScreenSpaceEventHandler 关联vue组件的方法实现交互

方案解决效果

在这里插入图片描述

index.vue

采用原生cesium 实现entity的弹窗,并封装为vue组件

<!--/**
 * @author: liuk
 * @date: 2023/8/10
 * @describe: 小区视角
 * @email:1229223630@qq.com
*/-->
<template>
  <div>
    <div class="heat-info" v-if="showPopup" :style="{top:popupPos.top,left:popupPos.left}">
      <div class="name">小区</div>
      {{showPopup}}
      <div class="bottom_div">
        <div>
          <div class="num">20.3&nbsp;<span></span></div>
          <div style="margin-top: 6px;">均温</div>
        </div>
        <div>
          <div class="num">92.9&nbsp;<span>%</span></div>
          <div style="margin-top: 6px;">室温</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import communityList from "../data/community.json"

let heatDatasource, preSelEntity,handler
export default {
  data(){
    return {
      showPopup: false,
      popupPos: {
        left: 0,
        top: 0
      },
    }
  },
  methods:{
    addEntity() {
      let features = communityList.features || [];
      features.forEach(el => {
        let pos = Cesium.Cartesian3.fromDegreesArray(el.geometry.coordinates[0].map(el1 => {
          return el1.join(',')
        }).join(',').split(',').map(Number));
        let boundingSphere = new Cesium.BoundingSphere.fromPoints(pos);
        let center = boundingSphere.center;
        heatDatasource.entities.add({
          position: center,
          customType: "communityEntity",
          label: {
            text: el.properties.name,
            disableDepthTestDistance: Number.POSITIVE_INFINITY,
            horizontalOrigin: Cesium.HorizontalOrigin.Top,
            scaleByDistance: new Cesium.NearFarScalar(2000, 1, 500000, 0.1)
          },
          show: true,
          polygon: {
            hierarchy: new Cesium.PolygonHierarchy(pos),
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            material: Cesium.Color.fromCssColorString("white").withAlpha(0),
          },
          polyline: {
            show: true,
            positions: pos,
            width:1.5,
            material: Cesium.Color.fromCssColorString("#C0C0C0").withAlpha(0.5),
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
          }
        });
      })
    },
    onMouseClick(movement){
      var preSelEntity = window.dasViewer.scene.pick(movement.position);
      if (!Cesium.defined(preSelEntity) || !Cesium.defined(preSelEntity.id)) return;
      var entity = preSelEntity.id;
      if (!(entity instanceof Cesium.Entity) || entity.customType !== "communityEntity") return;
      window.dasViewer.camera.flyTo({
        destination: Cesium.Rectangle.fromCartesianArray(entity.polygon.hierarchy.getValue().positions),
        complete: () => {
          this.$emit('hideCommunityEntity')
        }
      });
    },
    onMouseMove(movement){
      var pickedObject = window.dasViewer.scene.pick(movement.endPosition);
      if (!Cesium.defined(pickedObject) || !Cesium.defined(pickedObject.id)) {
        this.resetSelectedEntity();
        return;
      }
      var entity = pickedObject.id;
      if (!(entity instanceof Cesium.Entity) || entity.customType !== "communityEntity") {
        this.resetSelectedEntity();
        return;
      }
      if (entity !== preSelEntity) {
        this.resetSelectedEntity();
        entity.polygon.material = Cesium.Color.fromCssColorString("white").withAlpha(0.1);
        entity.polyline.material = Cesium.Color.fromCssColorString("white").withAlpha(1);
      }
      preSelEntity = entity;
      this.showPopupBox(movement.endPosition);
    },
    onMouseLeave(){
      this.showPopup = false
    },
    resetSelectedEntity(){
      if (preSelEntity) {
        this.showPopup = false
        preSelEntity.polygon.material = Cesium.Color.fromCssColorString("white").withAlpha(0);
        preSelEntity.polyline.material = Cesium.Color.fromCssColorString("#C0C0C0").withAlpha(0.5);
        preSelEntity = null;
      }
    },
    showPopupBox(movement){
      this.showPopup = true
      this.popupPos.left = `${movement.x + 10}px`;
      this.popupPos.top = `${movement.y + 10}px`;
    }
  },
  mounted(){
    const self = this
    heatDatasource = new Cesium.CustomDataSource("community");
    window.dasViewer.dataSources.add(heatDatasource);
    this.addEntity()
    handler = new Cesium.ScreenSpaceEventHandler(window.dasViewer.scene.canvas);
    handler.setInputAction(self.onMouseClick, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    handler.setInputAction(self.onMouseMove, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    handler.setInputAction(self.onMouseLeave, Cesium.ScreenSpaceEventType.MOUSE_LEAVE);
  },
  beforeDestroy() {
    handler.destroy()
    window.dasViewer.dataSources.remove(heatDatasource);
  }
}
</script>


<style lang="less" scoped>
.heat-info {
  position: absolute;
  width: 196px;
  min-height: 124px;
  padding: 12px;
  border: 1px solid rgba(85, 85, 85, 1);
  box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.5);
  border-radius: 4px;
  background-color: rgba(0, 0, 0, 0.8);
  color: #fff;
  z-index: 9999;
  pointer-events: none;

  .name {
    font-family: PingFangSC-Medium;
    font-size: 16px;
    color: #FFFFFF;
    letter-spacing: 0;
    font-weight: 500;
    margin-top: 5px;
  }

  .bottom_div {
    display: flex;
    position: relative;
    justify-content: space-between;
    font-size: 12px;
    color: #A2A3A3;
    letter-spacing: 0;
    font-weight: 400;
    font-family: PingFangSC-Regular;
    margin-top: 21px;

    .num {
      font-size: 20px;
      color: #FFFFFF;
      letter-spacing: 0;
      line-height: 16px;
      font-weight: 400;

      span {
        font-size: 12px;
        color: #FFFFFF;
        letter-spacing: 0;
        font-weight: 200;
        color: #A2A3A3;
      }
    }

  }
}
</style>
以下是使用VueCesium实现从飞机模型视角拍摄地面生成图片并通过WebSocket发送给后端的详细步骤示例代码: ### 实现思路 1. **初始化Cesium场景**:在Vue组件中引入Cesium库,并创建一个Cesium Viewer实例,用于展示地球场景。 2. **添加飞机模型**:在Cesium场景中添加飞机模型,并设置其位置姿态。 3. **从飞机模型视角拍摄地面**:通过设置相机位置方向,使其与飞机模型的视角一致,然后使用Cesium的`Viewer`实例的`canvas`元素的`toDataURL`方法生成图片的Base64编码数据。 4. **通过WebSocket发送图片数据**:创建WebSocket连接,将生成的图片数据发送给后端。 ### 代码示例 ```vue <template> <div id="cesiumContainer"></div> </template> <script> import * as Cesium from 'cesium'; export default { name: 'CesiumVueExample', mounted() { // 初始化Cesium Viewer const viewer = new Cesium.Viewer('cesiumContainer'); // 添加飞机模型 const entity = viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883), model: { uri: 'path/to/airplane.glb' } }); // 设置相机位置方向与飞机模型一致 viewer.trackedEntity = entity; // 拍摄地面生成图片 const captureImage = () => { const canvas = viewer.canvas; const dataUrl = canvas.toDataURL('image/png'); // 创建WebSocket连接 const socket = new WebSocket('ws://your-backend-url'); socket.onopen = () => { // 发送图片数据 socket.send(dataUrl); }; socket.onmessage = (event) => { console.log('Received message from server:', event.data); }; socket.onclose = () => { console.log('WebSocket connection closed'); }; }; // 模拟定时拍摄 setInterval(captureImage, 5000); } }; </script> <style scoped> #cesiumContainer { width: 100%; height: 100vh; } </style> ``` ### 代码解释 1. **初始化Cesium Viewer**:在`mounted`钩子函数中,创建一个Cesium Viewer实例,并将其绑定到`cesiumContainer`元素上。 2. **添加飞机模型**:使用`viewer.entities.add`方法添加飞机模型,并设置其位置模型文件路径。 3. **设置相机视角**:通过`viewer.trackedEntity`属性将相机的视角设置为与飞机模型一致。 4. **拍摄地面生成图片**:使用`viewer.canvas.toDataURL`方法将当前场景的画面转换为Base64编码的图片数据。 5. **通过WebSocket发送图片数据**:创建一个WebSocket连接,并在连接打开后将图片数据发送给后端。 ### 注意事项 - 确保已经正确引入Cesium库,可以通过CDN或npm安装。 - 替换`'path/to/airplane.glb'`为实际的飞机模型文件路径。 - 替换`'ws://your-backend-url'`为实际的后端WebSocket服务地址。 ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值