百度地图爬坑(vue项目)

579 阅读1分钟

项目需求

  • 进入地图地址没传经纬度显示当前定位地址,传入经纬度,使用传入位置
  • 信息弹框定位到地址Marker点上方
  • 长按地址实现地址的复制
  • 点击导航按钮底部弹出地图选择(根据手机系统下载的地图)

image.png

b028e27952535f7ab58829406ebfb1c.jpg

开发前准备

  1. 申请账号和秘钥 百度地图开发平台
  2. 项目根目录index.html中引用地图
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title id="zy-title"></title>
    <script
      type="text/javascript"
      src="https://api.map.baidu.com/api?v=2.0&ak=你申请的秘钥"
    ></script>
  </head>
  <body style="visibility: hidden;">
    <div id="app"></div>
  </body>
</html>

坑一: 基于BMapLib.InfoBox自定义弹框

原因:百度地址自带的信息窗口弹框(InfoWindow)不满足需求的样式,因此需要使用自定义信息窗口,故需要额外引入百度开源类库InfoBox_min.js

百度地址所有开源库:百度开源库地址

<script type="text/javascript" src="https://api.map.baidu.com/library/InfoBox/1.2/src/InfoBox_min.js"></script>

信息弹框实现

     getHtml() {
        const html = `<div class="box-content">
          <div class="box-content-left">
             <span class="address-text double-line-ellipsis">${this.address}</span>
          </div>
          <div class="box-content-right" >
            <button class="btn" id="btn">
              <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="0.3rem" height="0.3rem" viewBox="0 0 30 31" version="1.1">
                <title>导航icon</title>
                  <g id="月底" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
                      <g id="1.2地址详情页备份" transform="translate(-567.000000, -745.000000)" fill="#FFFFFF" fill-rule="nonzero">
                          <g id="编组-2" transform="translate(25.000000, 697.538462)">
                              <g id="编组" transform="translate(500.000000, 18.500000)">
                                  <g id="导航icon" transform="translate(42.000000, 29.000000)">
                                      <path d="M4.89231976,10.7884564 L25.2131095,2.05686699 C26.2279586,1.62079905 27.4041592,2.08999383 27.8402271,3.10484285 C28.0639952,3.62561229 28.0562853,4.21687246 27.8190143,4.73163008 L18.5776304,24.780734 C18.1152486,25.7838675 16.9272137,26.2222333 15.9240802,25.7598515 C15.369156,25.5040661 14.9616056,25.008637 14.8176179,24.4148066 L13.3099188,18.1967912 C13.1460879,17.521124 12.6431229,16.9790163 11.981604,16.7651017 L5.06652675,14.5289816 C4.01554072,14.1891255 3.43905599,13.0616252 3.77891207,12.0106392 C3.95641149,11.4617321 4.36228623,11.0162051 4.89231976,10.7884564 Z" id="路径"/>
                                  </g>
                              </g>
                          </g>
                      </g>
                  </g>
                </svg>
                <span class="btn-txt">导航</span>
              </button>
          </div>
        </div>`
        return html
      },
  async function customInfoBox(map, marker) {
        let infoBox = new BMapLib.InfoBox(map, this.getHtml(), {
          boxStyle: {
            background: '#fff',
            width: '7rem',
            borderRadius: '0.16rem',
            boxShadow: '0 2px 4px 0 rgba(0,0,0,0.2)',
            zIndex: 999,
            padding: '0.4rem 0.2rem',
            align: INFOBOX_AT_TOP
          },
          // 隐藏关闭按钮
          closeIconMargin: '1px -50px 0 0',
          closeIconUrl: closeImage,
          enableAutoPan: true,
          offset: new BMap.Size(0, 17)
        })
        if (this.lastInfoBox && this.lastInfoBox !== null) {
          this.lastInfoBox.close()
          this.lastInfoBox = null
        } else {
          infoBox.open(marker)
          this.lastInfoBox = infoBox
          await this.$nextTick()
          this.handleDom()
        }
      },

坑二:在自定义弹框中添加点击事件没有反应

解决办法: 引入百度的事件包装器库

 <script
      type="text/javascript"
      src="https://api.map.baidu.com/library/EventWrapper/1.2/src/EventWrapper.min.js"
    ></script>

使用:

  /**
       *绑定导航事件&长按复制
       */
    function handleDom() {
        const btn = document.querySelector('#btn')
        if (btn && btn !== null) {
          // 绑定导航事件
          BMapLib.EventWrapper.addDomListener(btn, 'touchend', () => {
            this.navigation()
          })
        }
        const addressDom = document.querySelector('.address-text')
        if (addressDom && addressDom !== null) {
          // 地址长按
          BMapLib.EventWrapper.addDomListener(
            addressDom,
            'touchstart',
            () => {
              clearTimeout(this.loop)
              this.loop = setTimeout(() => {
                addressDom.style.background = `#EBF2FD`
                this.customCopyModal(addressDom)
              }, 1000)
            },
            false
          )
          BMapLib.EventWrapper.addDomListener(
            addressDom,
            'touchend',
            () => {
              clearTimeout(this.loop)
            },
            false
          )
        }
      }

具体需求代码实现

<template>
  <div class="mapPage">
    <div id="allmap" ref="allmap"></div>
  </div>
</template>
<script>
  import BMap from 'BMap'
  import closeImage from '../../assets/archives/close.png'
  import BMapLib from 'BMapLib'
    export default {
    data() {
      return {
        address: '',
        company: '',
        point: {}
      }
    },
    mounted() {
      this.loop = null
      this.lastInfoBox = null
      this.address = this.$route.query.address
      this.company = this.$route.query.company
      this.city = this.$route.query.city
      setTimeout(() => {
        this.baiduMap(this.address, this.company)
      }, 300)
    },
    methods: {
      baiduMap(address) {
        // 实例化百度地图对象
        const that = this
        let map = new BMap.Map(this.$refs.allmap)
        // 设置地图中心=点 默认深圳
        let point = new BMap.Point(114.02597366, 22.54605355)
        map.centerAndZoom(point, 12)
        // 允许滚轮缩放
        map.enableScrollWheelZoom(true)
        map.enableContinuousZoom(true)

        // 将地址解析结果显示在地图上,并调整地图视野
        // lng经度  lat 纬度
        const { lng, lat } = this.$route.query
        if (lng && lat) {
          that.point = new BMap.Point(lng, lat)
          that.createdMarker(that.point, map)
        } else {
          // 创建地址解析器实例
          let myGeo = new BMap.Geocoder()
          myGeo.getPoint(
            address,
            function(point) {
              if (point) {
                that.point = point
                that.createdMarker(point, map)
              } else {
                that.$Toast('您选择的地址没有解析到结果!')
              }
            },
            that.city
          )
        }
      },
      createdMarker(point, map) {
        map.centerAndZoom(point, 16)
        let marker = new BMap.Marker(point)
        map.addOverlay(marker)
        this.customInfoBox(map, marker)
      },
      navigation() {
        if (isQZD()) {
          callApp('mapNavigationAction', {
            title: this.company,
            desc: this.address,
            latitude: this.point.lat,
            longitude: this.point.lng
          })
        } else {
          this.$Toast('非企知道应用')
        }
      },
      copyText() {
        const el = document.createElement('textarea')
        el.value = this.address
        el.setAttribute('readonly', '')
        el.style.cssText = `position:fixed;left:-9999px`
        document.body.appendChild(el)
        const selected = document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false
        el.select()
        document.execCommand('copy')
        document.body.removeChild(el)
        if (selected) {
          document.getSelection().removeAllRanges()
          document.getSelection().addRange(selected)
        }
        this.$toast('复制成功')
      },
      getHtml() {
        const html = `<div class="box-content">
          <div class="box-content-left">
             <span class="address-text double-line-ellipsis">${this.address}</span>
          </div>
          <div class="box-content-right" >
            <button class="btn" id="btn">
              <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="0.3rem" height="0.3rem" viewBox="0 0 30 31" version="1.1">
                <title>导航icon</title>
                  <g id="月底" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
                      <g id="1.2地址详情页备份" transform="translate(-567.000000, -745.000000)" fill="#FFFFFF" fill-rule="nonzero">
                          <g id="编组-2" transform="translate(25.000000, 697.538462)">
                              <g id="编组" transform="translate(500.000000, 18.500000)">
                                  <g id="导航icon" transform="translate(42.000000, 29.000000)">
                                      <path d="M4.89231976,10.7884564 L25.2131095,2.05686699 C26.2279586,1.62079905 27.4041592,2.08999383 27.8402271,3.10484285 C28.0639952,3.62561229 28.0562853,4.21687246 27.8190143,4.73163008 L18.5776304,24.780734 C18.1152486,25.7838675 16.9272137,26.2222333 15.9240802,25.7598515 C15.369156,25.5040661 14.9616056,25.008637 14.8176179,24.4148066 L13.3099188,18.1967912 C13.1460879,17.521124 12.6431229,16.9790163 11.981604,16.7651017 L5.06652675,14.5289816 C4.01554072,14.1891255 3.43905599,13.0616252 3.77891207,12.0106392 C3.95641149,11.4617321 4.36228623,11.0162051 4.89231976,10.7884564 Z" id="路径"/>
                                  </g>
                              </g>
                          </g>
                      </g>
                  </g>
                </svg>
                <span class="btn-txt">导航</span>
              </button>
          </div>
        </div>`
        return html
      },
      async customCopyModal(addDom) {
        const div = document.createElement('div')
        div.className = 'copy-box'
        const span = document.createElement('span')
        span.className = 'copy-text'
        span.textContent = '复制'
        BMapLib.EventWrapper.addDomListener(
          div,
          'touchend',
          () => {
            this.copyText()
            addDom.style.background = `#fff`
            addDom.removeChild(div)
          },
          false
        )
        div.appendChild(span)
        addDom.insertBefore(div, null)
      },
      async customInfoBox(map, marker) {
        let infoBox = new BMapLib.InfoBox(map, this.getHtml(), {
          boxStyle: {
            background: '#fff',
            width: '7rem',
            borderRadius: '0.16rem',
            boxShadow: '0 2px 4px 0 rgba(0,0,0,0.2)',
            zIndex: 999,
            padding: '0.4rem 0.2rem',
            align: INFOBOX_AT_TOP
          },
          // 隐藏关闭按钮
          closeIconMargin: '1px -50px 0 0',
          closeIconUrl: closeImage,
          enableAutoPan: true,
          offset: new BMap.Size(0, 17)
        })
        if (this.lastInfoBox && this.lastInfoBox !== null) {
          this.lastInfoBox.close()
          this.lastInfoBox = null
        } else {
          infoBox.open(marker)
          this.lastInfoBox = infoBox
          await this.$nextTick()
          this.handleDom()
        }
      },
      /**
       *绑定导航事件&长按复制
       */
      handleDom() {
        const btn = document.querySelector('#btn')
        if (btn && btn !== null) {
          // 绑定导航事件
          BMapLib.EventWrapper.addDomListener(btn, 'touchend', () => {
            this.navigation()
          })
        }
        const addressDom = document.querySelector('.address-text')
        if (addressDom && addressDom !== null) {
          // 地址长按
          BMapLib.EventWrapper.addDomListener(
            addressDom,
            'touchstart',
            () => {
              clearTimeout(this.loop)
              this.loop = setTimeout(() => {
                addressDom.style.background = `#EBF2FD`
                this.customCopyModal(addressDom)
              }, 1000)
            },
            false
          )
          BMapLib.EventWrapper.addDomListener(
            addressDom,
            'touchend',
            () => {
              clearTimeout(this.loop)
            },
            false
          )
        }
      }
    }
  }
</script>

<style lang="scss">
  #allmap {
    .infoBox:before {
      content: '';
      width: 0;
      height: 0;
      border-top: 0.25rem solid #fff;
      border-left: 0.25rem solid transparent;
      border-right: 0.25rem solid transparent;
      position: absolute;
      top: 1.62rem;
      left: 3.28rem;
    }
    .box-content {
      display: flex;
      justify-content: space-between;
      &-left {
        position: relative;
        display: flex;
        align-items: center;
        .copy-box {
          background: #292a2d;
          box-shadow: 0px 8px 16px 0px rgba(68, 77, 164, 0.06);
          width: 0.88rem;
          height: 0.7rem;
          position: absolute;
          top: -0.7rem;
          left: 50%;
          transform: translateX(-50%);
          display: flex;
          justify-content: center;
          align-items: center;
          border-radius: 0.1rem;
          font-size: 0.28rem;
          &:before {
            content: '';
            width: 0;
            height: 0;
            border-top: 0.15rem solid #292a2d;
            border-left: 0.15rem solid transparent;
            border-right: 0.15rem solid transparent;
            position: absolute;
            top: 0.66rem;
            left: 0.3rem;
          }
          .copy-text {
            color: #fff;
          }
        }
        .address-text {
          display: -webkit-box;
          width: 4.56rem;
          font-size: 0.32rem;
          color: #292a2d;
          padding: 2px 0.1rem;
        }
      }
      &-right {
        z-index: 9999;
        .btn {
          display: flex;
          justify-content: center;
          align-items: center;
          z-index: 9999;
          width: 1.8rem;
          height: 0.82rem;
          line-height: 0.82rem;
          background: #3981f4;
          border-radius: 9px;
          text-align: center;
          color: #fff;
          .btn-txt {
            margin-left: 0.1rem;
            font-size: 0.28rem;
          }
        }
      }
    }
  }
</style>