访客在 1000usdinchina.com 第一眼看到的是一张地图 —— 一张中国的 交互式 SVG 地图,每个覆盖城市一个可点的点。没有 Mapbox、没有 Leaflet、没有瓦片服务器。 就是手写 SVG。这篇讲为什么、怎么做,以及那个城市点变黑团的 bug。
这是系列第 4 篇。
目录
为什么用原生 SVG 不用地图库
地图库很棒,但对这活完全不合适:
- 体积。 Leaflet/Mapbox + 瓦片是几百 KB 到几 MB。这里的地图是一段内联 SVG,gzip 后几 KB, 零运行时依赖。
- 边缘友好。 没有瓦片请求、没有 API key、没有要经 Worker 代理的东西。它随 HTML 一起 ship, 从缓存秒渲染。
- 可控。 我不需要街道和卫星图 —— 我要一个风格化轮廓 + 约 100 个精确放置的点。SVG 给我对这件事 的像素级控制,且仅此而已。

城市点与交互
每座城是一个按投影坐标定位的 <circle>,接上悬停和点击:
<svg viewBox="0 0 1000 800" class="china-map">
<path class="china-outline" d="M..." />
<!-- 每个覆盖城市一个点 -->
<a href="/en/city/chengdu">
<circle class="city-dot" cx="486" cy="430" r="4"
data-city="chengdu" aria-label="Chengdu" />
</a>
<!-- ... -->
</svg>
.city-dot { fill: var(--accent); transition: r .15s, fill .15s; cursor: pointer; }
.city-dot:hover { r: 7; fill: var(--accent-strong); }
悬停放大点,点击跳到城市。因为每个点是个锚点,它无需 JavaScript 也能用,且键盘可达 —— 无障碍白送。
黑团 bug
这个坑花了一下午。地图放大时,成簇的城市变成实心黑团。
flowchart TD
A[SVG 里被旧 generator 烤进了 67 个多余 city-dot] --> B[这些点没有 CSS class]
B --> C[无样式 circle 默认 fill: black]
C --> D[放大时密集无样式点融成黑团]
D --> E[修法:给烤进去的点 display:none]
E --> F[别从 generator 重生 —— 它已漂移]根因:早期版本的地图 generator 把 67 个无样式 city-dot 元素直接烤进了 SVG。一个没指定
fill 的 SVG <circle> 默认实心黑。正常缩放下它们小、没人注意;一放大,密集黑点融成一团。
修法刻意保守:给烤进去的点 display:none,而不是重生整张 SVG。为什么不直接重跑 generator?
因为 generator 已经和实际在 prod 的手调地图漂移了 —— 重生会「修好」黑团,却悄悄把几十处
手动位置校正回退。当 generator 已和手改产物漂移,别盲目重生。 治标,保住手工成果。
SVG vs canvas vs 瓦片
对一个风格化轮廓上的约 100 个静态点,SVG 赢:
| 内联 SVG | Canvas | 瓦片地图(Leaflet/Mapbox) | |
|---|---|---|---|
| 载荷 | 几 KB | 小 + JS | 几百 KB–MB |
| 依赖 | 无 | 库 | 库 + 瓦片 + key |
| 无障碍 | 原生(锚点、ARIA) | 手动 | 部分 |
| 边缘友好 | 完美(内联) | 良 | 需瓦片请求 |
| 适合... | 几百个静态要素 | 几千个动态点 | 真实地理 / 街道 |
Canvas 在几千个动画点时才划算;瓦片地图在你需要真街道时。对一张边缘上的风格化 100 点地图, 内联 SVG 是最小、最快、最无障碍的选择。
要点
- 对约 100 个静态要素,手写内联 SVG 在体积、速度、无障碍上都胜过地图库。
- 锚点包裹的点,无需 JavaScript 就给你导航和键盘支持。
- 放大变实心黑团是无样式 SVG circle 的典型症状(默认 fill: black)。
- 若 generator 已和手改产物漂移,治标 —— 别重生丢掉手工活。
常见问题
不用地图库能做交互地图吗? 能。对一张点位固定的风格化地图,内联 SVG + 锚点包裹的 circle 就给你交互、导航和无障碍,零依赖。
我的 SVG circle 为什么变黑?
没有 fill 的 SVG <circle> 默认实心黑。加个 CSS class 或 fill 属性;密集的无样式 circle
放大时会融成一团。
什么时候该改用 canvas 或瓦片地图? 几千个动态/动画点用 canvas;需要真街道、卫星图或真实地理投影时用瓦片地图(Leaflet/Mapbox)。
下一篇 → 我如何用 Claude Code 单人 ship 这个应用:prompt、context、harness、loop engineering