Files
planet/docs/plans/earth-mobile-center-country-highlight-plan.md
linkong e1984c7a35 release: bump version to 0.49.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 17:42:27 +08:00

8.5 KiB
Raw Blame History

Earth Mobile Center Country Highlight Plan

Goal

移动端打开 Earth 国界图层后,用屏幕中心,也就是当前镜头正对的地球表面位置,自动识别所在国家,并高亮该国家国界。

桌面端仍保持现有 hover 行为。移动端不引入新的国界渲染体系,而是复用已有 country-boundaries.js 的 GeoJSON 命中和 hover 高亮能力。

Criteria for success

  1. 移动端 layout-mode-mobile 下,国界图层开启后,屏幕中心所在国家会自动高亮。
  2. 移动端旋转、缩放、巡航或自动旋转地球时,高亮会跟随镜头中心更新。
  3. 屏幕中心落在海洋或没有命中地球时,国家高亮会清除。
  4. 国界图层关闭时,不执行中心国家识别,也不显示残留高亮。
  5. 桌面端 pointer hover 行为保持不变。
  6. 移动端抽屉、搜索、设置、媒体、详情等前景 UI 打开时,不因为用户操作 UI 产生明显误高亮或抖动。
  7. 中心识别有节流或状态缓存,不把 GeoJSON point-in-polygon 检测放到无条件每帧高频执行。
  8. 实现后能通过本地静态检查或前端构建,并用移动端 viewport 手动或 Playwright 验证核心场景。

Existing pieces

当前项目已经具备大部分基础能力:

  • frontend/public/earth/js/country-boundaries.js
    • updateCountryBoundaryHover(coords):根据 { lat, lon } 命中国家并更新高亮线。
    • clearCountryBoundaryHover():清除当前 hover 高亮。
    • getShowCountryBoundaries():判断国界线图层是否可见。
  • frontend/public/earth/js/utils.js
    • screenToEarthCoords(clientX, clientY, camera, earth, domElement):屏幕坐标 raycast 到地球表面。
    • vector3ToLatLon(vector):地球本地坐标转经纬度。
  • frontend/public/earth/js/constants.js
    • COUNTRY_BOUNDARY_CONFIG 已定义普通国界线和 hover 国界线样式。
  • 移动端布局状态已经通过 layout-mode-mobile body class 区分。

因此本需求的核心不是新增图层,而是补一个移动端中心取点控制器。

Non-goals

  • 不改变桌面端 hover 交互。
  • 不替换 countries-admin0.min.geojson 数据源。
  • 不新增后端 API。
  • 不把国家面填充做成新的 selected country 面状 shader。
  • 不为移动端增加永久准星 UI除非后续产品明确需要视觉准星。

Implementation plan

1. Add a small mobile center hover controller

新增一个轻量函数,建议放在现有主循环附近或单独模块,例如:

frontend/public/earth/js/mobile-center-country-highlight.js

建议导出:

updateMobileCenterCountryHighlight({
  camera,
  earth,
  renderer,
  now,
  isBlocked,
});

clearMobileCenterCountryHighlight();

职责:

  1. 判断是否处于移动端。
  2. 判断国界图层是否开启。
  3. 判断当前是否被移动端前景 UI 阻塞。
  4. 对 renderer canvas 中心点做 raycast。
  5. 命中地球后转经纬度。
  6. 调用 updateCountryBoundaryHover({ lat, lon })
  7. 无命中或禁用时调用 clearCountryBoundaryHover()

2. Use canvas center, not window center

中心点应基于 renderer canvas rect 计算:

const rect = renderer.domElement.getBoundingClientRect();
const clientX = rect.left + rect.width / 2;
const clientY = rect.top + rect.height / 2;

这样在移动端安全区、地址栏变化、viewport resize 或 canvas 非全屏时仍然准确。

3. Convert center point into country hover coords

复用已有工具:

const point = screenToEarthCoords(clientX, clientY, camera, earth, renderer.domElement);
if (!point) {
  clearCountryBoundaryHover();
  return;
}

const coords = vector3ToLatLon(point);
updateCountryBoundaryHover(coords);

注意:screenToEarthCoords 返回的是 earth local point符合 vector3ToLatLon 的输入语义。

4. Gate updates by mobile and foreground UI state

建议新增一个本地判断函数:

function isMobileCenterCountryHighlightBlocked() {
  return (
    !document.body.classList.contains("layout-mode-mobile") ||
    document.body.classList.contains("earth-search-open") ||
    document.body.classList.contains("earth-settings-open") ||
    document.body.classList.contains("earth-media-open") ||
    document.body.classList.contains("earth-info-open")
  );
}

如果移动端抽屉只是半收起、且没有覆盖中心视野,可以继续允许中心高亮。若实际体验里抽屉展开会遮挡中心点,再把 drawer open 状态纳入阻塞条件。

5. Throttle and cache center updates

GeoJSON polygon 命中不应该无条件每帧执行。

第一版建议:

  • throttleMs = 120
  • 缓存上次经纬度,中心点变化小于 0.05 度时跳过。
  • 禁用、切回桌面、图层关闭、UI 阻塞时立即清除一次高亮。

伪代码:

if (now - lastUpdateAt < 120) return;
if (Math.abs(coords.lat - lastLat) < 0.05 && Math.abs(coords.lon - lastLon) < 0.05) return;

6. Wire into the Earth animation loop

frontend/public/earth/js/main.js 的动画循环中调用:

updateMobileCenterCountryHighlight({
  camera,
  earth,
  renderer,
  now: performance.now(),
  isBlocked: isMobileCenterCountryHighlightBlocked(),
});

这样自动旋转、手势旋转、缩放和巡航都会自然更新。

7. Keep desktop hover unchanged

桌面 pointer hover 仍然走当前逻辑。

移动端中心高亮只在 layout-mode-mobile 下生效,不应该监听 pointer move也不应该抢占 desktop hover 状态。

8. Optional visual tuning

第一版复用:

  • COUNTRY_BOUNDARY_CONFIG.hoverLineColor
  • COUNTRY_BOUNDARY_CONFIG.hoverLineOpacity
  • COUNTRY_BOUNDARY_CONFIG.hoverGlowOpacity

如果移动端体验太强,可以后续加独立配置:

mobileCenterHoverLineOpacity
mobileCenterHoverGlowOpacity

但第一版不建议过早分叉样式。

Verification

Static checks

  1. npm 前端构建或现有 lint/typecheck 命令通过。
  2. rg 确认新增函数只在移动端路径调用,不影响桌面 pointer hover。
  3. git diff --stat 和目标文件 diff 确认改动范围集中。

Manual mobile checks

使用移动端 viewport例如 390x844

  1. 打开 Earth。
  2. 开启国界图层。
  3. 转动地球到中国、美国、澳大利亚等大块陆地区域,确认中心国家国界高亮。
  4. 转动到太平洋或印度洋,确认高亮消失。
  5. 缩放地球,确认高亮仍跟随中心点。
  6. 打开移动端搜索、设置、媒体或详情面板,确认没有明显误高亮或抖动。
  7. 切回桌面 viewport确认 hover 仍由鼠标位置控制。

Playwright smoke check

如果已有 Playwright 流程,建议补一个移动端 smoke

  1. 设置 viewport 为手机尺寸。
  2. 打开 Earth 页面。
  3. 开启国界图层。
  4. 等待国界数据加载。
  5. 截图确认中心附近国家边界有 hover 高亮线。

这个 smoke 不必断言具体国家名称,因为当前功能核心是视觉高亮;更稳定的自动化可以后续通过暴露 debug state 实现。

Risks and mitigations

Polygon hit cost too高

风险:移动端设备上频繁 featureContains 可能带来卡顿。

缓解:

  • 使用 120ms 节流。
  • 经纬度变化小于阈值时跳过。
  • 后续如仍慢,再为 GeoJSON features 预计算 bbox先 bbox 粗筛再 point-in-polygon。

UI blocking state 不完整

风险:某些移动端前景 UI 没有对应 body class中心点被遮挡但高亮仍更新。

缓解:

  • 第一版覆盖现有主要 class。
  • 验证时记录遗漏项,补充到 isMobileCenterCountryHighlightBlocked()

Desktop hover 被移动端状态污染

风险:移动端中心高亮和桌面 hover 共用 _hoveredFeature 状态。

缓解:

  • 只在 layout-mode-mobile 下运行中心高亮。
  • 切出 mobile 或图层关闭时调用一次 clearCountryBoundaryHover()
  • 不改 updateCountryBoundaryHover() 的语义。

Milestones

  1. 设计落地:完成本 plan明确目标和验收标准。
  2. 最小实现:新增移动端中心取点 controller并接入 animation loop。
  3. 性能保护:加入节流、经纬度阈值和禁用态清理。
  4. 验证:本地构建通过,移动端 viewport 手动检查通过。
  5. 调优:根据截图或真机体验微调阻塞条件和节流阈值。