<template> <div class="overview"> <div class="top"> <div class="block" v-for="(value,key,index) in data" :key="index" @click="btnClick(value)"> <image-block :data="value"/> </div> </div> <div ref="body" class="body"> <left ref="left" class="left"/> <div ref="right" class="middle"> <div class="title">{{active}}</div> <div class="angle">横滚角: {{roll}} <br/>俯仰角: {{pitch}} <br/>航向角: {{heading}} </div> <div ref="device"/> </div> <right ref="devright" class="right"/> </div> </div> </template> <script> import { getRobotList,getRobotDetail,getRobotTask } from '@/api/robot' import ImageBlock from "./components/imageBlock"; import Left from './left' import * as THREE from 'three' import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' import { STLLoader } from 'three/examples/jsm/loaders/STLLoader' import OrbitControls from 'three-orbitcontrols' import dat from 'dat.gui' import Right from "./right"; var refresh = false export default { name: 'Device', components: {Right, Left,ImageBlock, THREE, dat,OrbitControls,GLTFLoader,STLLoader}, data() { return { active: '飞翼滑翔机一号', roll: '45.21°', pitch: '51.41°', heading: '93.00°', arr:[], controls: null, scene: null, gltf: null, camera: null, renderer: null, plane:null, paramsThree: { modelName: '', // .glb模型的文件名字 name: 'devGlb', // 模型加入场景的名字,此处统一是devGlb }, data:[ // {name:'飞翼滑翔机一号',gltf:''}, // {name:'飞翼滑翔机二号',gltf:''}, // {name:'飞翼滑翔机三号',gltf:''}, // {name:'飞翼滑翔机四号',gltf:''}, // {name:'飞翼滑翔机五号',gltf:''}, // {name:'飞翼滑翔机六号',gltf:''}, // {name:'飞翼滑翔机七号',gltf:''}, // {name:'飞翼滑翔机八号',gltf:''} ] } }, created() { this.$nextTick(() => { this.init() }) }, watch:{ 'camera.rotation.x':{ handler: function() { this.pitch = (this.camera.rotation.x/Math.PI*180+this.arr[1]).toFixed(2) + '°' this.roll = (this.camera.rotation.y/Math.PI*180+this.arr[0]).toFixed(2) + '°' this.heading = (this.camera.rotation.z/Math.PI*180+this.arr[2]).toFixed(2) + '°' } } }, methods: { btnClick(val){ this.active = val.name getRobotTask(val.id).then(res => { if (res.code === 200) { this.$refs.left.initTask(res.data) } }) getRobotDetail(val.id).then(res => { if (res.code === 200) { this.$refs.left.init(res.data) this.$refs.devright.init(res.data) this.roll = res.data.rollAngle + '°' this.pitch = res.data.pitchAngle + '°' this.heading = res.data.headingAngle + '°' this.arr = [res.data.rollAngle,res.data.pitchAngle,res.data.headingAngle] this.initModel(val.gltf,res.data.rollAngle,res.data.pitchAngle,res.data.headingAngle) } }) }, init () { this.initList() this.initMesh() }, initModel(name,roll,pitch,heading){ let that = this this.gltf = null if(this.scene&&this.paramsThree.modelName!==''){ let objM = this.scene.getObjectByName(this.paramsThree.modelName) if (objM) this.scene.remove(objM) refresh = true } if(name==='') return const loader = new STLLoader(); loader.load(`${process.env.BASE_URL}model/${name}.stl`, function ( geometry ) { const material = new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0x111111, shininess: 200 } ); const mesh = new THREE.Mesh( geometry, material ); mesh.name = name mesh.position.set( 0,0,0); mesh.rotation.set( pitch*Math.PI/180, roll*Math.PI/180, heading*Math.PI/180 ); mesh.scale.set( 0.03, 0.03, 0.03 ); mesh.castShadow = true; mesh.receiveShadow = true; that.scene.add( mesh ); that.paramsThree.modelName = name that.gltf = mesh } ); }, addShadowedLight( x, y, z, color, intensity ) { const directionalLight = new THREE.DirectionalLight( color, intensity ); directionalLight.position.set( x, y, z ); this.scene.add( directionalLight ); directionalLight.castShadow = true; const d = 1; directionalLight.shadow.camera.left = - d; directionalLight.shadow.camera.right = d; directionalLight.shadow.camera.top = d; directionalLight.shadow.camera.bottom = - d; directionalLight.shadow.camera.near = 1; directionalLight.shadow.camera.far = 4; directionalLight.shadow.bias = - 0.002; }, initList(){ getRobotList().then(res => { if (res.code === 200) { this.data = res.data.map(item => { return {name:'飞翼滑翔机'+item.robotId+'号',id:item.robotId,gltf:item.modelType} }) this.btnClick(this.data[0]) } }) }, initMesh () { this.scene = new THREE.Scene() // 场景 this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000) // 相机.视场,长宽比,近面,远面 this.camera.position.set( 0, 0, 30 ); this.camera.lookAt(this.scene.position) this.renderer = new THREE.WebGLRenderer({ antialias: true })// 渲染器 this.renderer.setClearColor(0x179AC6, 1); //设置背景颜色 this.renderer.setSize(this.$refs.right.clientWidth,this.$refs.right.clientHeight) this.renderer.shadowMapEnabled = true // 开启阴影 this.scene.add( new THREE.HemisphereLight( 0x443333, 0x111122 ) ); this.addShadowedLight( 1, 1, 1, 0xffffff, 1.35 ); this.addShadowedLight( 0.5, 1, - 1, 0xffffff, 1 ); this.$refs.device.append(this.renderer.domElement) this.renderer.render(this.scene, this.camera) this.controls = new OrbitControls(this.camera, this.renderer.domElement); //摄像机和容器div 注意事div this.controls.mouseButtons = { //左键平移 LEFT: THREE.MOUSE.PAN, //滚轮滑动 MIDDLE: THREE.MOUSE.DOLLY, //右键旋转 RIGHT: THREE.MOUSE.ROTATE } // 使动画循环使用时阻尼或自转 意思是否有惯性 this.controls.enableDamping = false; //是否可以缩放 this.controls.enableZoom = true; //是否自动旋转 this.controls.autoRotate = true; // //设置相机距离原点的最远距离 // this.controls.minDistance = 200; //设置相机距离原点的最远距离 this.controls.maxDistance = 600; //是否开启右键拖拽 this.controls.enablePan = true; this.renderScene() }, renderScene () { let {scene, camera, renderer} = this requestAnimationFrame(this.renderScene) if(refresh){ refresh = false camera.position.set( 0, 0, 30 ) camera.lookAt(scene.position) } renderer.render(scene, camera) } } } </script> <style rel="stylesheet/scss" lang="scss" scoped> .overview{ display: flex; flex-direction: column; padding-right: 30px; width: 100%; height: 100%; .title{ color: #ffffff; font-size: 20px; font-weight: bold; position: absolute; top: 30px; left: 360px; } .angle{ color: #2f3244; font-size: 17px; position: absolute; top: 80px; left: 360px; line-height: 25px; } .top{ margin: 10px 30px; display: flex; flex-direction: row; .block{ width: 12.5%; height: 30px; margin-right: 10px; } } .body{ position:relative; width: 100%; flex: 1; display: flex; flex-direction: row; .left{ margin-left: 20px; min-width: 300px; width: 300px; display: flex; flex-direction: column; padding-bottom: 20px; } .right{ margin-left: 20px; margin-right: 10px; min-width: 200px; width: 200px; display: flex; flex-direction: column; padding-bottom: 20px; } .middle{ flex: 1; /*background-color: #E6A23C;*/ margin-left: 20px; margin-top: 5px; margin-bottom: 25px; } } .map{ width: 100%; flex: 1; background-color: white; } } </style>