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>

  

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM