百度地图添加自定义覆盖物(水波纹)动画

1,037 阅读1分钟

水波纹动画的基本实现

<div class="radar">
    <div class="ripple"></div>
    <div class="ripple"></div>
    <div class="ripple"></div>
</div>

样式

.radar {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  .ripple {
    width: 0px;
    height: 0px;
    border-radius: 50%;
    position: absolute;
    z-index: 1;
    box-shadow: 0px 0px 2px 4px red;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
    animation: ripple 2s linear infinite;
    &:nth-child(1) {
      animation-delay: 0.666s;
    }
    &:nth-child(2) {
      animation-delay: 1.322s;
    }
  }
}
// 动画
@keyframes ripple {
  to {
    width: 60px;
    height: 60px;
    opacity: 0;
  }
}

效果图

image.png

百度地图中的应用,其实官方已经给出了添加方法,可查看文档 lbsyun.baidu.com/index.php?t… 具体实现步骤:

  • 1、定义构造函数
// 定义自定义覆盖物的构造函数
function SquareOverlay (center, color, length?: number) {
  this._center = center
  this._color = color
  this._size = length || 10
}
  • 2、创建自定义覆盖物事件
// 创建自定义覆盖物事件
function addOverDefault () {
  // 继承API的BMap.Overlay
  SquareOverlay.prototype = new mapInfo.BMapGL.Overlay()
  SquareOverlay.prototype.initialize = function (map) {
    // 保存map对象实例
    this._map = map
    const template = `<div class="radar-box">
      <div class="radar">
        <div class="ripple"></div>
        <div class="ripple"></div>
        <div class="ripple"></div>
      </div>
    </div>`
    // 创建文档碎片
    const divFragment = document.createRange().createContextualFragment(template)
    const div: any = divFragment.querySelectorAll('.radar-box')[0]
    div.style.position = 'absolute'
    map.getPanes().markerPane.appendChild(div)
    // 设置自定义元素元素外观
    const cirs: any = div.querySelectorAll('.ripple')
    cirs.forEach(v => {
      v.style.boxShadow = '0px 0px 2px 4px ' + this._color
      v.style.border = '1px solid ' + this._color
    })
    // div.style.width = this._size + 'px'
    // div.style.height = this._size + 'px'
    // div.style.background = this._color
    // 将div添加到覆盖物容器中
    this._div = div
  }
  // 实现绘制方法
  SquareOverlay.prototype.draw = function () {
    // 根据地理坐标转换为像素坐标,并设置给容器
    const position = this._map.pointToOverlayPixel(this._center)
    this._div.style.left = position.x - this._size / 2 + 'px'
    this._div.style.top = position.y - this._size / 2 + 'px'
  }
  // 实现显示方法
  SquareOverlay.prototype.show = function () {
    if (this._div) {
      this._div.style.display = ''
    }
  }
  // 实现隐藏方法
  SquareOverlay.prototype.hide = function () {
    if (this._div) {
      this._div.style.display = 'none'
    }
  }
  // 添加自定义方法
  SquareOverlay.prototype.toggle = function () {
    if (this._div) {
      if (this._div.style.display === '') {
        this.hide()
      } else {
        this.show()
      }
    }
  }
}
  • 3、创建点
// 添加点标记
function addPoint () {
  const datas = [
    {
      name: '巨龙大道',
      lo: 114.266168,
      lat: 30.717014
    },
    {
      name: '楚凡大道',
      lo: 114.736449,
      lat: 30.543493
    },
    {
      name: '东风大道',
      lo: 114.140261,
      lat: 30.479275
    },
    {
      name: '仙桃',
      lo: 113.459561,
      lat: 30.343232
    }
  ]
  const BMapGL = mapInfo.BMapGL
  const map = mapInfo.map
  datas.forEach(v => {
    let mySquare = null
    let pt = null
    switch (v.name) {
      case '仙桃':
        pt = new BMapGL.Point(v.lo, v.lat)
        mySquare = new SquareOverlay(pt, '#D7CE56')
        break
      case '巨龙大道':
        pt = new BMapGL.Point(v.lo, v.lat)
        mySquare = new SquareOverlay(pt, '#fff')
        break
      default:
        pt = new BMapGL.Point(v.lo, v.lat)
        mySquare = new SquareOverlay(pt, '#0cc73b')
        break
    }
    // 添加自定义覆盖物
    map.addOverlay(mySquare)
  })
}

完整代码:

<template>
  <div class="wrap_box">
    <div id="mapDv" class="map_dv"></div>
    <button @click="togglePoint" style="position: absolute;top:0;left:0;z-index:9999">隐藏/显示点</button>
    <button @click='delPoint' style="position: absolute;top:0;left:100px;z-index:9999">删除</button>
  </div>
</template>

<script lang="ts" setup>
import { loadBMap } from '@/map/loadMap'
import { onMounted, reactive } from 'vue'
const mapInfo = reactive({
  map: null,
  BMapGL: null
})

function initMap (BMapGL) {
  const map = new BMapGL.Map('mapDv')
  map.setMapStyleV2({
    styleId: '2fb7ac51e049149d8217dc7334301562'
  })
  map.enableScrollWheelZoom(true) // 启动鼠标滚轮操作
  const point = new BMapGL.Point(114.348204, 30.623025)
  map.centerAndZoom(point, 10)
  mapInfo.map = map
  // 初始化事件
  addOverDefault()
}
// 定义自定义覆盖物的构造函数
function SquareOverlay (center, color, length?: number) {
  this._center = center
  this._color = color
  this._size = length || 60
}
// 创建自定义覆盖物事件
function addOverDefault () {
  // 继承API的BMap.Overlay
  SquareOverlay.prototype = new mapInfo.BMapGL.Overlay()
  SquareOverlay.prototype.initialize = function (map) {
    // 保存map对象实例
    this._map = map
    const template = `<div class="radar-box">
      <div class="radar">
        <div class="ripple"></div>
        <div class="ripple"></div>
        <div class="ripple"></div>
      </div>
    </div>`
    // 创建文档碎片
    const divFragment = document.createRange().createContextualFragment(template)
    const div: any = divFragment.querySelectorAll('.radar-box')[0]
    div.style.position = 'absolute'
    div.style.zIndex = 9
    map.getPanes().markerPane.appendChild(div)
    // 设置自定义元素元素外观
    const cirs: any = div.querySelectorAll('.ripple')
    cirs.forEach(v => {
      v.style.boxShadow = '0px 0px 2px 4px ' + this._color
      v.style.border = '1px solid ' + this._color
    })
    div.style.width = this._size + 'px'
    div.style.height = this._size + 'px'
    // div.style.background = this._color
    // 将div添加到覆盖物容器中
    this._div = div
  }
  // 实现绘制方法
  SquareOverlay.prototype.draw = function () {
    // 根据地理坐标转换为像素坐标,并设置给容器
    const position = this._map.pointToOverlayPixel(this._center)
    this._div.style.left = position.x - this._size / 2 + 'px'
    this._div.style.top = position.y - this._size / 2 + 'px'
  }
  // 实现显示方法
  SquareOverlay.prototype.show = function () {
    if (this._div) {
      this._div.style.display = ''
    }
  }
  // 实现隐藏方法
  SquareOverlay.prototype.hide = function () {
    if (this._div) {
      this._div.style.display = 'none'
    }
  }
  // 添加自定义方法
  SquareOverlay.prototype.toggle = function () {
    if (this._div) {
      if (this._div.style.display === '') {
        this.hide()
      } else {
        this.show()
      }
    }
  }
}
// 添加点标记
function addPoint () {
  const datas = [
    {
      name: '巨龙大道',
      lng: 114.266168,
      lat: 30.717014,
      address: '地址:北京市东城区王府井大街88号乐天银泰百货八层'
    },
    {
      name: '楚凡大道',
      lng: 114.736449,
      lat: 30.543493,
      address: '地址:北京市东城区王府井大街88号乐天银泰百货八层'
    },
    {
      name: '东风大道',
      lng: 114.140261,
      lat: 30.479275,
      address: '地址:北京市东城区王府井大街88号乐天银泰百货八层'
    },
    {
      name: '仙桃',
      lng: 113.459561,
      lat: 30.343232,
      address: '<div>地址:北京市东城区王府井大街88号乐天银泰百货八层</div>'
    }
  ]
  const BMapGL = mapInfo.BMapGL
  const map = mapInfo.map
  datas.forEach(v => {
    let mySquare = null
    let pt = null
    switch (v.name) {
      case '仙桃':
        pt = new BMapGL.Point(v.lng, v.lat)
        mySquare = new SquareOverlay(pt, '#D7CE56')
        break
      case '巨龙大道':
        pt = new BMapGL.Point(v.lng, v.lat)
        mySquare = new SquareOverlay(pt, '#fff')
        break
      default:
        pt = new BMapGL.Point(v.lng, v.lat)
        mySquare = new SquareOverlay(pt, '#0cc73b')
        break
    }
    // 添加自定义覆盖物
    map.addOverlay(mySquare)
    // 添加浮窗事件
    const opts = {
      width: 200, // 信息窗口宽度
      height: 100, // 信息窗口高度
      title: v.name // 信息窗口标题
    }
    const infoWindow = new BMapGL.InfoWindow(v.address, opts)
    mySquare._div.addEventListener('click', function (e) {
      e.preventDefault()
      e.stopPropagation()
      map.openInfoWindow(infoWindow, pt)
    })
  })
}
// 隐藏/显示点
function togglePoint () {
  const map = mapInfo.map
  const alls = map.getOverlays()
  const points = alls.filter(v => {
    return (v._div && v._div.className === 'radar-box')
  })
  // console.log(points)
  points.forEach(v => {
    v.toggle()
  })
}
// 删除自定义覆盖物
function delPoint () {
  const doms = document.querySelectorAll('.radar-box')
  doms.forEach(vm => {
    vm.parentNode.removeChild(vm)
  })
}

onMounted(() => {
  loadBMap().then(BMapGL => {
    mapInfo.BMapGL = BMapGL
    initMap(BMapGL)
    addPoint()
  })
})
</script>

<style lang="scss" scoped>
.wrap_box {
  width: 100vw;
  height: 100vh;
  .map_dv {
    width: 100%;
    height: 100%;
    position: relative;
    // 水波动画样式
    ::v-deep(.radar) {
      width: 100%;
      height: 100%;
      border-radius: 50%;
      position: absolute;
      top: 0;
      left: 0;
      z-index: 2;
      .ripple {
        width: 0px;
        height: 0px;
        border-radius: 50%;
        position: absolute;
        z-index: 1;
        box-shadow: 0px 0px 2px 4px red;
        top: 50%;
        left: 50%;
        transform: translate(-50%,-50%);
        animation: ripple 2s linear infinite;
        &:nth-child(1) {
          animation-delay: 0.666s;
        }
        &:nth-child(2) {
          animation-delay: 1.322s;
        }
      }
    }
  }
}
// 动画
@keyframes ripple {
  to {
    width: 60px;
    height: 60px;
    opacity: 0;
  }
}
</style>

效果图

Video_2022-09-18_142129.gif