百度地图的自定义覆盖物 CustomOverlay,官方好像没有提供拖拽移动的功能。需要自己实现,AI 给的办法也是一言难尽,连 API 都编的假里假气的。这里记录一下我的实现思路,避免以后忘记:
- 给覆盖物的 dom 元素添加
mousedown
事件,触发后开启拖拽功能,并记录鼠标起始位置- 还要关闭地图的缩放功能(如果有的话),避免缩放过程中鼠标和覆盖物发生错位,这个过程,不会触发
mousemove
- 还要关闭地图的缩放功能(如果有的话),避免缩放过程中鼠标和覆盖物发生错位,这个过程,不会触发
- 给页面添加
mousemove
事件,触发后记录鼠标实时位置,执行拖拽移动操作- 还要判断鼠标移出地图容器的时候,关闭拖拽功能
- 在页面上绑定事件,而不是在覆盖物的 dom 元素上,是因为事件回调队列的执行顺序,万一更新dom元素定位的逻辑没有执行完,鼠标就已经移动到 dom 元素外,那
mousemove
事件就中断了。绑定到页面上,能保证事件正常触发。
- 给页面添加
mouseleave
和mouseup
事件,触发后关闭拖拽功能- 重新开启地图的缩放功能(如果有的话)
- 给页面添加
mouseleave
事件,是为了监听鼠标离开浏览器页面的情况 - 给页面添加
mouseup
事件,而不是绑定到覆盖物的 dom 元素上,原因同mousemove
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script type="text/javascript" src="https://api.map.baidu.com/api?v=1.0&&type=webgl&ak=您的密钥"></script>
<style>
h1, p { text-align: center; }
#container {
width: 80vw;
height: calc(80vh - 40px);
border: 4px solid pink;
margin: 0 auto;
}
</style>
</head>
<body>
<h1>百度地图自定义覆盖物拖拽移动功能</h1>
<p><button id="add-btn">在地图中心添加一个覆盖物</button></p>
<div id="container"></div>
<script>
let map
let mouseClientX // 实时鼠标位置
let mouseClientY // 实时鼠标位置
const overlayMap = new Map() // 地图上添加的覆盖物集合
let currentOverlay // 当前操作的覆盖物,有值表示启用拖拽移动功能
// 初始化地图
function init() {
// 初始化地图
map = new BMapGL.Map('container')
// 允许地图可被鼠标滚轮缩放
map.enableScrollWheelZoom()
// 启用地图双击缩放,左键双击放大、右键双击缩小
map.enableDoubleClickZoom()
const point = new BMapGL.Point(116.404, 39.915)
map.centerAndZoom(point, 15)
}
// 初始化
init()
// 随机生成一个HEX颜色
function getRandomColor() {
const letters = '0123456789ABCDEF'
let color = '#'
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)]
}
return color
}
// 在地图中心添加一个覆盖物
function handleAddCustomOverlay() {
const id = Date.now() // 唯一标识
const customOverlay = new BMapGL.CustomOverlay(domCreate, {
point: map.getCenter(),
offsetX: -25,
offsetY: -25,
anchors: [0, 0],
// 自定义属性
properties: {
id, // 唯一标识
color: getRandomColor()
}
})
map.addOverlay(customOverlay)
overlayMap.set(id, customOverlay)
}
// 创建覆盖物自定义dom
function domCreate() {
const { id, color } = this.properties
const overlayDiv = document.createElement('div')
overlayDiv.className = 'my-overlay'
overlayDiv.style.width = '50px'
overlayDiv.style.height = '50px'
overlayDiv.style.borderRadius = '50px'
overlayDiv.style.background = color
overlayDiv.addEventListener('mousedown', () => {
currentOverlay = overlayMap.get(id)
if (!currentOverlay) return
// currentOverlay 有值表示启用拖拽功能
// 记录目标dom对象,
currentOverlay.dragTarget = overlayDiv.parentElement
// 记录鼠标起始位置
currentOverlay.startX = mouseClientX
currentOverlay.startY = mouseClientY
if (map) {
// 禁止地图可被鼠标滚轮缩放
map.disableScrollWheelZoom()
// 禁用地图双击缩放,左键双击放大、右键双击缩小
map.disableDoubleClickZoom()
}
})
overlayDiv.addEventListener('mouseup', () => {
if (!currentOverlay) return
// 关闭拖拽功能
handleCloseDrag()
})
return overlayDiv
}
// 关闭拖拽功能
function handleCloseDrag() {
if (map) {
// 允许地图可被鼠标滚轮缩放
map.enableScrollWheelZoom()
// 启用地图双击缩放,左键双击放大、右键双击缩小
map.enableDoubleClickZoom()
}
// 重置 currentOverlay 表示关闭拖拽移动功能
currentOverlay = null
}
// 鼠标移动回调
function handleBodyMouseMove(e) {
// 记录鼠标位置
mouseClientX = e.clientX
mouseClientY = e.clientY
if (!currentOverlay) return
// 判断鼠标是否已经超出地图容器区域
let target = e.target
let isIn = false
while (target && !isIn) {
// 也可以匹配自定义的选择器,当下是 #container
if (target.className.includes('bmap-container')) {
isIn = true
} else {
target = target.parentElement
}
}
if (!isIn) {
// 超出地图容器,关闭功能
handleCloseDrag()
return
}
// 获取鼠标移动的偏移量
const dx = mouseClientX - currentOverlay.startX
const dy = mouseClientY - currentOverlay.startY
// 更新dom元素的新位置
const newLeft = currentOverlay.dragTarget.offsetLeft + dx
const newTop = currentOverlay.dragTarget.offsetTop + dy
currentOverlay.dragTarget.style.left = newLeft + 'px'
currentOverlay.dragTarget.style.top = newTop + 'px'
// 更新鼠标起始位置
currentOverlay.startX = mouseClientX
currentOverlay.startY = mouseClientY
// 获取覆盖物的当前坐标
const oldPoint = currentOverlay.getPoint()
// 转成页面px位置
const { x, y } = map.pointToPixel(oldPoint)
// 计算新的位置
const newPixel = new BMapGL.Pixel(x + dx, y + dy)
// 换算成地图坐标点
const newPoint = map.pixelToPoint(newPixel)
// 更新覆盖物的位置
// 覆盖物的 getPoint 方法获取的就是覆盖物对象下的 point 属性,所以直接替换它即可
// 效果同 currentOverlay.setPoint(newPoint)
currentOverlay.point = newPoint
}
// 添加事件监听
document.querySelector('#add-btn').addEventListener('click', handleAddCustomOverlay)
document.body.addEventListener('mousemove', handleBodyMouseMove)
document.body.addEventListener('mouseleave', handleCloseDrag)
document.body.addEventListener('mouseup', handleCloseDrag)
</script>
</body>
</html>