2026-06-13-terrain-marker-redesign-design.md 5.7 KB

3D 地形标记点重构设计

背景

当前 TerrainViewer.vue 中的标记点是简单的球体 + 光晕,点击后仅显示文本弹窗。产品希望:

  1. 参考三张数据大屏/矿山监控风格的图片,升级标记点视觉样式。
  2. 点击标记点能够展示视频、HTML 内容或设备数据。
  3. 通过 type 字段区分不同类型,例如 videohtmldevice

目标

  • 标记点视觉风格:A 图的标签牌 + 垂线、C 图的六边形图标、B 图的科技/矿山数据大屏配色。
  • 点击标记点弹出内容面板,支持 video、html、device 三种类型。
  • 保持现有外部 iframe 通信协议(addMarker / removeMarker / clearMarkers / flyTo)向后兼容。
  • 一次只展开一个弹窗;弹窗跟随标记点移动;超出屏幕自动贴边。

参考图风格摘要

图片 风格关键词
5efce9079b9f35554d30a513537fa8c4.png 彩色长方形标签牌、细垂线连接到地面、工业区域名称
8bf719d7d65f9d1c54a2d9e53bf98c95.png 绿色圆形定位图标、黄色人员图标、数据大屏深色背景
4622db6e4a309eca72d06c7e8bbddc91.png 六边形设备图标、深色信息卡片、字段化展示

组件拆分

组件 职责
TerrainViewer.vue Three.js 场景、地形、相机、CSS2DRenderer、标记点 Mesh/标签/垂线、弹窗状态管理、事件转发
MarkerPopup.vue 根据 marker.type 渲染 video / html / device 三种内容
App.vue 传入 markers 数据,接收 markerClick 事件

标记点视觉设计

每个标记点由三部分组成:

  1. 地面六边形底座CylinderGeometry(6 segments),薄盘,贴于地形表面,按类型着色。
  2. 垂线Line 从地面中心向上延伸至标签底部,半透明,长度约 50 地形单位。
  3. 标签牌(CSS2DRenderer):DOM 元素,始终面向相机,位于底座正上方约 50 地形单位处,包含:
    • 左侧六边形 SVG 图标
    • 标题文字
    • 圆角卡片、半透明深色背景、类型色边框

类型配色

type 主色 用途
video #22c55e 青绿 视频监控
device #38bdf8 冰蓝 设备数据
html #f59e0b 琥珀黄 HTML 内容/报表

颜色可通过 marker.color 覆盖。

标签交互

  • 悬停:标签轻微放大/高亮。
  • 点击:打开弹窗,触发 markerClick 事件,并向父页面 postMessage
  • 标签默认始终显示。

Marker 数据结构

{
  lon: 113.722,
  lat: 24.575,
  alt: 0,                 // 可选,海拔偏移
  title: '监控 01',        // 标签牌与弹窗标题
  type: 'video',          // 'video' | 'html' | 'device'
  color: 0x22c55e,        // 可选,覆盖默认类型色

  // video 类型(二选一)
  videoUrl: 'https://.../stream.m3u8',
  iframeUrl: '',

  // html 类型(二选一)
  htmlContent: '<div>...</div>',
  htmlUrl: '',

  // device 类型
  status: '正常',
  metrics: [
    { label: '温度', value: 24, unit: '°C' },
    { label: '湿度', value: 62, unit: '%' }
  ]
}

弹窗设计

弹窗为深色半透明卡片,宽度 320px,最大高度限制,超出滚动。

type: 'video'

按优先级:

  1. 存在 videoUrl<video controls autoplay muted :src="videoUrl" />
  2. 存在 iframeUrl<iframe :src="iframeUrl" />
  3. 都没有 → 显示“未配置视频源”

type: 'html'

按优先级:

  1. 存在 htmlContentv-html 渲染
  2. 存在 htmlUrl<iframe :src="htmlUrl" />
  3. 都没有 → 显示“未配置 HTML 内容”

type: 'device'

渲染设备卡片:

  • 标题 title
  • 状态 status(带颜色小圆点)
    • 正常/在线/良好 → 绿色 #22c55e
    • 警告/注意 → 黄色 #f59e0b
    • 故障/离线/错误 → 红色 #ef4444
    • 其他 → 灰色 #9ca3af
  • 指标列表 metrics: [{ label, value, unit }]

弹窗定位

  • 打开时计算标记点屏幕坐标。
  • 相机旋转/缩放时,每帧更新弹窗位置。
  • 弹窗超出屏幕边缘时自动向内偏移。

事件与数据流

  1. App.vue 传入 markers
  2. TerrainViewer 监听 markers 变化,重建 Mesh 底座、垂线、CSS2D 标签。
  3. 用户点击标记点:
    • 设置 activeMarker
    • 计算弹窗初始屏幕坐标
    • emit('markerClick', marker)
    • postMessage({ type: 'markerClick', ... }) 给父页面
  4. 相机变化时更新弹窗屏幕坐标。
  5. 点击空白处或关闭按钮:activeMarker = null

错误与边界处理

  • type 缺失或非法 → 退化为文本渲染 title / content
  • video 无有效源 → 显示占位提示。
  • html 无内容 → 显示占位提示。
  • devicemetrics → 仅显示 title 和 status。
  • 弹窗移出屏幕 → 自动贴边。
  • CSS2D 标签位于 DOM 层,不会被 WebGL 地形深度遮挡。

兼容性

  • 保留现有 iframe 消息协议:addMarkerremoveMarkerclearMarkersflyTo
  • addMarker 支持传入新字段(typevideoUrlmetrics 等)。
  • 旧数据(无 type)仍可显示,退化为简单文本弹窗。

技术选型

  • 方案 1:CSS2DRenderer 标签 + Mesh 六边形标记 + HTML 弹窗(已确认)
    • 文字标签清晰、样式灵活、弹窗内嵌视频/HTML 自然。
    • 需引入 CSS2DRenderer

待实现检查项

  • 引入 CSS2DRenderer 并集成到渲染循环。
  • 创建六边形底座 Mesh 和垂线 Line。
  • 创建 CSS2D 标签组件(图标 + 标题)。
  • 创建 MarkerPopup.vue 支持 video / html / device。
  • 实现弹窗跟随标记点的屏幕坐标更新。
  • 更新 App.vue 示例数据,覆盖三种类型。
  • 验证现有 iframe 消息协议向后兼容。