水波纹动画的基本实现
<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;
}
}
效果图
百度地图中的应用,其实官方已经给出了添加方法,可查看文档 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>
效果图