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

最终效果

今天我们来实现如何使用ThreeJS实现产品的三维爆炸图分解与组合的效果,先看下最终项目完成后的效果展示动画
在这里插入图片描述

1. 实现原理

要实现这种爆炸图的分解与组合效果,其实原理很简单,就是找到模型中各个组成部分对应的Mesh,然后通过修改对应Mesh的Position坐标来实现产品的分解与组合效果,为了使分解和组合效果看起来更丝滑,在修改对应Position位置的时候,设置一个动画效果,使其开起来过渡更自然,我这里使用的是GSAP动画库,这个动画库非常强大,感兴趣的小伙伴可以看我之前写的一篇关于GSAP动画库使用的博客,这里只介绍具体的使用,就不讲解各个参数的定义了。

2. 创建项目

  • 在D盘新建vite-vue-Valve文件夹,鼠标右键点击新建的文件夹,使用vscode打开;
  • 在vscode中使用快捷键Ctrl+Shift+~打开终端,在终端中使用vite构建工具创建项目,输入pnpm create vite bmw-app --template vue创建项目
  • 创建成功后,在终端中输入cd bmw-app进入文件夹
  • 输入pnpm i 安装依赖包
  • 安装完成后,输入pnpm run div 启动项目,打开浏览器,可以看到系统默认的页面,说明项目环境搭建成功
  • 安装ThreeJS库,在终端中输入pnpm i three安装threejs插件
  • 安装GSAP库,在终端中输入 pnpm i gsap安装GSAP库
  • 删除vite构建工具为我们创建的HelloWord.vue文件和style.css中的样式,删除App.vue中的样式
  • 在components文件夹下新建ValveView.vue文件
  • 在App.vue的Template模板中调用 ValveView.vue
    App.vue中代码如下
    <template>
     <ValveView></ValveView>
    </template>
    
    <script setup>
    import ValveView from './components/ValveView.vue'; 
    </script>
    <style scoped>
    </style>
    

style.css中的样式代码如下:
*{ margin: 0; padding: 0; list-style: none; }

3. 编写代码

  • 在ValveView.vue的template模板中添加一个div,id设置为scene,作为承载Threejs的容器;再增加一个div,设置class=“control”,在这个div中添加两个button,并给两个button添加点击事件,用于控制产品模型的分解与组合
    template模板中代码如下:
    <template>
      <div id="scene"> 
      </div>
      <div class="control">
        <button @click="split(true)">分解</button>
        <button @click="split(false)">组合</button>
      </div>  
    </template>
    
  • 设置ValveView.vue中元素的样式,默认情况想,我们上面设置的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 THREE.Scene()方法初始化场景,具体代码如下
    // 初始化场景
    const initScene = () => {
      scene = new THREE.Scene()
      scene.background = new THREE.Color(0xcccccc)
      scene.environment = new THREE.Color(0xcccccc)
    }
    
  • 创建初始化相机函数,在初始化相机函数中,调用 new THREE.PerspectiveCamera()方法创建相机并设置相机的位置,具体代码如下:
    // 添加相机
    const initCamera = () => {
      camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 1)
      camera.position.set(0.08, 0.08, 0.15)
    }
    
  • 创建初始化灯光函数,在初始化灯光函数中,添加环境光、自然光、聚光灯等各种灯光效果,代码如下:
    // 添加灯光
    const initLight = () => {
      // 设置环境光
      scene.add(new THREE.AmbientLight(0xffffff, 0.5))
    
      // 添加球光源
      const hesLight = new THREE.HemisphereLight(0xffffff, 0x444444)
      hesLight.intensity = 0.6
      scene.add(hesLight)
      // 自然光
      const dirLight = new THREE.DirectionalLight()
      dirLight.position.set(0, 0, 15)
      scene.add(dirLight)
      const dirLight2 = new THREE.DirectionalLight()
      dirLight2.position.set(0, 0, -15)
      scene.add(dirLight2)
      const dirLight3 = new THREE.DirectionalLight()
      dirLight3.position.set(15, 0, 0)
      scene.add(dirLight3)
      const dirLight4 = new THREE.DirectionalLight()
      dirLight4.position.set(-15, 0, 0)
      scene.add(dirLight4)
      const dirLight5 = new THREE.DirectionalLight()
      dirLight5.position.set(0, 15, 0)
      scene.add(dirLight5)
      const dirLight6 = new THREE.DirectionalLight()
      dirLight6.position.set(0, -15, 0)
      scene.add(dirLight6)
      const dirLight7 = new THREE.DirectionalLight()
      dirLight7.position.set(5, 15, 5)
      scene.add(dirLight7)
      const dirLight8 = new THREE.DirectionalLight()
      dirLight8.position.set(-5, -15, -5)
      scene.add(dirLight8)
      // 聚光灯
      const sportLight = new THREE.SpotLight(0xffffff, 0.8)
      sportLight.angle = Math.PI / 8; //散射角度,跟水平线的夹角
      sportLight.penumbra = 0.1;  // 聚光锥的半影衰减百分比
      sportLight.decay = 2; // 纵向:沿着光照距离的衰减量。
      sportLight.distance = 10;
      sportLight.shadow.radius = 10;
      // 阴影映射宽度,阴影映射高度 
      sportLight.shadow.mapSize.set(512, 512);
      sportLight.position.set(0, 15, 0);
      // 光照射的方向
      sportLight.target.position.set(0, 0, 0);
      sportLight.castShadow = true;
      scene.add(sportLight);
    }
    
  • 创建加载GLTF模型函数,使用Threejs提供的new GLTFLoader()方法加载gltf模型文件,具体代码如下:
    // 加载GLTF模型
    const initModel = () => {
      loader = new GLTFLoader()
      loader.load('/model/fm.gltf', gltf => {
        model = gltf.scene 
        model.position.set(0, -0.08, 0)
        // 添加模型到场景
        scene.add(model)
      })
    }
    
  • 创建初始化渲染器函数,使用Threejs提供的new THREE.WebGLRenderer()方法创建渲染器,并设置相关参数,具体diam如下:
    // 创建渲染器
    	const initRenderer = () => {
    	  renderer = new THREE.WebGLRenderer({ antialias: true })
    	  renderer.setSize(window.innerWidth, window.innerHeight)
    	  document.getElementById('scene').appendChild(renderer.domElement)
    	}
    
  • 创建初始化控制器函数,添加控制器,使用Threejs提供的new OrbitControls()方法创建一个控制器,并设置相关参数,具体代码如下:
    // 添加控制器
    const initControl = () => {
      controls = new OrbitControls(camera, renderer.domElement)
      controls.enableDamping = true
      controls.dampingFactor = 0.25
      controls.enableZoom = true
    }
    
  • 创建循环渲染动画,通过调用requestAnimationFrame(animate) 循环调用该动画,并使用renderer.render(scene, camera)实时渲染场景,具体代码如下:
    // 渲染循环
    const playAnimate = () => {
      const animate = function () {
        requestAnimationFrame(animate) 
        renderer.render(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 = model.children[0].children
      if(val) { 
        myModel.forEach(item => {
          if(item.name === "Obj3d66-5355088-2-382") {
            // item.position.y = 0.03
            gsap.to(item.position,{
              duration:1,//动画持续时间
              y:0.03,//目标位置
              ease:'power2.inOut'
            })
          } else if (item.name === "Obj3d66-5355088-2-382_1"){
            // item.position.y = -0.05
            gsap.to(item.position,{
              duration:1,//动画持续时间
              y:-0.05,//目标位置
              ease:'power2.inOut'
            })
          }
        });
      } else {
        myModel.forEach(item => {
          if(item.name === "Obj3d66-5355088-2-382") {
            // item.position.y = 0.03
            gsap.to(item.position,{
              duration:1,//动画持续时间
              y:0,//目标位置
              ease:'line'
            })
          } else if (item.name === "Obj3d66-5355088-2-382_1"){
            // item.position.y = -0.05
            gsap.to(item.position,{
              duration:1,//动画持续时间
              y:0,//目标位置
              ease:'line'
            })
          }
        });
      }
    }
    

至此,我们就完成了产品三维爆炸图的分解与组合效果,运行程序,刷新浏览器,通过鼠标点击分解按钮和组合按钮查看动画效果,效果如下:
在这里插入图片描述
ok,threejs项目实战的第二个项目就实现了,小伙伴们有疑问的评论区留言,喜欢的小伙伴点赞关注+收藏哦!

  • 20
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
三维空间中,一条直线可以由两个点确定。我们可以使用 `THREE.Line` 来定义一条直线,并将其添加到场景中。 ```javascript // 创建两个点 var point1 = new THREE.Vector3(-5, 0, 0); var point2 = new THREE.Vector3(5, 0, 0); // 创建直线 var geometry = new THREE.Geometry(); geometry.vertices.push(point1, point2); var material = new THREE.LineBasicMaterial({ color: 0xff0000 }); var line = new THREE.Line(geometry, material); // 将直线添加到场景中 scene.add(line); ``` 上述代码创建了一条以两个点 `-5, 0, 0` 和 `5, 0, 0` 为端点的红色直线,并将其添加到场景中。 如果你想让这条直线在场景中保持不变,不会随着相机的移动而移动,可以将其添加到场景的静态物体中。 ```javascript var staticObject = new THREE.Object3D(); staticObject.add(line); scene.add(staticObject); ``` 这样,当相机移动时,这条直线将保持不变。 当然,你也可以在直线上加入一些动画效果,让它动起来。你可以通过设置直线的顶点坐标来使其运动。以下是一个简单的例子: ```javascript function animate() { requestAnimationFrame(animate); // 使直线的第二个点沿着 y 轴移动 line.geometry.vertices[1].y += 0.1; line.geometry.verticesNeedUpdate = true; renderer.render(scene, camera); } ``` 上述代码中,`animate` 函数每帧调用一次,使直线的第二个点沿着 y 轴移动了 `0.1` 的距离。`verticesNeedUpdate` 标记告诉 three.js 更新直线的顶点坐标。 希望这些能帮助你入门 three.js 中的直线绘制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九仞山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值