# 准备页地图预览方案
> 文档版本：v1.0
> 最后更新：2026-04-07 12:18:00

本文档用于说明活动准备页的地图预览能力如何设计与落地。

当前目标不是直接进入实现，而是先把方案边界、分层、前后端职责和分阶段路径定清。

---

## 1. 目标

准备页当前已经具备：

- 活动状态摘要
- 赛道选择
- 设备准备
- 进入地图

但“本局对象预览”仍然主要是文字占位，对玩家帮助有限。

准备页地图预览的目标是：

1. 在进入地图前，让玩家建立空间预期
2. 在多赛道活动中，让玩家能直观看到当前赛道差异
3. 让准备页更像“出发前准备”，而不是工程参数页
4. 不打断当前 runtime 主链，也不额外制造一套重资源地图体系

---

## 2. 推荐方案

当前推荐采用：

**低级别正式瓦片做底图，前端动态叠加赛道**

也就是：

- 底图来源：现有正式瓦片资源
- 底图级别：选择一个较低 zoom 作为预览缩略底图
- 叠加层：前端根据当前赛道数据在预览图上动态绘制起点、终点、控制点和腿线

这是一个“混合预览方案”：

- 不单独制作新的预览地图资源
- 不在前端完整拼装高精度交互地图
- 只在准备页做只读预览

---

## 3. 为什么选这个方案

### 3.1 不新增独立地图资源

如果单独为准备页制作预览地图资源，会带来：

- 资源重复
- 发布链复杂度上升
- 底图与正式地图不一致风险

使用低级别正式瓦片作为预览底图，可以保持：

- 同源
- 一致
- 低成本

### 3.2 多赛道切换更自然

多赛道场景下：

- 底图通常是同一张地图
- 差异主要在赛道叠加层

因此最合理的方式是：

- 底图固定
- 切换赛道时仅重绘 overlay

这样：

- 切换更快
- 数据结构更清楚
- 不需要为每个 variant 重新生成整张预览图

### 3.3 前后端职责清楚

后端负责：

- 提供底图预览所需元数据
- 提供赛道叠加所需坐标数据

前端负责：

- 展示底图
- 动态叠加赛道
- 在准备页进行只读预览

这符合当前项目一直坚持的分层原则。

---

## 4. 整体分层

准备页地图预览建议分成 4 层。

### 4.1 地图底图层

来源：

- 当前正式瓦片资源

形式：

- 低级别缩略底图

职责：

- 提供地点区域的空间背景

### 4.2 预览元数据层

来源：

- 后端预览元数据

职责：

- 告诉前端如何把经纬度投到预览图坐标系中

### 4.3 赛道叠加层

来源：

- variant 控制点与腿线数据

职责：

- 在准备页上画出当前赛道

### 4.4 准备页展示层

职责：

- 只读展示
- 切换赛道联动预览
- 不承担局内交互

---

## 5. 后端需要提供的最小字段

V1 不要求后端提供完整预览图 URL，而是先提供“底图元数据 + 赛道 overlay 元数据”。

建议后端提供以下最小结构：

```json
{
  "preview": {
    "mode": "full",
    "baseTiles": {
      "tileBaseUrl": "https://.../tiles/",
      "zoom": 15,
      "tileSize": 256
    },
    "viewport": {
      "width": 800,
      "height": 450,
      "minLon": 117.0000,
      "minLat": 36.6000,
      "maxLon": 117.0800,
      "maxLat": 36.6600
    },
    "variants": [
      {
        "variantId": "variant_a",
        "name": "A线",
        "routeCode": "route-variant-a",
        "controls": [
          { "id": "start", "kind": "start", "lon": 117.01, "lat": 36.61 },
          { "id": "c1", "kind": "control", "lon": 117.02, "lat": 36.615 },
          { "id": "finish", "kind": "finish", "lon": 117.03, "lat": 36.62 }
        ],
        "legs": [
          { "from": "start", "to": "c1" },
          { "from": "c1", "to": "finish" }
        ]
      }
    ]
  }
}
```

其中关键字段是：

- `baseTiles.tileBaseUrl`
- `baseTiles.zoom`
- `viewport.width / height`
- `viewport.minLon / minLat / maxLon / maxLat`
- `variants[].controls`
- `variants[].legs`

这些字段足够前端做只读预览。

---

## 6. 前端如何消费

前端准备页的消费方式建议如下：

### 6.1 底图渲染

- 使用低级别瓦片作为底图来源
- 按 `viewport` 与 `zoom` 计算需要的瓦片范围
- 只在准备页内绘制一张静态缩略底图

### 6.2 赛道叠加

- 根据 `viewport` 把控制点经纬度投影到预览图坐标
- 在 canvas 或同等绘制层上叠加：
  - 起点
  - 终点
  - 控制点
  - 腿线

### 6.3 多赛道切换

- 切换赛道时不重新换底图
- 只重绘叠加层

### 6.4 展示层级

准备页只做：

- 只读预览
- 不拖拽
- 不缩放
- 不交互打点

---

## 7. 多赛道场景如何处理

多赛道场景是这套方案的重点。

当前建议规则：

1. 同一活动下，所有 variant 共用一张底图
2. 当前选中的 variant 决定叠加层内容
3. 如果活动允许手动选赛道，切换赛道时预览同步切换
4. 如果活动是随机分配或后台指定：
   - 准备页在最终绑定前可只显示地点底图
   - 一旦后端返回最终绑定赛道，再显示该赛道 overlay

这套设计和当前多赛道 Variant 架构是一致的：

- 底图属于地图对象
- 叠加属于 variant 对象

---

## 8. 预览级别建议

建议预览能力分成 3 档：

### 8.1 `none`

- 不显示地图预览
- 只显示地点、地图、赛道文字信息

适用于：

- 不允许赛前预览的正式比赛

### 8.2 `summary`

- 显示底图
- 只显示简化赛道范围、起终点或大致区域
- 不暴露完整点位和腿线

适用于：

- 需要局前空间感，但不允许完整剧透路线

### 8.3 `full`

- 显示底图
- 显示完整点位与腿线

适用于：

- 体验活动
- 教学活动
- 低门槛公开活动

V1 建议先直接支持：

- `none`
- `full`

后面再补 `summary`。

---

## 9. 分阶段实施建议

### 9.1 V1

只做：

- 低级别瓦片底图
- 前端动态赛道叠加
- 支持多赛道切换联动
- 只读展示

不做：

- 复杂预览交互
- 预览模式细分
- 缩放与拖拽

### 9.2 V2

补：

- `none / summary / full`
- 不同活动类型的预览策略
- 更细的赛道保密规则

### 9.3 V3

考虑扩展到：

- 活动详情页缩略预览
- 活动列表卡片缩略图
- 赛后结果页路线回看缩略图

---

## 10. 不建议的方案

当前不建议：

### 10.1 单独制作一套预览地图资源

问题：

- 成本高
- 一致性差
- 发布链复杂

### 10.2 前端直接现场拼完整高精度瓦片地图

问题：

- 性能波动大
- 首次加载慢
- 多端一致性差

### 10.3 后端为每个 variant 预生成完整大图

问题：

- 多赛道下资源重复
- 每次改赛道都要重生图
- 扩展性不如“底图固定 + 叠加切换”

---

## 11. 当前建议结论

准备页地图预览最推荐的路线是：

**使用低级别正式瓦片作为预览底图，由前端在准备页动态叠加当前赛道。**

这条路线的优点是：

- 不新增独立地图资源
- 底图与正式地图同源
- 多赛道切换成本低
- 前后端职责清晰
- 易于分阶段落地

当前建议优先推进：

1. 后端补预览元数据最小字段
2. 前端在准备页实现只读底图 + overlay
3. 先支持 `full`
4. 后续再扩 `summary / none`
