> **本文回答**：创建 Marker —— new vjmap.Marker / setLngLat / addTo / remove / show / hide。

## 一、最小用法

```js
const marker = new vjmap.Marker()
  .setLngLat([113.0, 23.0]);
marker.addTo(map);
```

> ⚠️ **`Marker.addTo()` 返回 `undefined`**（d.ts 声明 `this`，运行时无返回值——源码 `marker.ts` 重写时未 return）。不要写 `const m = new Marker().setLngLat(x).addTo(map)`——`m` 会是 `undefined`，后续 `m.remove()` 报 TypeError。`setLngLat`/`setPopup` 等链返回 `this` 正常，仅 `addTo` 断链。

`setLngLat` 接受：

- `[lng, lat]` 数组
- `LngLat` 实例：`new vjmap.LngLat(lng, lat)`
- `{ lng, lat }` 对象

## 二、从 CAD 坐标创建

CAD 坐标必须先转经纬度：

```js
const cadPt = [725.32, 18969.19];
const lngLat = map.toLngLat(cadPt);
new vjmap.Marker().setLngLat(lngLat).addTo(map);
```

## 三、两种构造形式

```js
// 形式 1：只传 options
new vjmap.Marker({ color: "#f00", draggable: true });

// 形式 2：传 DOM + options
const el = document.createElement("div");
el.className = "my-marker";
new vjmap.Marker(el, { anchor: "bottom" });

// 形式 3（推荐）：options.element
new vjmap.Marker({ element: el, anchor: "bottom" });
```

## 四、常用方法

| 方法 | 功能 |
|---|---|
| `addTo(map)` | 附加到地图 |
| `remove()` | 从地图移除 |
| `setLngLat(ll)` | 设置 / 移动位置（会触发重绘） |
| `getLngLat()` | 当前位置 |
| `getElement()` | 获取底层 DOM（可 addEventListener / 改样式） |
| `setPopup(popup)` | 绑定 Popup |
| `togglePopup()` | 切换 Popup |
| `setOffset([x, y])` | 运行时改偏移 |
| `setRotation(deg)` | 旋转 |
| `show() / hide()` | 显隐（不从 map 里移除） |
| `isHidden()` | 是否隐藏 |
| `setColor(c)` | 改默认水滴颜色 |
| `setDraggable(b)` | 启用拖拽 |
| `setAnimation(t)` | 弹跳 / 坠落动画（见 [animation](animation.md)） |

## 五、批量创建

```js
const markers = [];
for (let i = 0; i < 100; i++) {
  const m = new vjmap.Marker({ color: "#0af" })
    .setLngLat(map.toLngLat(bounds.randomPoint()));
  m.addTo(map);          // addTo 返回 undefined，勿把链尾赋给 m
  markers.push(m);
}
// 批量移除
markers.forEach(m => m.remove());
markers.length = 0;
```

**> 500 个 Marker 建议改用 Symbol 图层**，见 [performance-tips](performance-tips.md)。

## 六、更新位置（动画式）

```js
function moveTo(marker, target, duration = 1000) {
  const start = marker.getLngLat();
  const startT = performance.now();
  function step() {
    const t = Math.min(1, (performance.now() - startT) / duration);
    marker.setLngLat([
      start.lng + (target.lng - start.lng) * t,
      start.lat + (target.lat - start.lat) * t
    ]);
    if (t < 1) requestAnimationFrame(step);
  }
  requestAnimationFrame(step);
}
```

## 七、show / hide vs remove

| API | 行为 |
|---|---|
| `hide()` | DOM 仍在文档，`display: none` |
| `setVisible(v, true)` | `isDisplay=true` 分支＝CSS `display` 显隐切换（等价 `show()`/`hide()`） |
| `setVisible(false, false)` | `isDisplay` 假分支调 `this.remove()`，等价 `remove()`：真正移除 |
| `remove()` | DOM 摘除、事件解绑、`_map` 删除（并置 `isRemoved=true`） |

⚠️ `remove()` **不是不可逆**：`marker.addTo(map)` 会重置 `isRemoved`、重建全部监听并重新挂上。

⚠️ `setVisible(false,false)` 之后**不能**用 `setVisible(true,false)` 恢复——它调 `addTo(this._map)`，而 `_map` 已被删（`addTo(undefined)` 报错）。恢复要用 `marker.addTo(map)`。

⚠️ 更深坑：**一旦误调 `setVisible(true,false)` 并捕获了异常，之后再 `addTo(map)` 会静默无效**——`addTo` 包装在调原生方法之前就把 `isShow` 置 `true`，异常中断后 `isShow` 停在 `true`，下次 `addTo` 被 `if (this.isShow) return` 拦截。补救：`marker.isShow = false` 再 `addTo(map)`。

> "DOM 摘除但保留地图登记"是内部 `remove(true)` / `addTo(map, true)` 布尔参路径（`removeWhenNoInMapView` 自动裁剪专用），公开 API 不暴露该开关。

## 八、常见坑

1. **CAD 坐标直接 setLngLat** → marker 出现在赤道非洲；必须 `map.toLngLat`。
2. **没有 addTo** → 新建的 Marker 不显示。
3. **元素没有尺寸** → 自定义 DOM 没 width/height 时，看起来像没创建。
4. **重复创建未 remove** → 同一个逻辑点反复 `new Marker().addTo(map)` 会重影。保留引用，需要时移除。
5. **`addTo` 链尾赋值** → `const m = new Marker().setLngLat(x).addTo(map)` 得到的 `m` 是 `undefined`（addTo 无返回值，d.ts 声明 `this` 与运行时不符）；先 `setLngLat` 赋值再单独 `addTo`。`Popup.addTo` 未被重写、正常返回 `this`，不受影响。
