跳到主要内容

地图与平面图

地图 / 平面图的文档是 图层 + 要素 id,几何用世界单位(米、楼层像素、CAD 单位)。相机仍是 viewBox。一次挂载「整国 path」不是矢量的优势,是自杀。

1. 场景怎么切

世界:投影后的平面坐标(不要在指针处理器里再跑一次 lon/lat)
相机:viewBox 覆盖当前视口;缩放改 scale,锚点在光标下
栅格瓦片:<image> 或 canvas 对齐世界 AABB(可以光栅)
矢量要素:可见 zoom 范围内的 path / 热点(必须是节点)
选中/编辑:单独 g.selection
DOM:楼层切换、搜索列表、气泡(读屏)

必须是节点:可点建筑、车位、热区、正在编辑的边界。无障碍名挂 title / aria-label,不要只靠颜色。

可以光栅:底图瓦片、卫星图、等高线密度层、未选中的海量道路。道路在远 zoom 用瓦片,近 zoom 再换成可点 path,或始终瓦片 + 热区矩形。

export interface GeoFeature {
id: string;
kind: "building" | "hotspot" | "room";
/** 世界坐标多边形 */
points: readonly { x: number; y: number }[];
label: string;
}

export interface MapDoc {
world: { minX: number; minY: number; maxX: number; maxY: number };
features: GeoFeature[];
}

export function viewBoxForCamera(
cam: { x: number; y: number; scale: number },
cssW: number,
cssH: number,
): { minX: number; minY: number; w: number; h: number } {
return { minX: cam.x, minY: cam.y, w: cssW * cam.scale, h: cssH * cam.scale };
}

裁剪:用视口矩形(再 pad 一圈)查空间索引,只 append 命中的要素。国家边界这种超长 path 要按 tile 切,或远距用简化线。节点预算见 虚拟化。相机操作见 锚点缩放

底图瓦片是 &lt;image>x/y/width/height 用世界单位对齐 AABB。瓦片是光栅,不要给每张瓦片挂 filter。命中应穿透瓦片:pointer-events: none&lt;image> 上,事件落到要素层。

2. 平面图特有约束

室内图常常底图是一张大 PNG/SVG 平面。底图世界 = 图像自然尺寸或 CAD 单位。热点矩形存在图像坐标,和 热区 同一模型。底图可 &lt;image href>;若要导出独立 SVG,外链会丢,见 序列化

不要把底图矢量化成十万条墙线还每条可点——墙是光栅,门是节点。楼层切换是换文档或换图层,不是 CSS display 藏着整栋仍在 DOM 里。

气泡位置用 overlay,锚点仍是世界坐标经 CTM,见 Overlay

适用:室内平面、园区、已投影的 2D 地图。三维地球、倾斜摄影走 WebGL,SVG 只叠可点注记。

3. 不适用

  • 把 WebGL 地球和 SVG 楼层当同一相机硬叠,经纬度与平面米混在一个 viewBox。
  • 用 CSS scale 做地图缩放:瓦片 DPI 和命中一起坏。
  • 选中态靠换一张整图:要素必须能单独改样式。

4. 失败形态

症状原因
一打开卡死整国 / 整栋 path 全挂 DOM
点建筑点到隔壁命中用了屏幕坐标,或瓦片层挡住了透明 SVG
导出没底图&lt;image> 外链在文件模式下未打包
放大底图糊、矢量锐预期如此;要锐底图就换更高 zoom 瓦片,不要 scale 位图
旋转平面图后热点漂热点存在 CSS 盒子里,没和底图共用世界变换

权威资料

核对日期:2026-08-26