Fără Descriere

BAI d477cf29ec 修改水效果 3 săptămâni în urmă
.vscode 3d6bab9555 项目初始 1 lună în urmă
public d8a352c4e0 标签转跳 3 săptămâni în urmă
src d477cf29ec 修改水效果 3 săptămâni în urmă
.gitignore 3d6bab9555 项目初始 1 lună în urmă
README.md d8a352c4e0 标签转跳 3 săptămâni în urmă
demo.txt d8a352c4e0 标签转跳 3 săptămâni în urmă
index.html 3d6bab9555 项目初始 1 lună în urmă
package-lock.json b6c3ea47e2 项目结构优化 新增渡槽场景 3 săptămâni în urmă
package.json b6c3ea47e2 项目结构优化 新增渡槽场景 3 săptămâni în urmă
tsconfig.app.json 3d6bab9555 项目初始 1 lună în urmă
tsconfig.json 3d6bab9555 项目初始 1 lună în urmă
tsconfig.node.json 3d6bab9555 项目初始 1 lună în urmă
vite.config.ts b6c3ea47e2 项目结构优化 新增渡槽场景 3 săptămâni în urmă

README.md

ThreeEngine - 三维水利场景

基于 Three.js 的水利三维可视化场景组件。

嵌入到其他 Vue 项目

1. 安装依赖

npm install three 3d-tiles-renderer @loaders.gl/3d-tiles @loaders.gl/core @loaders.gl/gltf

2. 拷贝项目

将本项目 src/ 目录下的以下内容拷贝到目标项目中:

路径 说明
src/components/ Vue 组件(Scene3D、WaterLevelLabel 等)
src/config/ 场景配置文件
src/assets/ 纹理、模型、图标等静态资源

3. 使用组件

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import Scene3D from '@/components/Scene3D.vue'

const sceneRef = ref<InstanceType<typeof Scene3D>>()

onMounted(() => {
  // 3 秒后更新标签值
  setTimeout(() => {
    sceneRef.value?.setLabelValue('6602380005', 1.58)
  }, 3000)
})
</script>

<template>
  <Scene3D
    ref="sceneRef"
    :label-values="{ '6602380005': 0.00 }"
    :show-debug-tools="false"
    :tileset-url="'/scene/tileset.json'"
  />
</template>

Props 说明

Prop 类型 默认值 说明
labelValues Record<string, number> {} 标签数据值,{ [标签id]: 数值 }
showDebugTools boolean false 是否显示调试面板
tilesetUrl string '/scene/tileset.json' 3D Tiles 数据地址
cameraPosition { x, y, z } 默认位置 初始相机位置
cameraTarget { x, y, z } 默认 target 初始相机目标点

Expose API(通过 ref 调用)

更新标签值

// 更新单个标签
sceneRef.value?.setLabelValue('6602380005', 1.58)

// 批量更新标签
sceneRef.value?.setLabelValues({ '6602380005': 1.58, 'gate002': 0.5 })

// 直接修改响应式数据
sceneRef.value?.labelValues['6602380005'] = 3.0

相机控制

// 飞行动画(自由指定位置)
sceneRef.value?.flyTo(
  { x: -900, y: 60, z: -2100 },  // 目标位置
  { x: -843, y: 12, z: -2182 },  // 目标看点(可选)
  2000                             // 动画时长(毫秒)
)

// 按预设名称跳转(配置在 sceneConfig.ts 的 cameraPresets 中)
sceneRef.value?.flyToPreset('default')      // 默认视角,时长 1500ms
sceneRef.value?.flyToPreset('gate', 2000)   // 闸门视角,时长 2000ms

// 查看所有预设
console.log(sceneRef.value?.cameraPresets)

镜头预设配置

src/config/sceneConfig.ts 中配置镜头预设:

export const cameraPresets: CameraPreset[] = [
  {
    id: 'default',
    name: '默认视角',
    positionX: -831.56685,
    positionY: 40.63456,
    positionZ: -2225.321,
    targetX: -843.0744,
    targetY: 12.01539,
    targetZ: -2182.06814,
  },
]

标签配置

src/config/sceneConfig.ts 中配置标签:

export const waterLevelLabels: WaterLevelLabelConfig[] = [
  {
    id: '6602380005',           // 唯一标识,外部传值时使用此 id
    name: '莫勒切河节制分水闸闸后水量监测',
    type: 'waterLevel',          // 标签类型,决定图标和文本
    positionX: -830.11,          // 场景中的位置(外部无需关心)
    positionY: 14,
    positionZ: -2156.83,
    initialValue: 0.00,         // 初始默认值
  },
]

扩展标签类型

sceneConfig.ts 中注册新类型:

export type LabelDataType = 'waterLevel' | 'flowRate' | '你的新类型'

export const labelTypeRegistry: Record<LabelDataType, LabelTypeDisplayConfig> = {
  waterLevel: {
    icon: 'shuiliang.png',
    unit: 'm³/s',
    label: '水 量',
    decimalPlaces: 2,
  },
  flowRate: {
    icon: 'shuiliang.png',
    unit: 'm³/s',
    label: '流 量',
    decimalPlaces: 2,
  },
  // 新类型: 在这里新增
}

之后在 WaterLevelLabel.vueiconMap 中映射新的图标文件即可。


数据传输与更新机制

数据流总览

外部系统
   │
   ├── ① Props 传入初始值
   │     :label-values="{ '6602380005': 0.00 }"
   │
   ├── ② 调用 Expose 方法更新
   │     sceneRef.setLabelValue(id, value)
   │     sceneRef.setLabelValues({ id: value, ... })
   │
   └── ③ 直接修改响应式数据
         sceneRef.labelValues[id] = value
              │
              ▼
    ┌─────────────────────────┐
    │  场景组件内部             │
    │  labelValues (reactive)  │
    │  ├── Scene3D.vue         │
    │  └── DucaoScene.vue      │
    └──────────┬──────────────┘
               │ :value prop 绑定
               ▼
    ┌─────────────────────────┐
    │  WaterLevelLabel.vue     │
    │  watch(value)            │
    │    → redrawTexture()     │
    │    → Canvas 重新绘制     │
    │    → spriteTexture       │
    │      .needsUpdate = true │
    └─────────────────────────┘

三种更新方式

方式一:Props 传入(初始化 / 响应式更新)

<Scene3D :label-values="{ '6602380005': 1.58 }" />
  • 父组件通过 prop labelValues 传入一个 Record<string, number>
  • 场景内部 watch(() => props.labelValues) 监听变化,同步到本地的 labelValues reactive 对象
  • 注意:由于 watch 是 deep 监听,父组件更新 prop 对象的属性值时会自动触发重绘

方式二:调用 Expose 方法

// 更新单个标签
sceneRef.value?.setLabelValue('6602380005', 1.58)

// 批量更新
sceneRef.value?.setLabelValues({
  '6602380005': 1.58,
  '6602380006': 3.20,
})
  • setLabelValue(id, value) — 直接设置 labelValues[id] = value
  • setLabelValues(levels) — 遍历设置多个值
  • 两种方法都直接操作 reactive 对象,Vue 的响应式系统会自动触发 WaterLevelLabel 的 prop 更新

方式三:直接操作响应式数据

sceneRef.value?.labelValues['6602380005'] = 3.0
  • 直接修改 labelValues 对象的属性
  • 由于 labelValuesreactive() 对象,Vue 会自动追踪变化

内部更新链路(逐层追踪)

labelValues[id] 更新
  │
  ▼
Scene3D.vue / DucaoScene.vue 的模板
  │  <WaterLevelLabel :value="labelValues[data.config.id]" />
  │
  ▼
WaterLevelLabel.vue props.value 更新
  │
  ▼
watch(() => props.value) 触发
  │
  ▼
redrawTexture() 被调用
  ├── ctx.clearRect()          → 清空 Canvas
  ├── ctx.drawImage(iconImg)   → 重绘图标
  ├── drawText()               → 重绘数值文本
  │     ├── 使用 cfg.label (如 "水 量")
  │     ├── 使用 props.value.toFixed(cfg.decimalPlaces)
  │     └── 白色文字 + 半透明单位文本
  └── spriteTexture.needsUpdate = true  → Three.js 更新纹理
       │
       ▼
       屏幕上的 Sprite 立即显示新数值

初始化流程

组件挂载
  │
  ▼
initLabelData()
  │
  ├── 遍历 sceneLabels (从 sceneConfig.ts 读取标签配置)
  ├── 对每个标签:
  │     ├── 优先读取 props.labelValues[cfg.id](外部传入值)
  │     └── 如果外部未传入,则使用 cfg.initialValue(配置默认值)
  │
  ▼
  labelDataList 构建完成
  │
  ▼
3D 场景初始化 → labelDataList.forEach(init)
  │
  ▼
  WaterLevelLabel.init(scene, camera)
    → ensureSprite() → 创建 CanvasTexture Sprite
    → createTooltip() → 创建悬停提示 Sprite

标签配置数据结构

interface WaterLevelLabelConfig {
  id: string           // 唯一标识,外部传值时使用
  name: string         // 名称(悬停 tooltip 显示)
  type: LabelDataType  // 标签类型(waterLevel / flowRate / StressMonitor)
  scene: SceneType     // 所属场景(main / ducao)
  positionX: number    // 3D 场景坐标
  positionY: number
  positionZ: number
  initialValue: number // 初始默认值
}

两个场景的差异

场景 组件 标签来源 标签数量
主场景(沉砂池) Scene3D.vue sceneLabels.main 6 个
渡槽场景 DucaoScene.vue sceneLabels.ducao 8 个

两个场景的标签配置统一在 sceneConfig.tswaterLevelLabels 数组中管理,通过 scene 字段区分。但数据更新方式完全一致(Props + Expose + 直接修改)。