效果展示:
1. 导入依赖
import * as THREE from 'three'
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader';
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls';
//props: {
// websockData: Object, //父组件传递的websocketData
//},
data() {
return {
camera: null, //相机
scene: null, //场景
renderer: null, //渲染器
container: null, //容器
controls: null, //控制器
sprites : [], //精灵、几何体、材质、贴图的集合,控制实时变化
}
},
2.创建相机、场景、光源、渲染器、控制器
//创建相机
initCamera() {
this.camera = new THREE.PerspectiveCamera(45, this.container.clientWidth / this.container.clientHeight, 0.1, 10000)
this.camera.position.set(43, 11, 22); //相机位置
},
//创建场景
initScene() {
this.scene = new THREE.Scene()
this.scene.background = new THREE.Color("#c5ccec");
this.scene.add(this.camera)
},
//光源
initLight() {
//平行光,沿着特定方向发射的光
const directionalLight1 = new THREE.DirectionalLight("#ccc", 1);
directionalLight1.position.set(1, 1, 1);
this.scene.add(directionalLight1);
const directionalLight2 = new THREE.DirectionalLight("#ccc", 1);
directionalLight2.position.set(-1, 0.5, -1);
this.scene.add(directionalLight2);
const directionalLight3 = new THREE.DirectionalLight("#ccc", 1);
directionalLight3.position.set(-23, 1, 27);
this.scene.add(directionalLight3);
const directionalLight4 = new THREE.DirectionalLight("#ccc", 1);
directionalLight4.position.set(24, 1, -24);
this.scene.add(directionalLight4);
},
//渲染器
initRenderer() {
this.container = document.getElementById('container')
this.renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
this.renderer.setPixelRatio(this.container.devicePixelRatio);
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
this.container.appendChild(this.renderer.domElement)
},
//视图控制器
initControls() {
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
//上下翻转的最大角度
this.controls.maxPolarAngle = 1.5;
//上下翻转的最小角度
this.controls.minPolarAngle = 0.3;
//是否允许缩放
this.controls.enableZoom = true;
//控制是否带有惯性
this.controls.enableDamping = true
},
3.导入GLTF模型
//加载设备模型
initGLTF(item) {
let that = this
function onProgress(xhr) {
if (xhr.lengthComputable) {
const percentComplete = xhr.loaded / xhr.total * 100;
// ${Math.round(percentComplete, 2)}% //加载进度
}
}
function onError() {}
//导入模型
new GLTFLoader().setPath('static/obj/').load(item + '.gltf', function (object) {
object.scene.position.x = 0;
object.scene.position.z = 0;
object.scene.name = item; //模型名称
object.scene.scale.set(0.1, 0.1, 0.1) //设置模型比例尺寸
object.scene.traverse(function (child) {
if (child.isMesh) {
//给模型下的Mesh添加材质颜色
child.material = new THREE.MeshPhongMaterial({
color: child.material.color,
shininess: 100,
reflectivity: 0.5,
flatShading: false
})
}
});
//给模型内的Mesh集合加到全局
that.sprites = []
object.scene.children.forEach(child => {
that.sprites.push({
mesh: child,
sprite: null,
texture: null,
spriteMaterial: null,
})
});
that.scene.add(object.scene);
}, onProgress, onError);
}
4.在animate中实时显示Sprite
animate() {
this.renderer.render(this.scene, this.camera);
this.animateId = requestAnimationFrame(this.animate);
//先清空精灵
this.scene.children.forEach(child =>{
if(child.type === "Sprite"){
this.scene.remove(child);
}
})
this.sprites.forEach(item => {
if (item.sprite) { //注意加载前要把加载过的精灵销毁掉,释放内存
this.scene.remove(item.sprite)
this.scene.remove(item.texture)
this.scene.remove(item.spriteMaterial)
}
//绘制矩形
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
//canvas 实体
context.fillStyle = "rgba(0,0,0,0.5)"; //填充带透明颜色
context.fillRect(0,0,600,200); //x,y,width,height
//canvas 边框
context.lineJoin = "round";
context.lineCap = 'round';
context.miterLimit = 0;
context.lineWidth = 5; //borderWidth
context.strokeStyle = "#17fe6d";
context.strokeRect(0,0,300,150);
//canvas 文字
context.fillStyle = "#fff";
context.font = "24px bold Arial";
context.fillText(item.mesh.name, 30, 30);
context.fillStyle = "#fff";
context.font = "20px bold Arial";
context.fillText("websocket值:" + this.websockData['socketKey'], 30, 60);
item.texture = new THREE.Texture(canvas); //就是上面的canvas,将它写在一个函数中然后返回。
item.texture.needsUpdate = true;
item.spriteMaterial = new THREE.SpriteMaterial({map: item.texture});
item.sprite = new THREE.Sprite(item.spriteMaterial);
item.sprite.scale.set(2.5, 2.5, 2.5); //大小缩放
item.sprite.position.set(item.mesh.position.x / 10, (item.mesh.position.y + 40) / 10 + 4, item.mesh.position.z / 10); //位置
item.sprite.name = "标识图"
this.scene.add(item.sprite); //将其放入场景中
})
},
5.获取websocket数据
initWebSocket() {
//初始化weosocket
// this.websock = new WebSocket('ws://' + location.host + '/socket');
this.websock = new WebSocket("ws://192.168.2.9:5002/socket");
this.websock.onmessage = this.websocketonmessage;
this.websock.onopen = this.websocketonopen;
this.websock.onerror = this.websocketonerror;
this.websock.onclose = this.websocketclose;
},
websocketonmessage(e) {
this.websockVarData = JSON.parse(e.data);
},
websocketonopen() {
console.log("socket连接成功")
},
websocketonerror(){
console.log("连接错误")
},
websocketsend(Data) {
this.websock.send(Data);
},
websocketclose(e) {
console.log("websocket关闭");
},
closesocket() {
this.websock.close();
},
也可以在父组件调用,传递websockVarData值当前组件获取,数据也是实时的,不需要用watch监听值,在animate方法中是自动更新的
6.最后初始化方法
init() {
this.initRenderer()
this.initCamera()
this.initScene()
this.initLight()
this.initGLTF("xx模型")
this.initControls()
},
mounted() {
this.initWebSocket()
this.init()
this.animate()
},