Vue整合EsMap

一、EsMap场景整合到Vue

1.1. 生成EsMap本地文件

1.1.1. 进入EsMap数字孪生三维可视化云平台

打开链接https://www.esmap.cn/esmapv/content/cn/member/index.html
image

1.1.2. 新建三维场景

点击新增三维场景,输入信息,点击下一步即可。

  • 其中三维场景ID和建筑token在web端需要使用
  • ID为项目工程名
    image

1.1.3. 绘制三维场景

在画板上添加元素后,点击提交即可。
image

1.1.4. 绘制场景导航

打开导航编辑。
image
点击开始绘制,在图层上进行描点,描点后点击提交即可。
image
如需删除点或者导航线,结束绘制后,左键单击选择需要修改的点或线,再右键打开选择列操作即可。
image
提交完后,点击模拟导航,即可查看导航线是否生效。
image

1.1.5. 下载三维场景

回到场景页面,点击下载场景即可。
image
下载后的文件如下:
image

1.2. 整合Vue

1.2.1. 将下载的场景文件放到/public/data文件夹下

image

1.2.2. 添加如下代码到/public/index.html中

<script src="./libs/esmap-3.0.min.js"></script>

1.2.3. 在页面中引入esmap

  • 先通过new window.esmap.ESMap生成map模型数据,再通过this.map = map使得可以在vue任何地方调用map模型的方法,对3D模型进行操作。
  • 通过map.on('mapClickNode', (e) => {})可绑定每次点击模型触发的方法。
<template>
  <div id='map-container'></div>
</template>

<script>
let that
export default {
  mounted() {
    that = this;

    // 生成3D地图
    this.loadMap();
  },
  methods: {
    loadMap() {
      var map = new window.esmap.ESMap({
        mode: window.esmap.MapMode.Building,       // *配置三维场景模式* Building为室内模式,City为城市模式
        container: document.getElementById('map-container'),  // 三维场景显示容器
        mapDataSrc: './data',               // 离线三维场景数据位置
        token: '210323',                    // 打开三维场景对应的token(即创建场景的token)
      });
      map.openMapById('parkingLot_01');     // 创建场景的ID

      // 地图点击事件
      map.on('mapClickNode', (e) => {
        console.log(e);

      })

      this.map = map;
    }
  }
}

二、EsMap相关API整合

2.1. 绘制线标注

  • 实现esmap的drawLineMark(linemark, callback)方法。
    • linemark:esmap.ESLineMarker对象。
    • callback:回调方法
  • 该方法需在map.on('loadComplete', function () {})中调用。
    makeLine() {
      // 点坐标集合
      let points = [{x: 12723047.070994029, y: 3579191.674298213}, {x: 12723061.788526496, y: 3579214.131607668}]

      // 在三维场景加载完成事件中绘制线标注
      this.map.on('loadComplete', function () {
        let lineMarker = new window.esmap.ESLineMarker({
          id: 1,
          name: 'lineMarker',
          points: points,
          style: {
            lineWidth: 6,       // 线宽度
            alpha: 0.8,         // 线透明度
            offsetHeight: 0,    // 向上偏移量(距离地面高度,默认为0)
            lineType: esmap.ESLineType.FULL, // 线样式
            pickable: true,     // 是否支持选中
            color: '#FF0000',   // 线条颜色
            smooth: true,       // 曲线是否光滑
            fixedWidth: false,  // 缩放时线宽是否保持不变
            lineWidthMeter: false, // 线宽是否代表真实宽度
            seeThrough: false,  // 线是否穿透显示
          },
        })
        that.map.drawLineMark(lineMarker)
      })
    },

线标注样式说明

image

2.2. 修改模型颜色

  • 该方法需在map.on('loadComplete', function () {})中调用。
    map.on('loadComplete', function () {
        map.changeModelColor({
            id: 3068654,            //模型ID,id可为数组
            //name: '停车场',        //模型name
            //fnum: 1,              //建筑楼层,可为数组
            color: '#00ff00'
        })
    });

2.3. 添加气泡标注

  • 通过esmap.ESPopMarker对象生成。
  • 该方法需绑定在左键点击触发事件上,或者button按钮点击事件上(暂只测试了这两种,不通过这两种方式,直接默认事件调用会报错)。
  • 该方法无需使用map.on('loadComplete', function () {}),否则无法生效。
    let pop = {x: 123, y: 321};

    var popMarker = new esmap.ESPopMarker({
        mapCoord: {
            x: pop.x,  // 坐标
            y: pop.y,
            height: 4,        // 距离地面高度
            fnum: 1           // 所在楼层
        },
        width: 300,           // 气泡标注宽度
        height: 100,          // 气泡标注高度
        content: `HTML代码<a target='_bank' href='http:// www.esmap.cn'>易景空间官方网站</a>`,    // 气泡框的内容,支持输入HTML代码
        closeCallBack: function () {  // 信息窗点击关闭后的回调函数
        },
        created: function () {        // 创建完成的回调函数
        }
    });

2.4. 添加圆形标注

  • 通过esmap.ESPolygonMarker生成。
  • 该方法需在map.on('loadComplete', function () {})中调用。
  • map.on('loadComplete', function () {})中调用map的方法不能用this,要用that.map
    let that = this
    this.map.on('loadComplete', function () {
        var marker = new esmap.ESPolygonMarker({
            name: "圆形标注",
            id: 20,
            color: 'rgb(0, 0, 255)',    //设置颜色
            alpha: 0.3,     //设置透明度
            lineWidth: 2,   //设置边框线的宽度
            height: 6.2,    //设置高度
            points: {
                type: 'circle',     //设置为圆形
                center: {           //设置此形状的中心坐标
                    x: coord.x + 20,
                    y: coord.y - 20
                },
                radius: radius,     //设置半径
                segments: 40,       //设置段数,默认为40段
            }
        });
        var building = that.map.getBuilding();
        // 获取第一层的楼层对象
        var floor = building.getFloor(1);
        // 获取或者新建一个name为'mypoly'的多边形标注层
        var layer = floor.getOrCreateLayerByName('mypoly', esmap.ESLayerType.POLYGON_MARKER);
        layer && layer.addMarker(marker);
    })

2.5. 添加矩形标注

  • 通过esmap.ESPolygonMarker生成。
  • 该方法需在map.on('loadComplete', function () {})中调用。
  • map.on('loadComplete', function () {})中调用map的方法不能用this,要用that.map
    let that = this;

    this.map.on('loadComplete', function () {
        // 创建一个多边形对象
        var rectangleMarker = new esmap.ESPolygonMarker({
            id: 'polygon1',        // 标注ID
            name: '多边形标注',     // 标注名称
            color: '#9F35FF',      // 填充颜色
            opacity: 0.3,          // 透明度
            height: 6,             // 距离地面高度
            points: {
                type: 'rectangle', // 设置为矩形
                center: {          // 设置此形状的中心坐标
                    x: that.map.center.x,
                    y: that.map.center.y
                },
                width: 3,         // 设置矩形宽度
                height: 4         // 设置矩形长度
            }
        });
        var building = that.map.getBuilding();
        // 获取第一层的楼层对象
        var floor = building.getFloor(1);
        // 获取或者新建一个name为'mypoly'的多边形标注层
        var layer = floor.getOrCreateLayerByName('mypoly', esmap.ESLayerType.POLYGON_MARKER);
        // 添加标注
        layer.addMarker(rectangleMarker);
    });

2.6. 删除矩形和圆形标注

  • layer为多边形标注层实例对象
    layer.remove(marker)   // 删除某个标注(marker是指由new esmap.ESPolygonMarker生成的多边形实例对象)
    layer.removeAll()      // 删除该图层下所有标注

2.7. 视角飞行动画

    // 获取点击元素所处的楼层计算出高度
    const height = this.map.focusBuilding.getFloorHeight(1)

    // 聚焦点击元素所在的位置
    this.map.cameraFlyTo({
        directionAngle: 20.16,    // 视角朝向的方向角
        pitchAngle: 90.85,        // 视角朝向的俯仰角
        radius: 100.4,            // 视角中心目标点至相机位置本身的距离
        time: 1.5,                // 视角移动飞行时间,单位(秒/s)
        x: popConfig.mapCoord.x,  // 聚焦点x轴
        y: popConfig.mapCoord.y,  // 聚焦点y轴
        z: height,                // 聚焦点高度
        callback: () => {         // 飞行动画结束后的回调函数
        }
    })

2.8. 绑定dom元素到气泡上

2.8.1. 获取esmap点击事件

  • 此处通过esmap的点击事件触发气泡。
  • 所以在点击事件触发后收集点击位置的值。
    // 绑定esmap点击事件
    map.on('mapClickNode', (e) => {
        // 存储点击元素的配置到onlyPopConfig响应式对象,生成单个气泡子组件
        this.onlyPopConfig.value = e;
    })

2.8.2. dom元素封装

  • 此处是将组件popDialog绑定到气泡上,直接使用html元素也可以。
  • 组件外层需要一层container层,此层级用于包裹esmap图层(使用绝对定位覆盖esmap图层),使得popDialog组件可以在esmap相对图层上偏移。
  • 需给container层设置一个父级home层,此层级使用相对定位,确保popDialog组件偏移位置
    <div class="home">
      <!-- esmap图层 -->
      <div id='map-container'></div>

      <div class="container">
        <!-- 单个气泡组件,点击事件触发后,才生成气泡 -->
        <popDialog
            v-if="onlyPopConfig.value"
            :popData="onlyPopConfig.value"
            :key="onlyPopConfig.value"
        ></popDialog>
      </div>
    </div>

css代码

.home {
  box-sizing: border-box;
  margin: auto;
  position: relative;     /*home层设置相对定位,使得dom元素的偏移位置和气泡位置一致*/
  overflow: hidden;
}

.container {
  position: absolute;     /*container层设置绝对定位,覆盖esmap图层*/
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;   /*此层级不可点,防止层级覆盖esmap图层后,导致esmap图层不可点击*/
  overflow: hidden;
}

2.8.3. 生成气泡,将dom元素的偏移位置绑定到气泡生成的位置

  • 该步骤在组件内完成。
  • 组件也必须为绝对定位。
  • 动态设置组件的偏移位置,即气泡的生成位置。
  • 通过v-show="popTips.visible"根据气泡生成实时展示数据。
  <div
      class="pop_container"
      :style="{
      position: 'absolute',               // 必须设置绝对定位才能对应到地图场景上
      transformOrigin: 'bottom',          // 固定transform的原点位置,避免偏移方向出错
      transform: popTips.transformStyle   // 通过气泡的onUpdatePosition方法动态绑定偏移位置
    }"
  >
    <div v-show="popTips.visible">
      <!-- 以下均为气泡需要展示的内容,以下为可参考内容 -->
      <div class="pop_extend">
        <!-- 标题栏 -->
        <div class="pop_title">
          <span>标题</span>
          <li @click="hidePop">X</li>
        </div>
        <!-- 具体内容栏 -->
        <div class="pop_detail">
          我是绑定到气泡上的组件
        </div>
      </div>

      <!-- 底部线条 -->
      <div
          class="pop_line"
          :style="{
                backgroundImage:
                  'linear-gradient(to bottom, #1594FF, rgba(0, 0, 0, 0))',
                height: popData.height + 'px  '
              }"
      ></div>
    </div>
  </div>

js

  • 此处用的是vue3的写法。
  • 即在组件初始化的时候,生成气泡,再将页面dom元素的偏移位置与气泡位置绑定。
import {reactive, onMounted, onUnmounted} from 'vue'

export default {
  // 组件名称
  name: 'popDialog',
  // 组件参数 接收来自父组件的数据
  props: {
    popData: {
      type: Object, default: () => {
      }
    }
  },
  // 组件状态值
  setup(props) {
    // 定义该子组件的偏移和显隐的响应式对象
    let popTips = reactive({
      transformStyle: '',
      visible: false
    })

    // 定义气泡标注对象
    let PopMarker = reactive({})

    onMounted(() => {
      // 初始化生命周期执行气泡初始化
      initPop()
    })

    onUnmounted(() => {
      // 销毁组件生命周期执行气泡销毁
      PopMarker.close()
      PopMarker = null
    })

    // 初始化气泡标注并将位置信息绑定到该子组件的popTips响应式对象上
    function initPop() {

      // 根据父组件传的popData值,生成气泡标注
      PopMarker = new esmap.ESPopMarker({
        mapCoord: {
          x: props.popData.mapCoord.x,
          y: props.popData.mapCoord.y,
          height: 1,
          fnum: props.popData.FloorNum || 1,
        },
        bid: props.popData.bid,
        content: props.popData.html,
        className: props.popData.className,
        showLevel: props.popData.showLevel,
        updateSize: props.popData.updateSize,
        created: function () {
          // 将气泡标注的偏移位置绑定到该子组件的css属性transformStyle中
          PopMarker.onUpdatePosition((transformStyle, displayStyle) => {
            popTips.transformStyle = transformStyle;
            popTips.visible = displayStyle.display === 'none' ? false : true
          })
        }
      })
    }

    // 隐藏气泡标注,可以在组件内设置点击事件用于关闭气泡
    function hidePop() {
      PopMarker.hide()
    }

    return {popTips, hidePop}
  }
}

css

  • 当前使用的是scss的写法,在vue中需引入。
  • 如需自定义内容,则只需要.pop_container的内容即可,以下全是自定义内容。
  • 如需气泡内的某一元素可点击,在css设置pointer-events: auto;即可。
.pop_container {
  user-select: none;
  pointer-events: none;
  z-index: 98;
  font-size: 40px;
  color: #fff;

  .pop_line {
    position: relative;
    left: 50%;
    transform: translateX(-50%);
    width: 2px;
    height: 50px;
    background-image: linear-gradient(to bottom, #f3ab0f, rgba(0, 0, 0, 0));
    animation: scale-in-bl 0.5s;

    &::after {
      content: '';
      width: 8px;
      height: 8px;
      transform: translateX(-50%);
      position: absolute;
      bottom: 0;
      left: 0;
      background-color: #fff;
      border-radius: 50%;
    }
  }

  .pop_extend {
    backdrop-filter: blur(4px);

    color: #fff;
    animation: shutter-in-top 0.3s 0.3s forwards;
    opacity: 0;
    width: 150px;

    .pop_title {
      height: 25px;
      line-height: 25px;
      font-size: 12px;
      white-space: nowrap;
      display: flex;
      justify-content: space-between;
      align-items: center;
      box-sizing: border-box;
      background-color: #325f9c;
      padding: 0 10px;

      & > li {
        border: 1px solid #fff;
        padding: 2px;
        width: 10px;
        height: 10px;
        line-height: 10px;
        text-align: center;
        cursor: pointer;
        pointer-events: auto;    /* 设置关闭按钮为可点击元素 */
        list-style: none;        /* 无序列表前面的点不显示*/
      }
    }

    .pop_detail {
      background-color: #40669ca2;
      white-space: nowrap;
      pointer-events: auto;
    }
  }
}

@keyframes scale-in-bl {
  0% {
    transform: scaleY(0) translateX(-50%);
    opacity: 1;
  }

  100% {
    transform: scaleY(1) translateX(-50%);
    opacity: 1;
  }
}

@keyframes shutter-in-top {
  0% {
    transform: rotateX(-100deg);
    opacity: 0;
  }

  100% {
    transform: rotateX(0deg);
    opacity: 1;
  }
}

2.9. 生成导航线

  • 要生成两点之间的导航线,必须在esmap图层中绘制导航线!(详情参考)。
  • 通过esmap.ESNavigation生成。
  • 该方法必须在this.map.on('loadComplete', function () {})中调用。
    let that = this;

    this.map.on('loadComplete', function () {
        // 初始化导航对象
        var navi = new esmap.ESNavigation({
            lineStyle: {             // 路径规划线样式配置
                color: '#33cc61',         // 导航线颜色
                lineType: esmap.ESLineType.ESARROW, // 设置导航线样式
                lineWidth: 3,             // 设置导航线的宽度
                offsetHeight: 0.5,          // 设置导航线的高度
                smooth: true,             // 设置导航线的转角线平滑效果
                seeThrough: false,        // 设置导航线的穿透楼层地板总是显示的效果
                noAnimate: false,         // 设置导航线的动画效果
                godEdgeColor: '#920000',  // 设置边线的颜色
                godArrowColor: '#ff0000'  // 设置箭头颜色
            },
        });

        // 获取楼层数,building实例对象生成详情可参考【2.10.1. 相关实例对象生成】
        var fnum = that.building.getFloorNumByName('F1');

        // 确定起点
        navi.setStartPoint({
            x: 12723034.833164,
            y: 3579182.2929972,
            fnum: fnum,    // 楼层
            height: 0,    // 起点距离地面的高度
            url: 'image/startPoint.png', // 图片路径
            size: 64,      // 图片尺寸
            showLevel: 23  // 到达三维场景缩放等级隐藏导航对象
        });

        // 确定终点
        navi.setEndPoint({
            x: 12723057.4100803,
            y: 3579213.7764181,
            fnum: fnum,
            height: 0,
            url: 'image/endPoint.png',
            size: 64,
            showLevel: 23
        });

        navi.getRouteResult({
            drawRoute: true,    // 绘制导航线
        })
    })

2.10. 其他相关方法

2.10.1. 相关实例对象生成

  • 该对象需在map.on('loadComplete', function () {})中生成。
    let that = this;

    map.on('loadComplete', function () {
        that.building = map.getBuilding();

        // 获取第一层的楼层对象
        var floor = that.building.getFloor(1);

        // 获取或者新建一个name为'mypoly'的多边形标注层,用于绘制多边形标注
        that.layer = floor.getOrCreateLayerByName('mypoly', esmap.ESLayerType.POLYGON_MARKER);
    })

2.10.2. 相关图层工具生成

    map.showCompass = true;     //显示指北针
    map.showScaler = true;     // 显示三维场景比例尺,默认显示

    // 1.声明楼层控件配置参数
    var ctlOpt = new esmap.ESControlOptions({
        // LEFT_TOP: 左上方, LEFT_BOTTOM: 左下方, RIGHT_TOP: 右上方, RIGHT_BOTTOM: 右下方
        position: esmap.ESControlPositon.RIGHT_TOP, // 控件放置位置
        // 位置x,y的偏移量
        offset: {
            x: 0,
            y: 0
        },
        textColor: '#000',       // 字体颜色
        toggleColor: '#1F6ED4',  // 单层多层按钮和2d3d按钮颜色
        bgColor: '#FFF',         // 背景颜色
        borderRadius: 5,         // 圆角
        focusTextColor: '#FFF',  // 聚焦楼层字体颜色
        focusBgColor: '#1F6ED4', // 聚焦楼层背景颜色
        scale: 1,                // 放大比例
        showBtnCount: 3,        // 显示按钮个数
        expandBtn: true,        // 显示楼层展开按钮
        expandSpan: 20,         // 楼层展开高度
        viewModeBtn: true,      // 显示2D/3D切换按钮
        fnumToggleBtn: true     // 显示多楼层切换按钮
    });

    // 2.在三维场景加载完成事件中新建楼层控制控件对象
    map.on('loadComplete', function () {
        var floorControl = new esmap.ESScrollFloorsControl(map, ctlOpt);

        // 动态设置控件位置
        floorControl.x = 0
        floorControl.y = 0
    })
posted @ 2023-03-31 11:55  Pearl...T  阅读(495)  评论(0编辑  收藏  举报