> **本文回答**：setAnimation 弹跳/坠落、CSS 呼吸/光圈/扩散/荧光/旋转 —— 让 Marker 活起来。

## 一、setAnimation（只对默认水滴有效）

```js
const m = new vjmap.Marker({ color: "#f00" }).setLngLat([lng, lat]);
m.addTo(map);   // 注意：addTo 运行时返回 undefined，不能把链尾赋给 m
m.setAnimation("MAP_ANIMATION_BOUNCE");   // 弹跳
m.setAnimation("MAP_ANIMATION_DROP");     // 坠落
m.setAnimation("MAP_ANIMATION_NONE");     // 关闭
```

值：

- `MAP_ANIMATION_NONE`：无动画
- `MAP_ANIMATION_BOUNCE`：弹跳（原地上下）
- `MAP_ANIMATION_DROP`：坠落（从上方掉落一次）

## 二、自定义 element 如何让 setAnimation 生效

`setAnimation` 通过改 `marker.getElement().children[0]` 的 CSS 来动画；自定义 element 必须**外层再套一层 div**：

```js
const inner = document.createElement("div");
inner.className = "my-marker";
// inner 自己的样式...

const wrapper = document.createElement("div");
wrapper.appendChild(inner);                  // 关键：包一层

const m = new vjmap.Marker({ element: wrapper, anchor: "bottom" })
  .setLngLat([lng, lat]);
m.addTo(map);

m.setAnimation("MAP_ANIMATION_BOUNCE");
```

## 三、CSS 呼吸光圈

```css
.breath {
  width: 20px; height: 20px; border-radius: 50%; background: #f00;
  animation: breath 1.5s ease-in-out infinite;
}
@keyframes breath {
  0%, 100% { transform: scale(1); opacity: 1; }
  50%      { transform: scale(1.4); opacity: 0.5; }
}
```

```js
const el = document.createElement("div");
el.className = "breath";
new vjmap.Marker({ element: el }).setLngLat([lng, lat]).addTo(map);
```

## 四、CSS 涟漪扩散

```css
.ripple {
  position: relative; width: 20px; height: 20px;
}
.ripple::before, .ripple::after {
  content: ""; position: absolute; inset: 0;
  border: 2px solid #0af; border-radius: 50%;
  animation: ripple 1.5s ease-out infinite;
}
.ripple::after { animation-delay: 0.75s; }
@keyframes ripple {
  0%   { transform: scale(0); opacity: 1; }
  100% { transform: scale(4); opacity: 0; }
}
```

## 五、旋转

```css
.spin {
  width: 30px; height: 30px;
  background: url("/icons/compass.png") center/cover;
  animation: spin 4s linear infinite;
}
@keyframes spin {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
```

或用 JS：

```js
let deg = 0;
function frame() {
  deg = (deg + 2) % 360;
  marker.setRotation(deg);
  requestAnimationFrame(frame);
}
frame();
```

## 六、vjmap 内置特效类

详见 [builtin-icons](builtin-icons.md)。简要：

| 类 | 效果 |
|---|---|
| `vjmap.BreathingApertureMarker` | 呼吸光圈 |
| `vjmap.HaloRingMarker` | 光环 |
| `vjmap.DiffusedApertureMarker` | 扩散光圈 |
| `vjmap.FluorescenceMarker` | 荧光 |
| `vjmap.RotatingApertureMarker` | 旋转光圈 |
| `vjmap.RotatingTextBorderMarker` | 旋转文字边框 |
| `vjmap.AnimateMarkerLayer` | 批量动画 marker 图层 |

使用（构造签名是 `(features | {lngLat, text?}, options?: AnimateMarkerLayerOption)`）：

```js
const eff = new vjmap.BreathingApertureMarker(
  { lngLat: map.toLngLat([x, y]) },
  { /* AnimateMarkerLayerOption */ }
);
eff.setMarkersWidth(30);
eff.setMarkersColors(["#0af", "#fff"]);
eff.setDotSize?.(8);

const m = eff.createMarker();   // → vjmap.Marker
m.addTo(map);
```

## 七、轨迹动画（Marker 沿路径移动）

```js
const path = [[lng1, lat1], [lng2, lat2], [lng3, lat3]];
const marker = new vjmap.Marker().setLngLat(path[0]);
marker.addTo(map);

let i = 0, t = 0, duration = 1000;
const startTime = performance.now();

function anim() {
  const elapsed = performance.now() - startTime;
  const segT = (elapsed % duration) / duration;
  const segI = Math.floor(elapsed / duration) % (path.length - 1);
  const a = path[segI], b = path[segI + 1];
  marker.setLngLat([
    a[0] + (b[0] - a[0]) * segT,
    a[1] + (b[1] - a[1]) * segT
  ]);
  requestAnimationFrame(anim);
}
anim();
```

## 八、GPU 加速提示

移动 Marker 时：

```css
.my-marker {
  will-change: transform;
}
```

## 九、性能建议

- 动画 Marker 不要超过 50 个（DOM 动画很贵）。
- 大量动态图标（热力/轨迹）请改用 Symbol Layer + 数据驱动样式。
- `requestAnimationFrame` 回调中不要做复杂计算；只 setLngLat。

## 十、常见坑

1. **`setAnimation` 对自定义 element 无效** → 外层没包一层 div。
2. **CSS 动画卡顿** → 使用 `transform` / `opacity`；避免动画 `width`、`top`。
3. **特效 Marker 叠太多** → 每个都是一个 div + CSS 动画，几十个就卡。
4. **`animation` 和 `will-change` 滥用**：`will-change` 只加在动画元素上，不要全局加。
