唯杰地图 logo唯杰地图
VJMAP
EN

anchor-offset

vjmap/references/marker/anchor-offset.md 4.4 KB131 行
原文

本文回答:锚点 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);

十、常见坑

  1. 认为 offset 是地理单位 → offset 是像素。
  2. anchor 忘写 → 默认 center;图片图标看起来"浮在空中"。
  3. CSS transform 混乱 → Marker 内部用 translate() 定位;不要在外层 div 再加 transform 覆盖,否则位置错乱。
  4. 不同 DPR 下看起来偏 → 检查 devicePixelRatio;offset 是 CSS 像素,不受 DPR 影响。
  5. 拖拽后位置不对 → dragend 事件里检查 marker.getLngLat(),可能拖的是 div 中心,而 anchor 是 bottom,转换要一致。