Threejs项目实战之二:产品三维爆炸图效果展示

时间:2025-04-08 08:20:28
  • 在的template模板中添加一个div,id设置为scene,作为承载Threejs的容器;再增加一个div,设置class=“control”,在这个div中添加两个button,并给两个button添加点击事件,用于控制产品模型的分解与组合
    template模板中代码如下:
    <template>
      <div > 
      </div>
      <div class="control">
        <button @click="split(true)">分解</button>
        <button @click="split(false)">组合</button>
      </div>  
    </template>
    
  • 设置中元素的样式,默认情况想,我们上面设置的id为scene的div和class为control的div是一列排列的,我们需要将control中的两个button设置为页面右上角的位置,在style代码片段中设置类名为control的样式和button的样式代码如下
    <style scoped>
    .control{
      display: flex;
      position: fixed;
      top:50px;
      right: 50px;
    }
    .control button {
      width: 100px;
      height: 30px;
      margin: 10px;
    }
    </style>
    
  • 在script标签中引入threejs
    import * as THREE from 'three'
  • 这里我们选择的产品模型是gltf格式的文件,因此,我们需要引入threejs为我们提供的GLTFLoader加载器
    import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
  • 由于我们需要对物体进行鼠标旋转缩放控制,因此我们需要引入threejs为我们提供的OrbitControls控制器
    import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
  • 引入GSAP库,实现产品分解、组合时的动画效果
    import { gsap } from 'gsap'
  • 引入vue的生命周期onMounted
    import { onMounted } from 'vue'
  • 创建init函数,用于初始化threejs相关设置
    const init = () => {}
  • 为了便于后期代码的维护,我这里将创建threejs场景、相机、灯光、渲染器及控制器等各个部分进行了分别的封装,这样便于后期的代码维护与修改。
  • 首先设置全局变量scene, camera, loader, renderer, controls和model
    let scene, camera, loader, renderer, controls, model
  • 创建初始化场景函数,在初始化场景函数中,调用new ()方法初始化场景,具体代码如下
    // 初始化场景
    const initScene = () => {
      scene = new ()
       = new (0xcccccc)
       = new (0xcccccc)
    }
    
  • 创建初始化相机函数,在初始化相机函数中,调用 new ()方法创建相机并设置相机的位置,具体代码如下:
    // 添加相机
    const initCamera = () => {
      camera = new (75,  / , 0.01, 1)
      (0.08, 0.08, 0.15)
    }
    
  • 创建初始化灯光函数,在初始化灯光函数中,添加环境光、自然光、聚光灯等各种灯光效果,代码如下:
    // 添加灯光
    const initLight = () => {
      // 设置环境光
      (new (0xffffff, 0.5))
    
      // 添加球光源
      const hesLight = new (0xffffff, 0x444444)
       = 0.6
      (hesLight)
      // 自然光
      const dirLight = new ()
      (0, 0, 15)
      (dirLight)
      const dirLight2 = new ()
      (0, 0, -15)
      (dirLight2)
      const dirLight3 = new ()
      (15, 0, 0)
      (dirLight3)
      const dirLight4 = new ()
      (-15, 0, 0)
      (dirLight4)
      const dirLight5 = new ()
      (0, 15, 0)
      (dirLight5)
      const dirLight6 = new ()
      (0, -15, 0)
      (dirLight6)
      const dirLight7 = new ()
      (5, 15, 5)
      (dirLight7)
      const dirLight8 = new ()
      (-5, -15, -5)
      (dirLight8)
      // 聚光灯
      const sportLight = new (0xffffff, 0.8)
       =  / 8; //散射角度,跟水平线的夹角
       = 0.1;  // 聚光锥的半影衰减百分比
       = 2; // 纵向:沿着光照距离的衰减量。
       = 10;
       = 10;
      // 阴影映射宽度,阴影映射高度 
      (512, 512);
      (0, 15, 0);
      // 光照射的方向
      (0, 0, 0);
       = true;
      (sportLight);
    }
    
  • 创建加载GLTF模型函数,使用Threejs提供的new GLTFLoader()方法加载gltf模型文件,具体代码如下:
    // 加载GLTF模型
    const initModel = () => {
      loader = new GLTFLoader()
      ('/model/', gltf => {
        model =  
        (0, -0.08, 0)
        // 添加模型到场景
        (model)
      })
    }
    
  • 创建初始化渲染器函数,使用Threejs提供的new ()方法创建渲染器,并设置相关参数,具体diam如下:
    // 创建渲染器
    	const initRenderer = () => {
    	  renderer = new ({ antialias: true })
    	  (, )
    	  ('scene').appendChild()
    	}
    
  • 创建初始化控制器函数,添加控制器,使用Threejs提供的new OrbitControls()方法创建一个控制器,并设置相关参数,具体代码如下:
    // 添加控制器
    const initControl = () => {
      controls = new OrbitControls(camera, )
       = true
       = 0.25
       = true
    }
    
  • 创建循环渲染动画,通过调用requestAnimationFrame(animate) 循环调用该动画,并使用(scene, camera)实时渲染场景,具体代码如下:
    // 渲染循环
    const playAnimate = () => {
      const animate = function () {
        requestAnimationFrame(animate) 
        (scene, camera)
      }
      animate()
    }
    
  • 将上面创建的各个初始化函数添加到init函数中,使其在初始化时分别调用各个函数,init函数具体代码如下:
    const init = () => { 
      initScene()
      initCamera()
      initLight()
      initModel()
      initRenderer()
      initControl()
      playAnimate()
    }
    
  • 在vue生命周期onMounted中调用init函数,完成页面渲染,代码如下:
    onMounted(() => {
      init()
    })
    
  • 添加分解、组合控制动画,在template模板中,我们已经给两个按钮绑定了鼠标点击事件split(),它接收一个boolean类型的参数,为true时,我们对模型进行分解操作,为false时,我们对模型进行组合操作。我这里实现模型分解与组合的方法是获取模型中的Mesh数组,通过forEach循环遍历获取需要移动位置的Mesh,修改其相关的Position来移动Mesh的位置,这里使用了gsap动画来实现动画效果,具体代码如下:
    const split = (val) => {
      const myModel = [0].children
      if(val) { 
        (item => {
          if( === "Obj3d66-5355088-2-382") {
            //  = 0.03
            (,{
              duration:1,//动画持续时间
              y:0.03,//目标位置
              ease:''
            })
          } else if ( === "Obj3d66-5355088-2-382_1"){
            //  = -0.05
            (,{
              duration:1,//动画持续时间
              y:-0.05,//目标位置
              ease:''
            })
          }
        });
      } else {
        (item => {
          if( === "Obj3d66-5355088-2-382") {
            //  = 0.03
            (,{
              duration:1,//动画持续时间
              y:0,//目标位置
              ease:'line'
            })
          } else if ( === "Obj3d66-5355088-2-382_1"){
            //  = -0.05
            (,{
              duration:1,//动画持续时间
              y:0,//目标位置
              ease:'line'
            })
          }
        });
      }
    }