vue项目中使用three.js添加3D模型,模型加载做加载中动画以及模型作缓存、鼠标点击交互拿到模型对象
一、效果预览


二、代码页面容器
<template>
<div class="main-page">
<div class="center">
<div class="video-show module">
<div class="mx">
<!-- <iframe class="iframehtml" v-if="url" :src="url" frameborder="0" scrolling="no"></iframe> -->
<!-- 遮罩层 -->
<div class="modelMask" v-if="percentageBool">
<div class="marg">
<p>loading 3D model</p>
<div class="elPro">
<el-progress :stroke-width="6" :show-text="false" :percentage="percentage"></el-progress>
</div>
</div>
</div>
<!-- 模型存放区域 -->
<div id="threeContained"></div>
</div>
</div>
</div>
</div>
</template>
意:id为threeContained的div就是模型最终渲染的区域,所以在模型加载容器的时候注意,需要使用这个div的宽高,不是windows.innerwidth
三、鼠标交互事件(点击模型中的摄像头,获取摄像头的id)
// 鼠标点击事件
selectObject(event){
let container = document.getElementById('threeContained');
var mouse = new THREE.Vector2();
var raycaster = new THREE.Raycaster();
let getBoundingClientRect = container.getBoundingClientRect()
let x = ((event.clientX - getBoundingClientRect.left) / container.offsetWidth) * 2 - 1;// 标准设备横坐标
let y = -((event.clientY - getBoundingClientRect.top) / container.offsetHeight) * 2 + 1;// 标准设备纵坐标
let standardVector = new THREE.Vector3(x, y, 1);// 标准设备坐标
// 标准设备坐标转世界坐标
let worldVector = standardVector.unproject(this.camera);
// 射线投射方向单位向量(worldVector坐标减相机位置坐标)
let ray = worldVector.sub(this.camera.position).normalize();
// 创建射线投射器对象
let rayCaster = new THREE.Raycaster(this.camera.position, ray);
// 返回射线选中的对象 第二个参数如果不填 默认是false
let intersected = rayCaster.intersectObjects(this.scene.children, true);
if (intersected.length) {
const found = intersected[0];
if(found.object.name == "354713923249152"){
this.$router.push({
path:'/home/monitoring/monitoringVideo',
query:{
id:found.object.name
}
})
}
}
}
四、全部代码贴图
<script>
import { videoList } from "@/api/mainpage";
import * as THREE from "three"; //引入Threejs
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import Stats from "three/examples/jsm/libs/stats.module";
// import rtsp2webrtc from '@/components/rtsp2webrtc'
export default {
name: "MainPage",
components: {
// rtsp2webrtc,
},
mounted() {
if(!localStorage.getItem('Admin-Token')){
this.$router.push('/');
return;
}
// 测试地址
// this.$refs.video.connentStream(true, 'rtsp://admin:leinao123@192.168.8.220')
// this.init();
this.clock = new THREE.Clock();
this.init();
// this.animate();
window.onpointerdown = this.selectObject;
},
data() {
return {
videoList: [],
curIndex: 0,
url: "https://realsee.com/ke/BEy832qG8mQDNnOe/1mlg5kWnP4kTkhxh1TaxzDMUG8wYBe4V/#lianjia",
// 模型信息
scene: "",
light: "",
camera: "",
controls: "",
renderer: "",
load: "",
clock: "",
mixer: "",
percentage:0,//进度条数据
percentageBool:true,
meshChildren:[],
};
},
methods: {
// 模型函数开始
init() {
var that = this;
// 加缓存,避免模型再次请求
// THREE.Cache.enabled = true;
var container = document.getElementById("threeContained");
// 创建场景
that.scene = new THREE.Scene();
// that.scene.background = new THREE.Color(0x8cc7de);
that.scene.background = new THREE.Color("#080e30");
// 创建相机
that.camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
10000
);
// that.camera.position.set( -70, 25, 90 );
// 定位相机,并且指向场景中心(这里设置的值我是根据模型尺寸来写的,其实应该是动态获取模型长宽高后设置相机位置,根据自己模型大小来写)
that.camera.position.x = 800;
that.camera.position.y = 520;
that.camera.position.z = 3000;
//设置z轴朝上
// that.camera.up.x = 0;
// that.camera.up.y = 0;
// that.camera.up.z = 1;
that.camera.lookAt(that.scene.position);
// 显示三维坐标系
// var axes = new THREE.AxesHelper(100);
// // 添加坐标系到场景中:红色是X轴绿色是y轴蓝色是z轴
// that.scene.add(axes);
// // 创建地面的几何体
// var planeGeometry = new THREE.PlaneGeometry(800, 1000);
// // 给地面物体上色
// var planeMaterial = new THREE.MeshStandardMaterial({ color: 0xcccccc });
// // 创建地面
// var plane = new THREE.Mesh(planeGeometry, planeMaterial);
// plane.material.opacity = 0.6;
// plane.material.transparent = true;
// plane.rotation.x = -0.5 * Math.PI;
// plane.position.x = 0;
// plane.position.y = 0;
// plane.position.z = 0;
// plane.castShadow = true;
// // 接收阴影
// plane.receiveShadow = true;
// that.scene.add(plane);
// 灯光
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
hemiLight.position.set(0, 1, 0);
that.scene.add(hemiLight);
const directionalLight1 = new THREE.DirectionalLight(0xffeeff, 0.8);
directionalLight1.position.set(1, 1, 1);
that.scene.add(directionalLight1);
const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight2.position.set(-1, 0.5, -1);
that.scene.add(directionalLight2);
const ambientLight = new THREE.AmbientLight(0xffffee, 0.25);
that.scene.add(ambientLight);
// stats消耗资源展示
// that.stats = new Stats();
// container.appendChild(that.stats.dom);
// 材质
// const normal = new THREE.TextureLoader().load(
// "models/shanghai/textures/shanghai.jpg"
// );
// model
that.loader = new FBXLoader();
// that.loader.load("/models/SHIWAI.FBX",function (geometry) {
// that.loader.load("https://img.cnbita.com/meshes.fbx",function (geometry) {
// that.loader.load("https://img.cnbita.com/fbx/BDZ.FBX", function (geometry) {
that.loader.load("/models/fbx/BDZ.FBX", function (geometry) {
// that.loader.load("https://img.cnbita.com/fbx/newMeshes.fbx", function (geometry) {
that.percentageBool = false;
// geometry.scale.set(0.04, 0.04, 0.04);
geometry.scale.set(0.1,0.1,0.1);
geometry.position.set(0, 36, 0);
that.scene.add(geometry);
that.animate();
},
// onProgress回调
function ( xhr ) {
that.percentage = Math.floor(xhr.loaded / xhr.total * 100);
that.$forceUpdate();
// console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
},
// onError回调
function ( err ) {
console.error( 'An error happened' );
}
);
// 创建渲染器
that.renderer = new THREE.WebGLRenderer({
antialias: true,
logarithmicDepthBuffer: true,
});
that.renderer.setPixelRatio(window.devicePixelRatio);
// 设置渲染器的初始颜色
that.renderer.setClearColor(new THREE.Color(0xeeeeee));
// 设置输出canvas画面的大小
// that.renderer.setSize(window.innerWidth, window.innerHeight);
that.renderer.setSize(container.clientWidth,container.clientHeight);
container.appendChild(that.renderer.domElement);
// 控制模型的旋转
const controls = new OrbitControls(that.camera, that.renderer.domElement);
controls.target.set(0, 12, 0);
controls.update();
window.addEventListener("resize", that.onWindowResize);
},
// 窗口缩放
onWindowResize() {
var container = document.getElementById("threeContained");
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(container.clientWidth,container.clientHeight);
},
// 动画
animate() {
// requestAnimationFrame(this.animate);
// this.renderer.render(this.scene, this.camera);
// this.stats.update();
requestAnimationFrame(this.animate);
const delta = this.clock.getDelta();
if (this.mixer) this.mixer.update(delta);
this.renderer.render(this.scene, this.camera);
// this.stats.update();
},
// 鼠标点击事件
selectObject(event){
if(this.percentageBool){
return;
}
let container = document.getElementById('threeContained');
if(!container){
return;
}
var mouse = new THREE.Vector2();
var raycaster = new THREE.Raycaster();
let getBoundingClientRect = container.getBoundingClientRect()
let x = ((event.clientX - getBoundingClientRect.left) / container.offsetWidth) * 2 - 1;// 标准设备横坐标
let y = -((event.clientY - getBoundingClientRect.top) / container.offsetHeight) * 2 + 1;// 标准设备纵坐标
let standardVector = new THREE.Vector3(x, y, 1);// 标准设备坐标
// 标准设备坐标转世界坐标
let worldVector = standardVector.unproject(this.camera);
// 射线投射方向单位向量(worldVector坐标减相机位置坐标)
let ray = worldVector.sub(this.camera.position).normalize();
// 创建射线投射器对象
let rayCaster = new THREE.Raycaster(this.camera.position, ray);
// 返回射线选中的对象 第二个参数如果不填 默认是false
let intersected = rayCaster.intersectObjects(this.scene.children, true);
if (intersected.length) {
const found = intersected[0];
if(found.object.name == "354713923249152"){
this.$router.push({
path:'/home/monitoring/monitoringVideo',
query:{
id:found.object.name
}
})
}
}
}
},
};
</script>
<style scoped lang="scss">
@import "style/index.scss";
#threeContained {
width: 100%;
height: 100%;
}
.iframehtml {
width: 100%;
height: 100%;
}
.mx {
width: 97.5%;
height: 96%;
margin: 0 auto;
border-radius: 6.5% 6% 6% 6%;
position: absolute;
left: 1.25%;
top: 2%;
overflow: hidden;
// bottom: 5%;
// right: 4%;
.modelMask {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
// background: #080e30;
// background: rgba($color: #080e30, $alpha: 0.85);
background: url('~@/assets/images/modelBg.png') no-repeat center center;
background-size: 100% 100%;
z-index: 9;
display: flex;
.marg {
width: 300px;
height: 60px;
margin: auto;
text-align: center;
p {
color: #e2e2e2;
font-size: 13px;
}
.elPro {
width: 100%;
margin-top: 10px;
}
}
}
}
</style>

浙公网安备 33010602011771号