anchor-offset
本文回答:锚点 anchor 与偏移 offset 的数学关系 —— 缩放时保持位置不动的正确设法。
一、直觉
Marker 的 lngLat 是一个点,但图标是一个矩形。anchor + offset 决定"矩形的哪一点对齐在这个点上"。
default: anchor=center, offset=[0,0]
→ 矩形中心在 lngLat
二、九宫锚点图
top-left top top-right
┌───────────┬────────────┬───────────┐
│ │ │ │
│ left │ center │ right │
│ │ │ │
├───────────┼────────────┼───────────┤
│ │ │ │
│bottom-left│ bottom │bottom-right│
└───────────┴────────────┴───────────┘
三、偏移 offset 含义
offset 是"对齐点再偏移的像素量",[x, y]:x 正为右,y 正为下,负数反之。
四、经典问题:水滴尖尖指向目标点
默认图标高 41px,尖尖在底端。
- 方式 A:
anchor: "bottom"→ 矩形底端对齐在 lngLat。 - 方式 B:
anchor: "center", offset: [0, -20]→ 中心对齐后再上移 20 像素。
等效,任选其一。
五、典型实战
5.1 图片 Marker(32×40,尖尖在底)
new vjmap.Marker({
element: el,
anchor: "bottom"
}).setLngLat([lng, lat]).addTo(map);
5.2 POI 标签(下面是小圆点)
<div class="poi">
<div class="label">名称</div>
<div class="dot"></div>
</div>
希望"小圆点"精确对齐 lngLat:
// 假设小圆点在整个 div 的最底端中间
new vjmap.Marker({ element: el, anchor: "bottom" }).setLngLat([lng, lat]).addTo(map);
5.3 气泡提示(尖尖在左下)
new vjmap.Marker({ element: el, anchor: "bottom-left" })
.setLngLat([lng, lat])
.addTo(map);
六、anchor + offset 复合用
比如 50×30 的卡片,希望"卡片中心"对齐目标点下方 20px(让目标点上方显示卡片):
new vjmap.Marker({
element: el, // 50×30
anchor: "center",
offset: [0, -35] // 目标点上方 35px(= 卡片高度 / 2 + 20)
}).setLngLat([lng, lat]).addTo(map);
七、缩放时位置不动的原理
Marker 永远定位在 lngLat 这个地理点上。缩放地图时:
- 地理坐标 lngLat 不变;
- 屏幕像素变化,但 Marker 的 anchor 点始终对齐 lngLat → 在几何意义上不动。
- 视觉上 div 大小不变,但相对地图缩放。
7.1 "为什么我的 Marker 看起来在动?"
- 初始 anchor 错:
anchor: "center"+ 高 60 的 div,低 zoom 时图标中心在屏幕某位置,拉高 zoom 后由于地图放大,但 div 大小不变,视觉上"锚点飘了"。 - 修复:用
anchor: "bottom",尖尖总是落在目标点。
八、自动缩放 scaleMaxZoom
让 div 随地图一起缩小:
vjmap.createMarker({ scaleMaxZoom: 16, anchor: "center" }) // scaleMaxZoom 是扩展字段,须走 createMarker
.setLngLat([lng, lat]).addTo(map);
当 zoom > 16 时正常显示;zoom < 16 时 div 按比例缩小,防止挤堆。
九、pitchAlignment / rotationAlignment
倾斜 / 旋转地图时:
// Marker 始终贴地(随地图倾斜变扁)
new vjmap.Marker({ pitchAlignment: "map", rotationAlignment: "map" })
.setLngLat(...).addTo(map);
// Marker 始终正对屏幕(默认)
new vjmap.Marker({ pitchAlignment: "viewport", rotationAlignment: "viewport" })
.setLngLat(...).addTo(map);
十、常见坑
- 认为 offset 是地理单位 → offset 是像素。
- anchor 忘写 → 默认 center;图片图标看起来"浮在空中"。
- CSS transform 混乱 → Marker 内部用
translate()定位;不要在外层 div 再加transform覆盖,否则位置错乱。 - 不同 DPR 下看起来偏 → 检查 devicePixelRatio;offset 是 CSS 像素,不受 DPR 影响。
- 拖拽后位置不对 → dragend 事件里检查
marker.getLngLat(),可能拖的是 div 中心,而 anchor 是 bottom,转换要一致。