|
|
2 주 전 | |
|---|---|---|
| .vscode | 1 개월 전 | |
| public | 2 주 전 | |
| src | 2 주 전 | |
| .gitignore | 1 개월 전 | |
| README.md | 3 주 전 | |
| demo.txt | 3 주 전 | |
| index.html | 1 개월 전 | |
| package-lock.json | 3 주 전 | |
| package.json | 3 주 전 | |
| tsconfig.app.json | 1 개월 전 | |
| tsconfig.json | 1 개월 전 | |
| tsconfig.node.json | 1 개월 전 | |
| vite.config.ts | 3 주 전 |
基于 Three.js 的水利三维可视化场景组件。
npm install three 3d-tiles-renderer @loaders.gl/3d-tiles @loaders.gl/core @loaders.gl/gltf
将本项目 src/ 目录下的以下内容拷贝到目标项目中:
| 路径 | 说明 |
|---|---|
src/components/ |
Vue 组件(Scene3D、WaterLevelLabel 等) |
src/config/ |
场景配置文件 |
src/assets/ |
纹理、模型、图标等静态资源 |
<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>
| 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 | 初始相机目标点 |
// 更新单个标签
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.vue 的 iconMap 中映射新的图标文件即可。
外部系统
│
├── ① 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 │
└─────────────────────────┘
<Scene3D :label-values="{ '6602380005': 1.58 }" />
labelValues 传入一个 Record<string, number>watch(() => props.labelValues) 监听变化,同步到本地的 labelValues reactive 对象// 更新单个标签
sceneRef.value?.setLabelValue('6602380005', 1.58)
// 批量更新
sceneRef.value?.setLabelValues({
'6602380005': 1.58,
'6602380006': 3.20,
})
setLabelValue(id, value) — 直接设置 labelValues[id] = valuesetLabelValues(levels) — 遍历设置多个值WaterLevelLabel 的 prop 更新sceneRef.value?.labelValues['6602380005'] = 3.0
labelValues 对象的属性labelValues 是 reactive() 对象,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.ts 的 waterLevelLabels 数组中管理,通过 scene 字段区分。但数据更新方式完全一致(Props + Expose + 直接修改)。