Files
planet/docs/plans/earth-interactable-layer-plan.md
2026-04-30 14:30:12 +08:00

17 KiB
Raw Blame History

Earth Interactable Layer Plan

背景

状态Phase 1 已经开始落地Phase 2 的 BGP 事件 / 观测站迁移和 Phase 3 的算力中心迁移也已完成。frontend/public/earth/js/interactable.js 已新增AIS 船只、BGP 事件、BGP 观测站和算力中心图层已经改为通过 createInteractableLayer() 使用通用批量 Points、hover / locked overlay、默认 glow、状态更新、asset icon 预加载、屏幕空间 picking、固定 / 距离缩放和跨 Interactable 同坐标避让。登陆点因 THREE.Points 边缘深度裁切和贴地层级要求,已退回专用 THREE.Sprite 黄色球路径,并与海缆同高度同 renderOrder。后续阶段聚焦把可复用的扩圈 / 雷达扇形动画正式沉淀成 animations 扩展。

当前实现说明和接入示例见:

当前 AIS 船只图层已经形成了一个适合作为基准的交互图标模式:

  • 普通态使用批量 THREE.Points 渲染,避免每个对象一个 Sprite 带来的 draw call 和透明排序压力。
  • hover / locked 态使用单点 overlay 叠加 glow不改变普通批次交互反馈清晰且成本低。
  • moving / anchored 船只通过 canvas 点纹理表达不同形状moving 船只还按航向分桶。
  • 拾取走屏幕空间命中,拖动和惯性期间跳过高频 hover picking。
  • 图层高度贴近地表,仅保留很小的深度余量,避免“浮在表面层”的观感。

这个模式不应该只服务船只。后续 BGP 事件、BGP 观测站、算力中心、新闻事件、告警、地面传感器等都可能需要“图标类可交互元素”。如果每个图层继续各写一套 icon、glow、hover、locked、动画、picking 和图例逻辑,视觉会漂移,性能策略也会重复分叉。登陆点已经验证为例外:需要完整贴地且不被球面边缘裁切时,专用 Sprite 路径比通用 Points 更合适。

目标是把船只图层的成功做法抽象成一个通用接口:业务图层只描述“要画什么、在哪里、怎么交互”,底层统一负责批量渲染、默认 glow、状态 overlay、动画槽位、拾取和生命周期。

目标

  1. 建立统一的 Earth 交互图标接口,作为未来地表图标类元素的默认入口。
  2. 以 AIS 船只 glow 为默认 glow 视觉,其它图标默认沿用同一套 glow 质感。
  3. 保留图标颜色、状态颜色、hover 放大、locked 强调、dimmed 聚焦、动画扩展等能力。
  4. 支持 canvas / SVG / image icon不强行要求所有图标都可重着色。
  5. 保持船只当前性能路线:批量绘制普通态,少量 overlay 处理交互态。
  6. 给 BGP 事件扩圈、BGP 观测站雷达扇形等补充动画留出正式扩展点。

非目标

  • 不在第一阶段重写所有 Earth 图层。
  • 不把卫星、海缆、国家边界、真实地形这类非图标图层纳入同一个接口。
  • 不为了抽象牺牲业务图标的差异表达例如船只航向、BGP 事件严重级别、观测站雷达扫掠。
  • 不要求图片图标支持运行时重着色;图片图标只能通过预制多状态图片或 overlay tint 做有限表达。

核心设计

建议新增一个通用模块,例如:

frontend/public/earth/js/interactable.js

它导出一个工厂或注册函数:

createInteractableLayer({
  id,
  earth,
  renderOrder,
  altitudeOffset,
  icon,
  scale,
  glow,
  colors,
  states,
  animations,
  picking,
  data,
  getPosition,
  getKind,
  getRotation,
  getPayload,
});

业务模块仍保留自己的数据加载、图例、详情卡字段和业务语义。例如 vessels.js 负责 AIS 数据和船型映射,但 icon 渲染、hover overlay、locked overlay、默认 glow 和屏幕空间 picking 可以逐步迁入 interactable.js

参数草案

参数 类型 / 示例 默认值 说明
id "vessels" 必填 图层唯一标识,用于 debug、picking、legend 和状态缓存。
earth THREE.Object3D 必填 图层挂载目标,通常是 Earth root。
renderOrder 4.4 4 普通 icon 批次和 overlay 的基础渲染顺序。
altitudeOffset 0.2 0.2 图层高度,语义为 CONFIG.earthRadius + altitudeOffset。地表图标默认贴近真实地形基础层。
icon { type, source, draw, size, bins } 必填 图标来源。支持 canvas draw、SVG URL、image URL、内置 shape。
icon.fitSize 60{ width: 60, height: 60 } atlasCellSize asset 图标在 atlas canvas 内的最大绘制尺寸,默认居中等比 contain。SVG / 图片文件只负责原始形状,不需要为了显示大小手写 transform。
scale { base, min, max } { base: 1 } 基础缩放和距离稳定范围。当前船只可映射到 VESSEL_POINT_SIZE / baseScale
sizeMode "fixed" / "distance" "fixed" 是否固定屏幕像素尺寸;非 fixed 时按相机到地表距离做比例缩放。
sizeScale { min, max, referenceFov } { min: 0.12, max: 3, referenceFov: 75 } sizeMode !== "fixed" 时的缩放限制和参考视角。
glow.enabled true / false true 是否启用默认 glow。默认 glow 以船只 hover / locked overlay 为基准。
glow.intensity 0.0 - 2.0 1 glow 强度,内部映射到 canvas shadowBlur、opacity 或 shader uniform。
glow.colorMode "state" / "icon" / "fixed" "state" glow 颜色来源,默认跟随状态颜色。
hover.scale 1.0 - 2.0 1.18 hover 放大倍率。当前船只保持同尺寸 glow overlay接口仍保留放大能力供其它图层使用。
hover.mode "scale" / "glow-only" / "custom" "scale" hover 反馈方式。船只可用 "glow-only",其它图标默认放大。
colors.normal "#4A90D9" icon 原色 普通态颜色。只有可上色 icon 生效。
colors.hover "#7dd3fc" normal hover 态颜色。
colors.locked "#ffffff" hover locked 态颜色。
colors.dimmed "#9B9B9B" normal 聚焦其它对象时的弱化颜色。
colors.byKind { cargo: "#4A90D9" } {} 按业务类型着色如船型、BGP 严重级别。
colorable true / false 由 icon 类型推断 canvas shape 和 SVG mask 通常可上色;图片默认不可上色。
opacity { normal, hover, locked, dimmed } 船只当前值 各状态透明度。
rotation { enabled, bins, getAngle } disabled 是否按角度分桶,例如船只按 COG 分 32 桶。
animations IconAnimationSpec[] [] 补充动画列表,例如扩圈、雷达扇形、脉冲、轨迹尾迹。
picking.radiusPx 22 20 屏幕空间命中半径。
picking.throttleMs 100 80 hover picking 节流。
picking.skipWhileDragging true true 拖动和惯性期间跳过 hover picking。
zIndexPolicy "surface-icon" "surface-icon" 预设层级策略,避免每个业务图层手写高度和 renderOrder。
avoidance.enabled true / false true 是否参与跨 Interactable 的同坐标避让。默认开启,同一经纬度下的图标会沿地表切平面小幅排开,方便辨认和选择。
avoidance.radius number 1.1 同坐标避让的第一圈半径,单位为地球本地坐标单位。
avoidance.precision number 4 经纬度归并精度,默认约等于只处理几乎完全重叠的图标。
legend { label, color, shape }[] [] 可选图例声明,业务层也可以继续自己导出。
metadata object {} 业务扩展数据,不参与渲染但参与 tooltip / info-card / search。

Icon 规格

图标输入建议分三类:

{
  type: "canvas-shape",
  size: 128,
  draw(ctx, state) {
    // draw triangle / dot / custom shape
  },
}
{
  type: "svg-mask",
  source: "/earth/assets/icons/bgp-event-dot.svg",
  colorable: true,
}
{
  type: "image",
  source: "/earth/assets/icons/vendor-logo.png",
  colorable: false,
  stateSources: {
    hover: "/earth/assets/icons/vendor-logo-hover.png",
  },
}

颜色策略:

  • canvas-shape 默认可上色,适合船只、事件点、雷达站这类符号。
  • svg-mask 如果能作为 mask 使用,则可上色;如果是完整多色 SVG则按图片处理。
  • image 默认不可上色;需要状态变化时使用 stateSources 或额外 glow / ring。

默认 Glow 规范

默认 glow 以当前船只 overlay 为视觉基准:

  • 普通态尽量不启用 glow保持地图干净。
  • hover / locked 态叠加同位置 overlay。
  • glow 颜色默认跟随状态颜色或业务类型颜色。
  • glow blur 应该稳定,不随 camera zoom 夸张膨胀。
  • 允许通过 glow.intensity 控制强度,但不要让业务图层各自发明完全不同的光晕语言。

建议内部把 glow 拆成两个层次:

  1. textureGlowcanvas texture 里的 shadowBlur,适合小图标 hover / locked。
  2. effectGlow:额外 ring / halo / pulse适合告警、BGP 事件和锁定强调。

状态模型

通用状态至少包含:

状态 触发 默认表现
normal 普通显示 批量 Points使用 normal 颜色和 opacity。
hover 指针悬停 默认放大并显示 glow船只可配置为同尺寸 glow-only。
locked 点击锁定 / 详情打开 强 glow、更高 opacity可选 ring 或 pulse。
dimmed 聚焦其它对象 降低 opacity保留上下文。
hidden 图层关闭或过滤 不参与绘制和 picking。
alert 业务告警 可叠加动画,不替代 locked 状态。

状态更新需要增量化:只在 hover 目标、locked 目标、过滤条件、数据版本或相机距离阈值变化时更新,不在每帧遍历全部 icon 写材质属性。

动画扩展

动画不直接塞进 icon 基础参数,而是作为 animations 列表注册。每个动画声明自己的 geometry / material / update 策略:

{
  type: "expanding-ring",
  when: ["alert", "locked"],
  color: "state",
  radiusPx: [10, 42],
  durationMs: 1400,
  opacity: [0.8, 0],
}
{
  type: "radar-sweep",
  when: ["normal", "hover", "locked"],
  angleDeg: 72,
  rotationMs: 2600,
  opacity: 0.36,
}

首批建议内置动画:

动画 用例 说明
pulse-ring locked、告警点 原地呼吸环,强调选中对象。
expanding-ring BGP 事件 向外扩散的事件波纹。
radar-sweep BGP 观测站 扇形扫描,可持续旋转。
orbiting-dot 数据流 / collector 活跃态 小点绕 icon 环绕,表达活动状态。
trail 移动目标 可选短尾迹,船只或飞机类目标使用。

动画必须支持批量或分组绘制,避免为每个对象创建独立的高频更新对象。只有 locked / hover / 少量 alert 对象可以使用单对象 overlay。

渲染策略

普通态

普通态优先使用分桶 THREE.Points

  • 按 icon 类型、可上色策略、旋转分桶、纹理 key 分组。
  • 每组一个 BufferGeometry,存 positioncolor、必要的 payloadIndex
  • PointsMaterial.sizeAttenuation = false,保持屏幕尺寸稳定。
  • depthTest = truedepthWrite = false,避免遮挡关系破坏地表。

交互态

hover / locked 使用少量 overlay

  • overlay 复用 THREE.Points 单点对象或小型 ring mesh。
  • overlay texture 从统一 cache 获取。
  • overlay 更新只写当前 hover / locked 的 position、texture、opacity、size。

高密度升级

当某类图标超过分桶 Points 的舒适区,才考虑升级:

  • InstancedBufferGeometry billboard。
  • 自定义 shader 支持 per-instance rotation / scale / opacity。
  • 视口 bbox / LOD / cluster。

这个升级不应该改变业务接口,只替换底层 renderer。

Picking 策略

沿用船只当前方向:

  • 默认屏幕空间 picking而不是 Three.js 对每个 Sprite / Points 做 raycast。
  • 每个 icon 保留世界坐标和业务 payload。
  • 每次 pointer move 将候选点投影到屏幕,按半径和深度判断命中。
  • 拖动、惯性旋转、相机剧烈变化期间跳过 hover picking。
  • click 时允许做一次更精确的 picking。

后续可以按图层或经纬度网格增加空间索引,减少候选点数量。

与现有图层的迁移路径

Phase 1抽出船只基准能力

  • vessels.js 提取 texture cache、canvas icon draw、overlay glow、分桶 Points 创建、状态增量更新。
  • 保持 vessels.js 的公开 API 不变:loadVessels()toggleVessels()getVesselMarkers() 等继续可用。
  • 新模块先只服务船只,确保视觉没有回退。

Phase 2迁移 BGP 事件和观测站

  • BGP 事件使用 canvas-shape,已接入 Interactable
  • 严重级别映射到 colors.byKind,并通过通用 getPointSizeMultiplier 保留严重级别尺寸倍率。
  • 当前扩圈效果保留在 BGP 业务动画中,并跟随 Interactable marker 位置更新。
  • BGP 观测站主图标已接入 Interactable,活跃度映射到颜色和 getPointSizeMultiplier
  • BGP 观测站 halo / 覆盖扇形继续由 BGP 业务动画表达扫描,并跟随 Interactable marker 位置更新。

Phase 3迁移算力中心并评估登陆点

  • 算力中心保留现有业务 icon但接入统一 hover / locked / glow。已完成
  • 登陆点曾接入同一套 Points 渲染,但 pin 类 SVG 在地球边缘会被深度测试裁切;当前保留专用 THREE.Sprite,并使用 canvas 生成黄色扁平球,贴到海缆层级。
  • TODO登陆点暂不迁移到完整 Interactable。后续若要统一交互接口优先考虑 Sprite-backed adapter只对齐 getMarkers()getPointerIntersections()setMarkerState()updateVisualState() 等外观协议,不强行复用 THREE.Points、atlas 和跨图层避让。
  • 检查图例、搜索和 info-card 是否只依赖业务 payload而不是依赖渲染对象类型。

Phase 4形成 Earth 图标层规范

  • docs/technical/zh/earth-frontend-context.md 记录当前实现入口。
  • docs/technical/zh/earth-layer-style-reference.md 记录默认 glow、状态颜色、默认高度和动画参数。
  • docs/technical/zh/earth-render-layer-order.md 记录 surface icon renderOrder 范围。

风险与约束

  • 过早抽象可能让船只这种高质量基准被平均化,因此第一阶段必须以船只视觉不回退为验收标准。
  • 图片 icon 不可上色,接口需要明确 colorable = false 的行为,避免业务层误以为颜色一定生效。
  • 动画如果默认开启过多,会重新引入 overdraw 和每帧更新压力;默认只给 hover / locked 或少量 alert 使用。
  • 地形开启时,贴地 icon 需要在高度、depthTestpolygonOffset 和 renderOrder 之间保持平衡。
  • 统一 glow 不等于所有图标一模一样;业务可以调强度和颜色,但不应破坏整体视觉语言。

验收标准

  1. 船只迁入通用接口后普通态、hover、locked、航向、颜色、轨迹和 picking 行为保持一致。
  2. 新增一个 BGP 事件示例图层配置,不需要复制船只渲染代码即可得到 icon、glow、hover 和扩圈动画。
  3. 新增一个 BGP 观测站示例图层配置,不需要自写独立动画循环即可得到雷达扇形。
  4. 关闭图层后对应 icon、overlay、动画和 picking 全部停止。
  5. 高密度数据下普通态仍走批量绘制hover / locked 只更新少量 overlay。
  6. 文档同步说明默认高度、默认 glow、状态模型和动画扩展点。

相关文件

文件 当前角色 未来关系
frontend/public/earth/js/vessels.js 船只基准实现,包含分桶 Points、hover / locked overlay、默认 glow 形态 Phase 1 的抽象来源
frontend/public/earth/js/constants.js 保存船只高度、颜色、透明度、轨迹参数 后续可加入通用 surface icon 默认配置
frontend/public/earth/js/bgp.js BGP 事件和观测站视觉逻辑 BGP 事件和观测站主图标已接入 Interactable扩圈、halo 和覆盖扇形仍保留业务动画
frontend/public/earth/js/compute-centers.js 算力中心 icon 和交互 已通过 Interactable 接入统一 Points、overlay、glow 和 picking
frontend/public/earth/js/cables.js 登陆点 icon 和海缆线 登陆点当前使用专用 THREE.Sprite 黄色球,不再走 Interactable海缆线仍独立渲染
frontend/public/earth/js/main.js 当前集中处理 hover、click、locked 和 info-card 入口 后续需要接入通用 icon picking 结果
docs/technical/zh/earth-layer-style-reference.md 当前视觉参数参考 实现后同步默认 glow 和通用参数
docs/technical/zh/earth-render-layer-order.md 当前层级参考 实现后同步 surface icon 层级范围