ThreeG + BIM + Vue
·
下面我为你详细讲解 ThreeG + BIM + Vue 的完整使用指南,从安装到实战应用。
📦 环境准备与安装
1. Vue 项目创建和依赖安装
# 创建 Vue 项目(选择 Vue 3)
npm create vue@latest my-bim-project
cd my-bim-project
# 安装 Three.js 和相关依赖
npm install three
npm install @types/three # 如果使用 TypeScript
# 安装其他可能需要的工具
npm install axios # 用于请求 BIM 数据
npm install element-plus # UI 组件库(可选)
2. ThreeG 的引入方式
方式一:CDN 引入(推荐初学者)
<!-- public/index.html -->
<head>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/threeg/dist/threeg.min.js"></script>
</head>
方式二:本地文件引入
<!-- 将 threeg 库文件放在 public/jssdk/ 目录下 -->
<script src="./jssdk/threeg.min.js"></script>
方式三:NPM 安装(如果可用)
npm install threeg # 如果官方提供了 npm 包
🏗️ Vue 项目结构规划
src/
├── components/
│ ├── BIMViewer.vue # 主要的BIM查看器组件
│ ├── Toolbar.vue # 工具栏
│ ├── PropertyPanel.vue # 属性面板
│ └── Navigation.vue # 导航控件
├── views/
│ ├── Dashboard.vue # 主面板
│ └── ModelManagement.vue # 模型管理
├── utils/
│ ├── bimLoader.js # BIM加载器
│ └── threeg-helper.js # ThreeG工具函数
└── assets/
└── models/ # 本地模型文件
🔧 核心代码实现
1. 基础 BIM 查看器组件
<template>
<div class="bim-container">
<!-- 3D视图容器 -->
<div ref="viewerContainer" class="viewer-container"></div>
<!-- 加载状态 -->
<div v-if="loading" class="loading-overlay">
<div class="loading-spinner">加载中...</div>
</div>
<!-- 工具栏 -->
<div class="toolbar">
<button @click="fitToView">适应视图</button>
<button @click="toggleWireframe">{{ wireframe ? '关闭线框' : '显示线框' }}</button>
<button @click="measureDistance">测量距离</button>
<button @click="toggleLabels">{{ showLabels ? '隐藏标签' : '显示标签' }}</button>
</div>
<!-- 属性面板 -->
<PropertyPanel
v-if="selectedComponent"
:properties="selectedComponent.properties"
@close="selectedComponent = null"
/>
</div>
</template>
<script>
import { ref, onMounted, onUnmounted } from 'vue'
export default {
name: 'BIMViewer',
setup() {
const viewerContainer = ref(null)
const loading = ref(true)
const wireframe = ref(false)
const showLabels = ref(true)
const selectedComponent = ref(null)
// ThreeG 相关变量
let viewer3D = null
let app = null
// 初始化 ThreeG
const initThreeG = async () => {
if (!viewerContainer.value) return
try {
// 1. 创建 ThreeG 应用配置
const webAppConfig = new THREEG.D3.Application.WebApplication3DConfig()
webAppConfig.domElement = viewerContainer.value
// 2. 创建应用实例
app = new THREEG.D3.Application.WebApplication3D(webAppConfig)
viewer3D = app.getViewer()
// 3. 基础配置
viewer3D.setOrbitControls()
viewer3D.setMaximalRangeofCamera(0.15)
viewer3D.enableTranslate(true)
// 4. 加载 BIM 模型
await loadBIMModel()
loading.value = false
} catch (error) {
console.error('ThreeG 初始化失败:', error)
loading.value = false
}
}
// 加载 BIM 模型
const loadBIMModel = () => {
return new Promise((resolve, reject) => {
// 配置加载器
const loaderConfig = new THREEGLoaderConfig()
loaderConfig.dataEnvType = "Local"
loaderConfig.sdkPath = "./jssdk"
loaderConfig.path = "./auth.json" // 认证文件
// 加载模型
THREEGLoader.load(loaderConfig,
// 成功回调
(viewMetaData) => {
if (viewMetaData.viewType === "3DView") {
viewer3D.addModel(viewMetaData)
// 监听模型添加完成事件
viewer3D.addEventListener(
THREEG.D3.Viewer.Viewer3DEvent.ViewAdded,
() => {
console.log('BIM模型加载完成')
setupInteractions()
resolve()
}
)
}
},
// 失败回调
(error) => {
console.error('BIM模型加载失败:', error)
reject(error)
}
)
})
}
// 设置交互功能
const setupInteractions = () => {
// 启用选择功能
viewer3D.enableSelect()
// 监听选择事件
viewer3D.addEventListener(
THREEG.D3.Viewer.Viewer3DEvent.MouseClicked,
(event) => {
if (event.objects.length > 0) {
const selectedObject = event.objects[0]
// 获取构件属性
const properties = selectedObject.getProperties()
selectedComponent.value = {
object: selectedObject,
properties: properties
}
}
}
)
}
// 工具栏功能
const fitToView = () => {
if (viewer3D) {
viewer3D.fitToView()
}
}
const toggleWireframe = () => {
if (viewer3D) {
wireframe.value = !wireframe.value
viewer3D.enableWireframe(wireframe.value)
}
}
const measureDistance = () => {
if (viewer3D) {
const measurer = new THREEG.D3.Plugins.Measure(viewer3D)
measurer.enableMeasureDistance()
}
}
const toggleLabels = () => {
showLabels.value = !showLabels.value
// 这里需要根据实际情况控制标签显示
}
// 生命周期
onMounted(() => {
initThreeG()
})
onUnmounted(() => {
// 清理资源
if (viewer3D) {
viewer3D.dispose()
}
})
return {
viewerContainer,
loading,
wireframe,
showLabels,
selectedComponent,
fitToView,
toggleWireframe,
measureDistance,
toggleLabels
}
}
}
</script>
<style scoped>
.bim-container {
position: relative;
width: 100%;
height: 100vh;
}
.viewer-container {
width: 100%;
height: 100%;
}
.loading-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
color: white;
z-index: 1000;
}
.toolbar {
position: absolute;
top: 20px;
left: 20px;
z-index: 100;
}
.toolbar button {
margin: 5px;
padding: 8px 16px;
background: rgba(0, 0, 0, 0.7);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.toolbar button:hover {
background: rgba(0, 0, 0, 0.9);
}
</style>
2. 属性面板组件
<template>
<div class="property-panel">
<div class="panel-header">
<h3>构件属性</h3>
<button @click="$emit('close')" class="close-btn">×</button>
</div>
<div class="panel-content">
<div v-for="(value, key) in properties" :key="key" class="property-item">
<span class="property-key">{{ key }}:</span>
<span class="property-value">{{ value }}</span>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'PropertyPanel',
props: {
properties: {
type: Object,
default: () => ({})
}
},
emits: ['close']
}
</script>
<style scoped>
.property-panel {
position: absolute;
top: 20px;
right: 20px;
width: 300px;
background: rgba(255, 255, 255, 0.95);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
z-index: 100;
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #eee;
}
.close-btn {
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: #666;
}
.panel-content {
max-height: 400px;
overflow-y: auto;
padding: 16px;
}
.property-item {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.property-key {
font-weight: bold;
color: #333;
}
.property-value {
color: #666;
text-align: right;
max-width: 150px;
word-break: break-all;
}
</style>
🚀 高级功能扩展
1. BIM 模型工具函数
// utils/bim-loader.js
export class BIMLoader {
constructor(viewer3D) {
this.viewer3D = viewer3D
this.extObjMng = new THREEG.D3.Plugins.ExternalObject.ExternalObjectManager(viewer3D)
}
// 加载 IFC 模型
async loadIFCModel(modelPath) {
return new Promise((resolve, reject) => {
const ifcLoader = new THREEG.IFCLoader()
ifcLoader.load(
modelPath,
(model) => {
this.viewer3D.add(model)
this.viewer3D.fitToView()
resolve(model)
},
(progress) => {
console.log(`加载进度: ${(progress.loaded / progress.total * 100).toFixed(2)}%`)
},
(error) => {
reject(error)
}
)
})
}
// 获取模型树结构
getModelTree(model) {
return model.getModelTree()
}
// 添加天气效果
addRainEffect() {
const rainConfig = new THREEG.D3.Plugins.WeatherEffect.RainConfig()
rainConfig.viewer = this.viewer3D
rainConfig.density = 3
return new THREEG.D3.Plugins.WeatherEffect.Rain(rainConfig)
}
// 创建剖切面
createSectionBox() {
return new THREEG.D3.Plugins.SectionBox(this.viewer3D)
}
}
2. 在 Vue 中使用工具函数
<script>
import { BIMLoader } from '@/utils/bim-loader'
export default {
// ... 其他代码
methods: {
async setupBIMTools() {
this.bimLoader = new BIMLoader(this.viewer3D)
// 加载模型
await this.bimLoader.loadIFCModel('/models/building.ifc')
// 添加交互功能
this.setupInteractions()
},
async toggleRain() {
if (!this.rainEffect) {
this.rainEffect = this.bimLoader.addRainEffect()
}
this.rainEffect.enableEffect(!this.isRaining)
this.isRaining = !this.isRaining
},
toggleSection() {
if (!this.sectionBox) {
this.sectionBox = this.bimLoader.createSectionBox()
}
this.sectionBox.setVisible(!this.sectionVisible)
this.sectionVisible = !this.sectionVisible
}
}
}
</script>
⚙️ 部署配置
Nginx 配置(确保支持 BIM 文件)
server {
listen 8080;
server_name your-domain.com;
root /www/wwwroot/your-bim-project;
# SPA 路由支持
location / {
try_files $uri $uri/ /index.html;
}
# BIM 文件支持
location ~ \.(ifc|gltf|glb|fbx)$ {
add_header Content-Type application/octet-stream;
add_header Access-Control-Allow-Origin *;
expires 30d;
}
# 压缩文件
location ~ \.gz$ {
add_header Content-Encoding gzip;
add_header Access-Control-Allow-Origin *;
expires 30d;
}
}
🔍 调试技巧
1. 控制台调试
// 在浏览器控制台中调试
window.viewer3D = viewer3D // 暴露到全局
console.log('Viewer3D 对象:', viewer3D)
2. 错误处理
// 全局错误处理
window.addEventListener('error', (event) => {
console.error('ThreeG 错误:', event.error)
})
// Promise 错误处理
window.addEventListener('unhandledrejection', (event) => {
console.error('ThreeG Promise 错误:', event.reason)
})
💡 最佳实践
- 组件化设计:将不同功能拆分为独立组件
- 错误边界:添加适当的错误处理
- 性能优化:大模型使用分块加载
- 响应式设计:适配不同屏幕尺寸
- 类型安全:使用 TypeScript 获得更好的开发体验
这样你就有了一个完整的 Vue + ThreeG + BIM 开发环境!从基础查看器到高级功能都涵盖了。
更多推荐


所有评论(0)