WebGL百度地图实现涟漪图

690 阅读2分钟

一、百度地图API调用

  1. 百度地图API地址:lbsyun.baidu.com/index.php?t…
  2. 需要先申请密钥
    • 进入官网,找到【申请密钥(ak)】,并点击进去;
    • 根据开发需求选择【更为个人开发者】还是【成为企业开发者】;
    • 按照步骤提示进行注册;
    • 注册完成后,在个人控制台,找到【应用管理】-【我的应用】-【创建应用】,填写创建应用信息;
    • 创建应用完成,会生成一个【访问应用AK】,然后我们就可以用这个AK去调用百度API了。
  3. 通过script标签实现百度Api文件在index.html的头部引入,之后就可以调用百度API了,简单初始化地图demo。(vue下实现的例子,例子中的ak需要替换成自己的申请下来的ak) image.png
    loadMap () {
          return new Promise((resolve, reject) => {
            const script = document.createElement('script')
            script.src = `https://api.map.baidu.com/api?v=1.0&type=webgl&ak=${this.ak}&callback=initialize`
            document.body.appendChild(script)
            window.initialize = function () {
              setTimeout(() => {
                // eslint-disable-next-line
                resolve(BMapGL)
              }, 3000)
            }
          })
        }
    

二、地图主题更改

  1. 进入百度api官网,lbsyun.baidu.com/index.php?t… ,选择【友情链接】-【个性化地图编辑器】,跳转至【个性化地图编辑平台】,点击【开始使用】
  2. 进入【个性化地图】-【新建】
  3. 进入样式新建/编辑页面,通过【样式模版】【配色推荐】【识图配色】来配置合适的样式;也可以通过编辑JSON,直接更改样式,如果需要UI同学帮忙配置地图主题,通过JSON是很方便的交流方式。配置完成,点击【发布样式】,即可生成样式ID,复制后,在代码中引入即可。备注:再次编辑更新样式,样式ID不变。 image.png
    map.setMapStyleV2({
      styleId: 'XXXXXXXXXXXXXXXXXXX30084627'
    })
    

三、高亮选中城市

  1. 通过点击事件,给当前城市添加一个棱柱覆盖物,从而实现高亮当前城市的效果。百度地图棱柱参数:mapopen-pub-jsapi.bj.bcebos.com/jsapi/refer…
  2. 核心实现代码
    // 地图添加点击事件
    map.addEventListener('click', e => {
      this.addPrism(e)
    })
    
    // 高亮当前点击城市
    addPrism (e, custom) {
      // 获取点击位置经纬度
      const pt = e.latlng
      // 根据经纬度确定在哪个城市
      const geocode = new this.BMapGL.Geocoder()
      geocode.getLocation(pt, rs => {
        const addComp = custom || rs.addressComponents
        // 如果当前城市是已选中城市,不做处理
        if (addComp.city === this.pickCity && this.prisms && this.prisms.length) {
          return
        }
        this.pickCity = addComp.city
        for (const prismOld of this.prisms) {
          this.map.removeOverlay(prismOld)
        }
        const boundary = new this.BMapGL.Boundary()
        boundary.get(addComp.city, rs => {
          const boundaries = rs.boundaries
          // 颜色根据需求定制化
          const boundariesTop = []
          const boundariesDashed = []
          boundaries.forEach(bound => {
            const arr = bound.split(';')
            let top = ''
            let dashed = ''
            arr.forEach((item, index) => {
              const a = item.split(', ')
              const comma = index === arr.length - 1 ? '' : ';'
              top = top + `${a[0]}, ${Number(a[1]) + 0.03}${comma}`
              dashed = dashed + `${a[0]}, ${Number(a[1]) + 0.015}${comma}`
            })
            boundariesTop.push(top)
            boundariesDashed.push(dashed)
          })
          const prismTop = new this.BMapGL.Polygon(boundariesTop, {
            fillColor: 'rgba(164,227,206,0.005)',
            fillOpacity: 0,
            strokeColor: 'rgba(164,227,206)',
            strokeWeight: 2
          })
          const prismDashed = new this.BMapGL.Polygon(boundariesDashed, {
            fillColor: 'rgba(164,227,206,0.005)',
            fillOpacity: 0,
            strokeStyle: 'dashed',
            strokeColor: 'rgba(164,227,206)',
            strokeWeight: 1,
            strokeOpacity: 0.8
          })
          const prismBottom = new this.BMapGL.Polygon(boundaries, {
            fillColor: 'rgba(164,227,206,0.005)',
            fillOpacity: 0.46,
            strokeColor: 'rgba(164,227,206)',
            strokeWeight: 1,
            strokeOpacity: 0.3
          })
          this.prisms.push(prismTop)
          this.prisms.push(prismDashed)
          this.prisms.push(prismBottom)
          this.map.addOverlay(prismBottom)
          this.map.addOverlay(prismDashed)
          this.map.addOverlay(prismTop)
        })
      })
    }
    
  3. 实现效果 image.png

四、自定义工具栏

  1. 自定义工具类,调用百度api的ZoomControl类,根据我们需要的功能和样式排版,给我们的地图添加自定义的工具栏,官方demo实例地址:lbsyun.baidu.com/jsdemo.htm#…
  2. 核心代码实现
    addControl () {
      // 定义一个控件类
      function ZoomControl (that) {
        this.defaultAnchor = 'BMAP_ANCHOR_TOP_LEFT'
        this.defaultOffset = new that.BMapGL.Size(10, 10)
      }
      // 通过JavaScript的prototype属性继承于BMap.Control
      ZoomControl.prototype = new this.BMapGL.Control()
      // 自定义控件必须实现自己的initialize方法,并且将控件的DOM元素返回
      // 在本方法中创建个div元素作为控件的容器,并将其添加到地图容器中
      ZoomControl.prototype.initialize = map => {
        // 工具层
        const mapTool = document.createElement('div')
        mapTool.className = 'map-tool-buttons'
        const tools = [
          {
            class: 'focus tool-btn',
            text: '定位',
            method: () => {
              map.centerAndZoom(this.pickCity, 10)
            }},
          {
            class: 'zoomIn tool-btn',
            text: '放大',
            method: () => {
              map.setZoom(map.getZoom() + 1)
            }
          },
          {
            class: 'zoomOut tool-btn',
            text: '缩小',
            method: () => {
              map.setZoom(map.getZoom() - 1)
            }
          }
        ]
        tools.forEach(item => {
          const btn = document.createElement('div')
          btn.className = item.class
          btn.textContent = item.text
          btn.onclick = item.method
          mapTool.appendChild(btn)
        })
    
        // 添加DOM元素到地图中
        map.getContainer().appendChild(mapTool)
        // 将DOM元素返回
        return mapTool
      }
      // 创建控件元素
      const myZoomCtrl = new ZoomControl(this)
      // 添加到地图中
      this.map.addControl(myZoomCtrl)
    }
    
  3. 实现效果 image.png

五、实现涟漪点

  1. 实现涟漪图,需要引入mapvgl依赖文件,code.bdstatic.com/npm/mapvgl@…

    loadMap () {
      return new Promise((resolve, reject) => {
        const mapvglScript = document.createElement('script')
        mapvglScript.type = 'text/javascript'
        mapvglScript.src = 'https://code.bdstatic.com/npm/mapvgl@1.0.0-beta.152/dist/mapvgl.min.js'
        mapvglScript.onerror = reject
        document.head.appendChild(mapvglScript)
    
        const baiduApi = document.createElement('script')
        baiduApi.src = `https://api.map.baidu.com/api?v=1.0&type=webgl&ak=${this.ak}&callback=initialize`
        document.body.appendChild(baiduApi)
        window.initialize = function () {
          // eslint-disable-next-line
          resolve(BMapGL)
        }
      })
    }
    
  2. 涟漪图官网demo参考:mapv.baidu.com/gl/docs/Cir…

  3. 核心代码实现

    addCircle () {
      // eslint-disable-next-line
      const view = new mapvgl.View({
        map: this.map
      })
      // eslint-disable-next-line
      const waveLayer = new mapvgl.CircleLayer({
        // 绘制带波纹扩散的圆
        type: 'wave',
        // 扩散半径,支持直接设置和回调两种形式
        radius: r => 6 * r,
        // 周期影响扩散速度,越小越快
        duration: 1 / 3,
        // 时间
        random: true,
        // 拖尾影响波纹数,越大越多
        trail: 2,
        enablePicked: true,
        onClick: e => { // 点击事件
          console.log(e)
        }
      })
      const data = []
      for (let i = 0; i < 2000; i++) {
        const point = [this.randomNumber(80, 120), this.randomNumber(28, 44)]
        data.push({
          geometry: {
            type: 'Point',
            coordinates: point
          },
          color: '#D05428',
          // 圆的半径
          size: 2
        })
      }
      view.addLayer(waveLayer)
      waveLayer.setData(data)
    },
    // 随机经纬度
    randomNumber (min, max) {
      const range = max - min
      const rand = Math.random() * 100
      return min + rand * range / 100
    }
    
  4. 实现效果(存在bug,如果涟漪点过多,会有部分涟漪点没有动画效果,只显示一个小点,应该是高并发动画存在的bug)

    image.png

六、代码及文档地址

  1. gitee:gitee.com/wanglixiang…
  2. demo在线地址:wanglixiang_z.gitee.io/bmap-gl/#/
  3. 百度地图api官网:lbsyun.baidu.com/index.php?t…
  4. 地图核心类参数地址:mapopen-pub-jsapi.bj.bcebos.com/jsapi/refer…
  5. mapvgl官网:mapv.baidu.com/gl/docs/Cir…