feat(board): 添加机场模型页面前后端交互功能

- 添加 AGV小车的参考
- 优化模型展示效果,增加标签渲染和前后端交互功能
- 修改标签不能跟随刷新、依旧重复保留的bug
boardTest
zch 8 months ago
parent 74bcc37ad5
commit 16cb7ce4de

@ -1,10 +1,14 @@
<template>
<div ref="modelContainer" class="model-container">
<div ref="modelInfo" class="model" style="display: inline-block">
DEMO展示<!-- TODO-->
<!-- FIXME:显示数据测试 -->
<div ref="modelInfo" class="model-info" style="display: inline-block" >
<!-- <el-table :data="recordSteamInstantList" >
<el-table-column label="steamFlow" align="center" prop="steamFlow" />
<el-table-column label="heatInstantValue" align="center" prop="heatInstantValue" />
<el-table-column label="fluxFlow" align="center" prop="fluxFlow" />
</el-table>-->
</div>
</div>
</template>
<script>
@ -12,10 +16,11 @@ import * as THREE from 'three';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader';
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer';// CSS2DRendererThree.jsCSS
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer';// CSS2DObjectCSSThree.js
/*import {listRecordSteamInstant} from "@/api/ems/record/recordSteamInstant";*/
export default {
name: 'AirportModel',
/**
@ -25,8 +30,17 @@ export default {
*/
mounted() {
this.initThree();
this.getPData();
},
created() {
},
methods: {
getPData() {
/* //monitorCode "E0010_0100"
listRecordSteamInstant({monitorCode: "E0010_0100"}).then(response => {
this.recordSteamInstantList = response.rows;
});*/
},
initThree() {
//
const container = this.$refs.modelContainer;
@ -41,10 +55,10 @@ export default {
// DOM
container.appendChild(renderer.domElement);
const labelRenderer = new CSS2DRenderer() // CSS2D3DHTML
// 0xffffff1
const light = new THREE.DirectionalLight(0xffffff, 1);
// 0xffffff1
const ambient = new THREE.AmbientLight(0xffffff, 1);
// 0xffffff3
const light = new THREE.DirectionalLight(0xffffff, 3);
// 0xffffff3
const ambient = new THREE.AmbientLight(0xffffff, 3);
//
light.position.set(5, 3, 5);
//
@ -52,17 +66,18 @@ export default {
scene.add(ambient);
//
labelRenderer.setSize(window.innerWidth, window.innerHeight)
labelRenderer.setSize(window.innerWidth, window.innerHeight);
//
labelRenderer.domElement.style.position = 'absolute'
labelRenderer.domElement.style.position = 'absolute';
// 0
labelRenderer.domElement.style.top = '0'
labelRenderer.domElement.style.top = '0';
//
labelRenderer.domElement.style.pointerEvents = 'none'
//
document.body.appendChild(labelRenderer.domElement);
labelRenderer.domElement.style.pointerEvents = 'none';
/* // 将标签渲染器元素添加到文档中
document.body.appendChild(labelRenderer.domElement);*/
this.$refs.modelContainer.appendChild(labelRenderer.domElement);
let css2DObject = new CSS2DObject(this.$refs.modelInfo);
//TODO:
//FIXME:
css2DObject.position.set(-430.7033258028898, 302.2550016498,-220.19044239352576);
scene.add(css2DObject);
@ -70,12 +85,15 @@ export default {
const controls = new OrbitControls(camera, renderer.domElement);
//
controls.enablePan = true;
/* //是否自动旋转
controls.autoRotate = true;*/
/* // 使动画循环使用时阻尼或自转 意思是否有惯性
controls.enableDamping = true;*/
//
controls.enableZoom = true;
/* // 使动画循环使用时阻尼或自转 意思是否有惯性
controls.enableDamping = true;*/
// OrbitControls
controls.target = new THREE.Vector3(-433, 300, -217);
// OrbitControls使
@ -118,14 +136,39 @@ export default {
};
//
animate();
},
},
data() {
return {
//
loading: true,
/* // 蒸汽实时数据表格数据
recordSteamInstantList: [],*/
}
}
},
};
</script>
<style scoped>
/* 定义一个名为model-container的类 */
.model-container {
/* 设置宽度为100% */
width: 100%;
height: calc(100vh - 84px);
/* 设置高度为视口高度减去84px */
height: calc(100vh - 34px);
}
.model-info {
/* 设置文本颜色为红色 */
color: red;
/* 设置背景颜色为蓝色 */
background-color: blue;
/* 设置内边距为10像素 */
padding: 10px;
/* 设置字体大小为16像素 */
font-size: 50px;
}
</style>

@ -0,0 +1,518 @@
/*
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
import * as THREE from 'three'
import { camera, labelRenderer, renderer, scene, isLoading } from '@/views/board/model/setThree'
import * as TWEEN from '@tweenjs/tween.js'
let timeOrSpeed = true
let isAGVLoading = false
let isAGVAnimation = {
'2AGV': false,
'3AGV': false,
'5CCAGV': false,
'5BFAGV': false,
'5CTU': false
}
let AGVanimationLine = {
'2AGV': [],
'3AGV': [],
'5CCAGV': [],
'5BFAGV': [],
'5CTU': []
}
const calculateAngle = (sideA, sideB) => {
// 计算角度
let angle = Math.atan2(sideB, sideA) * 180 / Math.PI
// 确保角度值在0到180度之间
if (angle < 0) {
angle += 180
}
return angle
}
let animationEnum = ['2AGV', '3AGV', '5CCAGV', '5BFAGV', '5CTU']
const AGVanimation = async(e) => {
try {
if (AGVanimationLine[e].length > 0) {
await AGVanimationLine[e][0]()
AGVanimationLine[e].shift()
if (AGVanimationLine[e].length > 0) {
AGVanimation(e)
} else {
isAGVAnimation[e] = false
}
}
} catch (e) {
console.log(e)
}
}
function AGVanimate() {
animationEnum.forEach(e => {
if (AGVanimationLine[e].length > 0 && isAGVAnimation[e] === false) {
isAGVAnimation[e] = true
AGVanimation(e)
}
})
requestAnimationFrame(AGVanimate)
}
AGVanimate()
let floorOrigin = {
floor2Data: {
x: 109000,
y: 119960
},
floor3Data: {
x: 39000,
y: 49460
},
floor5Data: {
x: 159000,
y: 150575
}
}
let floorData1 = {
floor2Data: {
x: 42500,
z: 20500
},
floor3Data: {
x: 42500,
z: 20500
},
floor5Data: {
x: 50300,
z: 20500
}
}
let floorData = {
floor2Data: {
x: 1314,
z: 636
},
floor3Data: {
x: 1314,
z: 636
},
floor5Data: {
x: 1314,
z: 636
}
}
let agvData = {
floor2AGVGroup: null,
floor2AGV: null,
floor2AGVData: {
x: 0,
z: 0,
rotate: 0
},
floor2AGVGroupLocation: {},
floor3AGVGroup: null,
floor3AGV: null,
floor3AGVData: {
x: 0,
z: 0,
rotate: 0
},
floor3AGVGroupLocation: {},
floor5CCAGVGroup: null,
floor5CCAGV: null,
floor5CCAGVGroupLocation: {},
floor5CCAGVData: {
x: 0,
y: 0,
rotate: 0
},
floor5BFAGVGroup: null,
floor5BFAGV: null,
floor5BFAGVGroupLocation: {},
floor5BFAGVData: {
x: 0,
y: 0,
rotate: 0
},
floor5CTUGroup: null,
floor5CTU: null,
floor5CTUGroupLocation: {},
floor5CTUData: {
x: 0,
y: 0,
rotate: 0
}
}
let agvLabel = {
'2AGV': null,
'2AGVData': null,
'3AGV': null,
'3AGVData': null,
'5CCAGV': null,
'5CCAGVData': null,
'5BFAGV': null,
'5BFAGVData': null,
'5CTU': null,
'5CTUData': null
}
let loadAGVEnum = {
'beifushiAGV005': 'floor5BFAGV',
'chacheshiAGV005': 'floor5CCAGV',
'CTU005': 'floor5CTU',
'chacheshiAGV002': 'floor2AGV',
'chacheshiAGV003': 'floor3AGV'
}
let loadAGVGroupEnum = {
'beifushiAGV005': 'floor5BFAGVGroup',
'chacheshiAGV005': 'floor5CCAGVGroup',
'CTU005': 'floor5CTUGroup',
'chacheshiAGV002': 'floor2AGVGroup',
'chacheshiAGV003': 'floor3AGVGroup'
}
let LoadAGVDataEnum = {
'beifushiAGV005': 'floor5BFAGVData',
'chacheshiAGV005': 'floor5CCAGVData',
'CTU005': 'floor5CTUData',
'chacheshiAGV002': 'floor2AGVData',
'chacheshiAGV003': 'floor3AGVData'
}
let LoadAGVLocationEnum = {
'beifushiAGV005': 'floor5BFAGVGroupLocation',
'chacheshiAGV005': 'floor5CCAGVGroupLocation',
'CTU005': 'floor5CTUGroupLocation',
'chacheshiAGV002': 'floor2AGVGroupLocation',
'chacheshiAGV003': 'floor3AGVGroupLocation'
}
let AGVLabelEnum = {
'beifushiAGV005': '5BFAGV',
'chacheshiAGV005': '5CCAGV',
'CTU005': '5CTU',
'chacheshiAGV002': '2AGV',
'chacheshiAGV003': '3AGV'
}
let axisEnum = {
'chacheshiAGV002': {
x: 9,
z: 15
},
'chacheshiAGV003': {
x: 9,
z: 20.5
},
'beifushiAGV005': {
z: 11.6
},
'chacheshiAGV005': {
x: 9,
z: 22
},
'CTU005': {
x: -3.5,
z: -20
}
}
const loadingManager = new THREE.LoadingManager()
loadingManager.onLoad = function() {
isAGVLoading = true
agvData.floor5CTUGroup.position.z = -205
agvLabel['5CTU'].position.z = -205
agvData.floor5CTUGroup.position.x = 100
agvLabel['5CTU'].position.x = -205
AGVAnimation('5CTU', '5CTU', 'x', 7)
agvData.floor5CTUGroup.rotation.y = 90 * (Math.PI / 180)
}
const loadAGV = (r) => {
r.forEach(e => {
let agvName = loadAGVEnum[e]
let agvGroupName = loadAGVGroupEnum[e]
let agvDataName = LoadAGVDataEnum[e]
let agvLocationName = LoadAGVLocationEnum[e]
let agvLabelData = AGVLabelEnum[e]
let mtlLoader = new MTLLoader()
mtlLoader.load(`/model/${e}/${e}.mtl`,
materials => {
materials.preload()
let loader = new OBJLoader(loadingManager)
loader.setMaterials(materials)
loader.load(
`/model/${e}/${e}.obj`,
object => {
object.children[0].geometry.computeBoundingBox()
let axis = new THREE.Vector3()
axis.addVectors(object.children[0].geometry.boundingBox.min, object.children[0].geometry.boundingBox.max)
axis.multiplyScalar(0.5)
axis.applyMatrix4(object.children[0].matrixWorld)
agvData[agvGroupName] = new THREE.Group()
agvData[agvGroupName].add(object)
let axisDis = axisEnum[e]
Object.keys(axisDis || {}).forEach(v => {
axis[v] += axisDis[v]
})
agvData[agvGroupName].position.set(axis.x, axis.y, axis.z)
agvLabel[agvLabelData].position.set(axis.x, axis.y, axis.z)
agvData[agvLocationName] = axis
object.position.set(-axis.x, -axis.y, -axis.z)
agvData[agvDataName].x = axis.x
agvData[agvDataName].z = axis.z
scene.add(agvData[agvGroupName])
scene.add(agvLabel[agvLabelData])
object.name = 'tishengji_tuopan'
// scene.add(object)
agvData[agvName] = object
// 更新渲染器
renderer.render(scene, camera)
},
() => {
},
err => {
loadAGV([e])
}
)
})
})
}
// loadAGV(['chacheshiAGV003'])
let AGVGroupEnum = {
'5BFAGV': 'floor5BFAGVGroupLocation',
'5CCAGV': 'floor5CCAGVGroupLocation',
'5CTU': 'floor5CTUGroupLocation',
'2AGV': 'floor2AGVGroupLocation',
'3AGV': 'floor3AGVGroupLocation'
}
let AGVDataEnum = {
'5BFAGV': 'floor5BFAGVData',
'5CCAGV': 'floor5CCAGVData',
'5CTU': 'floor5CTUData',
'2AGV': 'floor2AGVData',
'3AGV': 'floor3AGVData'
}
let groupEnum = {
'5BFAGV': 'floor5BFAGVGroup',
'5CCAGV': 'floor5CCAGVGroup',
'5CTU': 'floor5CTUGroup',
'2AGV': 'floor2AGVGroup',
'3AGV': 'floor3AGVGroup'
}
let floorEnum = {
'5BFAGV': 'floor5Data',
'5CCAGV': 'floor5Data',
'5CTU': 'floor5Data',
'2AGV': 'floor2Data',
'3AGV': 'floor3Data'
}
let rotateF = (e) => {
e %= 360
if (e > 180) {
return e - 360
}
if (e < -180) {
return e + 360
}
return e
}
const AGVAnimationF1 = (item, location, time = 2000) => {
let AGVGroupLocationData = AGVGroupEnum[item]
let AGVLocationData = AGVDataEnum[item]
let group = groupEnum[item]
let floor = floorEnum[item]
}
const AGVAnimationF = (item, type, newLocation = {}, time = 2000) => {
let s = 1
if (type === 'rotate') {
newLocation.rotate %= 360
if ((item === '2AGV' || item === '3AGV' || item === '5CCAGV') && type === 'rotate') {
newLocation.rotate -= 90
}
if ((item === '5CTU') && type === 'rotate') {
newLocation.rotate += 90
}
}
let AGVGroupLocationData = AGVGroupEnum[item]
let AGVLocationData = AGVDataEnum[item]
let group = groupEnum[item]
let floor = floorEnum[item]
let agvLabelData = agvLabel?.[item]
switch (type) {
case 'xz': {
return new Promise(async(resolve, reject) => {
let locationx = agvData[AGVGroupLocationData].x - (newLocation.x / 100) * floorData[floor].x
let locationz = agvData[AGVGroupLocationData].z + (newLocation.z / 100) * floorData[floor].z
let xlength = agvData[AGVLocationData].x - locationx
let zlength = agvData[AGVLocationData].z - locationz
let rotate = calculateAngle(Math.abs(xlength), Math.abs(zlength))
if (xlength < 0 && zlength < 0) {
rotate = 180 - rotate
}
if (xlength < 0 && zlength > 0) {
rotate = 180 + rotate
}
if (xlength > 0 && zlength > 0) {
rotate = 360 - rotate
}
if (xlength === 0 && zlength === 0) {
} else {
await AGVAnimationF(item, 'rotate', { rotate: rotate })
}
let tween = new TWEEN.Tween(agvData[group].position)
.to({ x: locationx, z: locationz }, time)
.onComplete(() => {
agvData[group].position.x = locationx
agvData[AGVLocationData].x = locationx
agvData[group].position.z = locationz
agvData[AGVLocationData].z = locationz
resolve()
tween.stop()
tween = null
})
.start()
if(agvLabelData){
let tween1 = new TWEEN.Tween(agvLabelData.position)
.to({ x: locationx, z: locationz }, time)
.onComplete(() => {
tween1.stop()
tween1 = null
})
.start()
}
})
}
case 'x': {
return new Promise(async(resolve, reject) => {
let location = agvData[AGVGroupLocationData].x - (newLocation.x / 100) * floorData[floor].x
if ((agvData[group].position.x - location) > 0) {
await AGVAnimationF(item, 'rotate', { rotate: 0 })
} else {
await AGVAnimationF(item, 'rotate', { rotate: 180 })
}
if (agvData[AGVLocationData].x === location) {
resolve()
}
let distance = location - agvData[AGVLocationData].x
let tween = new TWEEN.Tween(agvData[group].position)
.to({ x: location }, Math.abs(distance) / s * 16) // 移动到(1, 1, 1)持续1000毫秒
.onComplete(() => {
agvData[group].position.x = location
agvData[AGVLocationData].x = location
resolve()
tween.stop()
tween = null
})
.start()
if(agvLabelData){
let tween1 = new TWEEN.Tween(agvLabelData.position)
.to({ x: location }, time)
.onComplete(() => {
tween1.stop()
tween1 = null
})
.start()
}
})
}
case 'z': {
return new Promise(async(resolve, reject) => {
let location = agvData[AGVGroupLocationData].z + (newLocation.z / 100) * floorData[floor].z
if ((agvData[group].position.z - location) > 0) {
await AGVAnimationF(item, 'rotate', { rotate: 270 })
} else {
await AGVAnimationF(item, 'rotate', { rotate: 90 })
}
if (agvData[AGVLocationData].z === location) {
resolve()
}
let distance = location - agvData[AGVLocationData].z
let tween = new TWEEN.Tween(agvData[group].position)
.to({ z: location }, Math.abs(distance) / s * 16) // 移动到(1, 1, 1)持续1000毫秒
.onComplete(() => {
agvData[group].position.z = location
agvData[AGVLocationData].z = location
resolve()
tween.stop()
tween = null
})
.start()
})
}
case 'rotate': {
return new Promise((resolve, reject) => {
let nowRotate = parseFloat((agvData[group].rotation.y / (Math.PI / 180)).toFixed(2))
nowRotate = rotateF(nowRotate)
newLocation.rotate = rotateF(newLocation.rotate)
let distance = newLocation.rotate - nowRotate
distance = rotateF(distance)
let nowRotateNum = agvData[group].rotation.y
let endRotateNum = nowRotateNum + (distance * (Math.PI / 180))
let bool = (nowRotateNum - endRotateNum) > 0
let tween = new TWEEN.Tween(agvData[group].rotation)
.to({ y: newLocation.rotate * (Math.PI / 180) }, 500) // 移动到(1, 1, 1)持续1000毫秒
.onComplete(() => {
agvData[group].rotation.y = (newLocation.rotate * (Math.PI / 180))
resolve()
tween.stop()
tween = null
})
.start()
})
}
default : {
return new Promise((resolve, reject) => {
resolve()
})
}
}
}
const AGVAnimation = (e, item, type, newLocation = {}, time = 2000) => {
if (agvData[groupEnum[e]] && AGVanimationLine[e].length < 10) {
AGVanimationLine[e].push(() => AGVAnimationF(item, type, newLocation, time))
}
}
export {
agvData,
AGVAnimation,
floorOrigin,
floorData1,
isAGVLoading,
agvLabel,
loadAGV
}
*/
Loading…
Cancel
Save