在前几篇文章,已经介绍了Threejs的基本使用,本文将讲解如何在Vue中加入Threejs以及OBJ、MTL加载。

先上效果:

文档结构(注意在vue3之前的版本,obj等资源要放在static下面):

其实在vue中使用threejs更加简单,唯一要注意的就是版本问题,我以前用的84版这一次引用的是最新的106版,在相机的处理上感觉与旧版本有区别。

步骤:

npm cnpm vue相关环境不再此处赘言。

1.安装threejs

cnpm install three -s -d

2.安装obj mtl加载

cnpm install three-obj-mtl-loader -s -d

3.前端代码

<template>
  <div style="height: 100%">
    <div>
      <button @click="triggerTips('m1')">m1</button>
      <button @click="triggerTips('m2')">m2</button>
      <button @click="triggerTips('m3')">m3</button>
    </div>
    <div id="container">

    </div>
  </div>

</template>

<script>
  import * as Three from 'three';
  import {OBJLoader, MTLLoader} from 'three-obj-mtl-loader'

  export default {
    data() {
      return {
        camera: null,
        scene: null,
        renderer: null,
        light: null,
        elements: [],
        tipsBottom: 5 + 200,
        tipsTop:5 + 200 + 40
      }
    },
    mounted() {
      this.initThree(document.getElementById('container'))
      this.animation();

    },
    methods: {
      createSphere(color) {
        //创建球体
        let radius = 10, segemnt = 16, rings = 16;
        let sphereMaterial = new Three.MeshLambertMaterial({
          color: color,
          transparent: true,
          opacity: 0.8
        });
        let sphere = new Three.Mesh(
          new Three.SphereGeometry(radius, segemnt, rings),
          sphereMaterial
        );
        sphere.geometry.verticesNeedUpdate = true;
        sphere.geometry.normalsNeedUpdate = true;
        return sphere;
      },
      triggerTips(id) {
        let element = this.elements.find(x => {
          if (x.id == id) {
            return x;
          }
        })
        if (!element) {
          console.log("不存在该id");
          return;
        }
        if (element.timeKey) {
          clearInterval(element.timeKey);
          element.timeKey = undefined;
          element.model.children.forEach(o => {
            for (let m in element.materials.materials) {
              if (o.name.indexOf(m) != -1) {
                o.material = element.materials.materials[m];
              }
            }

          })
          element.cylis.forEach(x => {
            this.scene.remove(x.cyli);
          })
          element.cylis.splice(0, element.cylis.length);
        } else {
          this.crateTips(element);
          element.timeIndex = 0;
          element.timeKey = setInterval(() => {
            element.model.children.forEach(o => {
              if (element.timeIndex == 0) {
                o.material = new Three.MeshBasicMaterial({color: 0xFF0000});
              } else {
                for (let m in element.materials.materials) {
                  if (o.name.indexOf(m) != -1) {
                    o.material = element.materials.materials[m];
                  }
                }
              }
            })
            element.timeIndex = ++element.timeIndex % 2;
          }, 500);
        }
      },
      crateTips(element) {
        element.cylis = [];
        let cyliMesh01 = this.createSphere(0xFF0000);
        cyliMesh01.position.set(element.model.position.x, this.tipsBottom, element.model.position.z);
        this.scene.add(cyliMesh01);
        element.cylis.push({cyli: cyliMesh01});

        let cyliMesh02 = this.createSphere(0xFFFF00);
        cyliMesh02.position.set(element.model.position.x, this.tipsTop, element.model.position.z);
        this.scene.add(cyliMesh02);
        element.cylis.push({cyli: cyliMesh02});
      },
      onResize() {
        this.camera.aspect = window.innerWidth / window.innerHeight;
        this.camera.updateProjectionMatrix();
        this.renderer.setSize(window.innerWidth, window.innerHeight);
      },
      animation() {
        this.elements.forEach(x => {
          x.cylis.forEach(c => {
            let cyl = c.cyli;
            if (cyl.position.y >= this.tipsTop) {
              c.direction = -1;
            } else if (cyl.position.y <=this.tipsBottom) {
              c.direction = 1;
            }

            cyl.position.setY(cyl.position.y + c.direction * 1.2);
          })

        })

        //刷新页面
        this.renderer.render(this.scene, this.camera);
        requestAnimationFrame(this.animation);
      },
      loadObj(id, baseUrl, objName, multiplyScalar, position, materials) {
        let obj = {id: id, isTips: false,cylis:[]};
        if (!multiplyScalar) {
          multiplyScalar = 1;
        }
        let manager = new Three.LoadingManager();
        let loader = new OBJLoader(manager);
        if (multiplyScalar) {
          loader.setMaterials(materials);
          obj.materials = materials;
        }
        loader.setPath(baseUrl);
        loader.load(objName, object => {
          //显示比例
          object.scale.multiplyScalar(multiplyScalar);
          //加入到页面中
          this.scene.add(object);
          object.castShadow = true;
          object.receiveShadow = true;
          obj.model = object;
          object.position.set(position.x, position.y, position.z);
          this.elements.push(obj);
        });
      },
      loadObjWithMaterials(id, baseUrl, objName, mtlName, position, multiplyScalar) {
        let mtlLoader = new MTLLoader();
        mtlLoader.setPath(baseUrl);
        mtlLoader.load(mtlName, materials => {
          this.loadObj(id, baseUrl, objName, multiplyScalar, position, materials);
        });
      },
      initThree(element) {
        let container = element;
        //初始化相机
        this.camera = new Three.PerspectiveCamera(70, element.clientWidth / element.clientHeight, 1, 10000);
        this.camera.position.x = 0;
        this.camera.position.y = 1000;
        this.camera.position.z = 1000;
        this.camera.rotation.x -= 45 * (Math.PI / 180);
        //场景
        this.scene = new Three.Scene();
        //渲染
        this.renderer = new Three.WebGLRenderer({antialias: true});
        this.renderer.setSize(element.clientWidth, element.clientHeight);
        element.appendChild(this.renderer.domElement);
        //灯光
        this.light = new Three.AmbientLight(0xFFFFFF);
        this.scene.add(this.light);

        //创建地面
        let plane = new Three.Mesh(
          new Three.PlaneBufferGeometry(2000, 2000),
          new Three.MeshPhongMaterial({
            color: 0x999999,
            specular: 0x101010
          })
        );
        plane.rotation.x = -Math.PI / 2;
        plane.position.y = -0.5;
        this.scene.add(plane);
        plane.receiveShadow = true;

        window.addEventListener('resize', this.onResize, false);
        this.loadObjWithMaterials("m1", 'static/model/female02/', 'female02.obj', 'female02.mtl', {
          x: 0,
          y: 0,
          z: 0
        }, 1,)
        this.loadObjWithMaterials("m2", 'static/model/female02/', 'female02.obj', 'female02.mtl', {
          x: 500,
          y: 0,
          z: 0
        }, 1,)
        this.loadObjWithMaterials("m3", 'static/model/female02/', 'female02.obj', 'female02.mtl', {
          x: 0,
          y: 0,
          z: 600
        }, 1,)
      }
    }
  }
</script>

<style>
  #container {
    height: 100%;
  }
</style>

最后说一句webstrom写前端是真的爽,不管是vue单页面,还是threejs都很流畅,提示也很到位。

Logo

前往低代码交流专区

更多推荐