项目背景

在使用高德地图做地图相关开发时,经常会使用到标记点marker功能,如果业务较复杂,标记点需要增加自定义的信息窗口infoWindow,自定义信息窗口支持传入content参数,content 可以是 dom 对象,也可以是 html 识别的字符串。

自定义最大的问题就是事件不好处理,比如关闭信息窗的点击事件。在官方的示例中,自定义使用的原生dom节点创建并添加事件,对于复杂场景,这样操作就显得较为复杂,可读性和拓展性都很差。这里可以使用es6的模板语法做html字符串拼接,把需要绑定事件的元素设置ID,在创建信息窗后,通过addEventListener进行点击事件绑定。

在更复杂的业务场景中,es6的模板语法显得力不从心,比如我想使用element-ui组件,自定义的svg图标组件。为了更灵活的进行信息窗自定义,我们可以选择使用vue组件作为自定义信息窗的content。首先我们要理清逻辑,假如有多个标记点,信息窗是在标记点被点击后显示,那么我们就在标记点上添加一个点击事件,点击时,创建信息窗并显示。而不要在创建标记点时就把信息窗创建了。下面是伪代码:

伪代码

// 父组件
<template>
    <div class="container">
        <div v-loading="mapLoading" class="wrap">
            <div id="container" style="width: 100%; height: 100%"></div>
			<!-- 自定义组件InfoWindow,初始时需要隐藏 -->
            <!-- 隐藏不要使用v-if,因为我们需要渲染完成后的原生html结构作为信息框的dom对象使用 -->
            <InfoWindow v-show="showInfoWindow" ref="infoWindow" :info-window="infoWindow"></InfoWindow>
        </div>
    </div>
</template>

<script>
  //引入定义的信息窗组件
  import InfoWindow from './components/InfoWindow'

  export default {
    components: {
      // 注册组件
      InfoWindow,
    },
    data() {
      return {
        // 组件加载后隐藏
        showInfoWindow: false,
        infoWindow: {}
      }
    },
    methods: {
      addMarker() {
          // 创建标记点Marker
          let labelMarker = new this.AMap.LabelMarker({
              icon: 'icon路径',
              position: 'position定位点'
          })
          // labelMarker都绑定上点击事件
          labelMarker.on('click', (e) => {
              // 点击后创建自定义信息窗口
              this.setInfoWindows(title, content, e)
              // 把地图中心点设置为当前点击的标记点
              this.map.setZoomAndCenter(this.zoom, item.position)
          })
      },
      setInfoWindows(title, content, e) {
          // 此时需要把组件的样式设置为可见
          this.showInfoWindow = true
          // 设置marker头部的标题信息窗口
          const infoWindow = new this.AMap.InfoWindow({
              // 使用自定义窗体
              isCustom: true,
              // 只有当组件渲染完毕后,通过$el才能拿到原生的dom对象
              content: this.$refs['infoWindow'].$el, 
              // 设置定位偏移量
              offset: new this.AMap.Pixel(-9, -60)
          })
          this.infoWindow = infoWindow
          // 信息窗口打开
          infoWindow.open(this.map, e.target.getPosition())
       }
    }
  }
</script>
// 信息窗口子组件

<template>
  <div>
    <el-card class="box-card" style="padding: 0 80 30 80;width: 400px;border-radius: 10px;">
      <div id="del-div">
        <el-link type="primary" icon="el-icon-close" @click="close()"></el-link>
      </div>
      <div style="text-align: center;">
        <el-button type="primary">主要按钮</el-button>
      </div>
  </div>
</template>
<script>
export default {
    props: {
        infoWindow: {
            type: Object,
            default: () => {}
        },
    },
    methods: {
    // 关闭
        close() {
            // 高德地图信息窗关闭的api
            this.infoWindow.close()
        },
        edit() {
            console.log('编辑按钮测试')
        },
        del() {
            console.log('删除按钮测试')
        }
    }
}
</script>
 
<style lang="css" scoped>
#del-div {
  position: absolute;
  right: 20;
  top: 20;
  transform: scale(1.2);
}
</style>

把信息窗口封装成vue组件后,灵活性更强,其它组件都可以在里面使用。但是要注意,上面都是伪代码,需要理解后根据实际业务把代码加进去。

参考博客:

vue 高德地图api爬坑之路(四) 自定义infoWindow信息窗体

Logo

前往低代码交流专区

更多推荐