Vue中用Three.js创建一个3D会议室(三)拖动与旋转

4 篇文章 1 订阅
1 篇文章 0 订阅

第三章


虽然现在基本的模型都创建出来了,但是我们3D会议室的主要目的是可以自己摆放会议室中的物品,自己设定布局,所以我们需要能够对会议室中的模型进行位置拖动与角度旋转的操作来达到这一高度自由的效果。

拖动

DragControls

说到拖动效果,就必须讲到Three.js的拖动对象DragControls,它为Three.js提供了便捷的拖动交互
直接上代码讲解

initDragControls(objects, info) {
      let that = this;
      // 初始化拖拽控件
      this.dragControls = new DragControls(
        objects,
        this.camera,
        this.renderer.domElement
      );
      this.dragControls.transformGroup = true;
      // 开始拖拽
      this.dragControls.addEventListener("dragstart", this.setDragstart);
      this.dragControls.addEventListener("drag", function (e) {
        if (e.object.position.y < -60) {
           e.object.position.y = -60;
         }
      });
      // 拖拽结束
      this.dragControls.addEventListener("dragend", this.setDragend);
    },
	// 具名拖拽开始函数
    setDragstart() {
      this.rotateBtnShow = false;
      this.editWellObject = false;
      this.controls.enabled = false;
    },
    // 具名拖拽结束函数
    setDragend() {
      this.controls.enabled = true;
    },

首先不要忘了导入,import { DragControls } from "three/examples/jsm/controls/DragControls";
注册了DragControls对象后,为我们提供了5个事件
dragstart:当用户开始拖拽3D Objects时触发;
drag:当用户拖拽3D Objects时触发;
dragend:当用户开始完成3D Objects时触发;
hoveron:当指针移动到一个3D Object或者其某个子级上时触发;
hoveroff:当指针移出一个3D Object时触发。Dran
要注意在我们注册事件的时候,能用具名函数就用具名函数,因为这样做可以再页面销毁时移除注册的事件,优化性能。
此次项目中,为了达到重力效果,也就是桌子、椅子之类的物品,在拖动时不能离开地面,所以我在drag中加入了对当前操作模型y轴的限制,以此来达到以下效果:
在这里插入图片描述
对于多个3D模型,我们需要注册多个DragControls对象,因为每个拖拽对象会对应相应的模型,如果仅仅注册一个DragControls对象,将会造成无论移动哪一个模型,都只是第一个模型在移动的景象

旋转

因为Three.js并未直接提供让3D模型进行旋转的对象,所以我们为了达到旋转的效果,一共分为两部

获取鼠标选中的3D模型

想要选中一个物体主要是依靠Raycaster这个对象,它主要用于鼠标拾取,也就是计算鼠标在3维空间移动时触碰了哪些物体。原理就是在鼠标所指的位置会发射一条射线,一条垂直鼠标,垂直电脑屏幕的射线,Three.js将获取到该射线依次经过的物体。
其实对于这个射线的理解,我们也可以换一种方式,比如浏览器开发者模式,如下图
在这里插入图片描述
点击该按钮后可以查看页面的元素,鼠标经过的元素会在Elements中展示,下面上代码

getIntersects(event) {
      event.preventDefault();

      let raycaster = new THREE.Raycaster();
      let mouse = new THREE.Vector2();

      mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
      mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

      //通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
      raycaster.setFromCamera(mouse, this.camera);

      // 获取与射线相交的对象数组,其中的元素按照距离排序,越近的越靠前
      let moudle = this.scene.children.filter((item) => {
        if (item.type === "Group") {
          return item;
        }
      });
      let intersects = raycaster.intersectObjects(moudle, true);
      if (intersects.length > 0) {
        let object = intersects[0].object;
        this.getGroup(object);
        return this.selectGroup;
      }
      return {};
    },
    // 获取组
    getGroup(intersects) {
      let selectGroup = intersects.parent;
      if (selectGroup.type === "Group") {
        return (this.selectGroup = selectGroup);
      } else {
        selectGroup = selectGroup.parent;
        this.getGroup(selectGroup);
      }
    },

首先注册Raycaster对象,获取鼠标的坐标,根据坐标和当前相机(因为相机的视角也是一个很重要的因素)计算出射线的位置,根据raycaster.intersectObjects()方法获取与射线相交的3D对象,然后根据3D对象获取其所在的3D组,因为外部加载的3D模型一般都会是以组的形式出现,射线可能只是触碰到该组的某一个对象。

// 鼠标单击触发的事件
    mouseClick(event) {
      let intersects = this.getIntersects(event);
      if (intersects !== {} && intersects.type === "Group") {
        this.selectObject = intersects;
        if (this.selectObject.name === "可旋转") {
          this.rotateBtnShow = true;
          this.addRotateClick(this.selectObject);
        } else {
          this.editWellObject = true;
        }
      } else {
        this.rotateBtnShow = false;
        this.selectObject = null;
        this.editWellObject = false;
      }
    },

很显然我们需要注册鼠标点击事件去获取因触发射线与3D对象相交后得到的组,鼠标注册事件放在mounted中,这里我就不具体提了,在此也要注意一点,因为鼠标是全局注册,所以当点击空白出时由于获取不到相关对象,可能会导致报错,并且我们也并不是所有的3D模型都需要旋转效果,比如墙面和地板本就是不支持的,所以我们需要添加一个筛选条件,也就是intersects.type === "Group"当获取到的结果是一个组时,才去触发旋转的效果;(代码中未注册的方法后面代码中会有)

添加旋转按钮以及旋转方法

获取到被选中的物体之后,我们就要为其赋予可旋转的方法,为了区分当前物体是否可旋转以及突出当前选中的物体,首先创建一个旋转的按钮出来。

createRotateBtn() {
     let halWidth = window.innerWidth / 2;
     let halHeight = window.innerHeight / 2;
     let vector = this.selectObject.position.clone().project(this.camera);
     this.$refs.rotateBtn.style.left = `${
       halWidth + vector.x * halWidth - 40
     }px`;
     this.$refs.rotateBtn.style.top = `${
       -vector.y * halHeight + halHeight - this.selectObject.position.y - 230
     }px`;
   },

其实旋转按钮的位置主要就是靠Css定位来决定,所以我们要通过被选中的3D模型的坐标来计算旋转按钮的lefttop

   addRotateClick() {
     if (this.addRotate) {
       this.addRotate = false;
       let that = this;
       let newX = Number(this.$refs.rotateBtn.style.left.replace("px", "")); // 数据格式转
       document.addEventListener("dragenter ", function (e) {
         e.preventDefault();
       });
       document.addEventListener("dragover", function (e) {
         e.preventDefault();
       });
       // 旋转事件
       this.$refs.rotateBtn.addEventListener(
         "drag",
         function (event) {
           if (event.x < newX) {
             that.selectObject.rotateY(Math.PI / 120);
           } else if (event.x > newX) {
             that.selectObject.rotateY(Math.PI / -120);
           }
           newX = event.x;
         },
         true
       );
     }

如果旋转事件一直注册,不仅消耗内存,而且还会使旋转的速度越来越快, 为了避免旋转事件被一直注册,所以设定addRotate来作为一个开关,对于旋转事件我主要赋予旋转按钮上,通过旋转按钮的拖动来影响3D模型的旋转角度。
以当前按钮的坐标为基准,如果向左拖动按钮,对应的3D模型就进行逆时针旋转,旋转的幅度可以自己设置,用selectObject.rotateY(Math.PI / 120)方法达到被选中物体的旋转,同理,往右拖动按钮,对应的3D模型就进行顺时针旋转。
上效果:在这里插入图片描述

小贴士

在拖动的时候,可能因为鼠标移到了旋转按钮而停止拖动,所以在之前拖动的代码中,在开始拖动的回调函数里对旋转按钮进行的隐藏。
因为Three.js会对鼠标发出的射线经过的所有物体进行统计,所以在拖动物体时,摄像机角度可能也会被拖动,所以也要在开始拖动的回调函数中禁用轨道控制器,不让摄像机被旋转。

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Vue 2使用Three.js创建地图可以按照以下步骤进行: 1. 首先,确保你已经安装了Vue CLI并创建了一个Vue项目。如果没有安装,请按照Vue CLI的官方文档进行安装和创建项目。 2. 安装Three.js库。可以通过以下命令使用npm进行安装: ```bash npm install three ``` 3. 在Vue组件引入Three.js库。在你需要使用Three.js的组件,可以通过以下方式引入: ```javascript import * as THREE from 'three'; ``` 4. 创建一个Canvas元素用于渲染Three.js场景。在你的Vue组件的模板,添加一个Canvas元素,例如: ```html <template> <div> <canvas ref="canvas"></canvas> </div> </template> ``` 5. 在Vue组件的生命周期钩子函数创建Three.js场景并渲染。在你的Vue组件,可以在`mounted`钩子函数创建Three.js场景,并在其进行渲染。例如: ```javascript export default { mounted() { // 获取canvas元素 const canvas = this.$refs.canvas; // 创建场景 const scene = new THREE.Scene(); // 创建相机 const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.position.z = 5; // 创建渲染器 const renderer = new THREE.WebGLRenderer({ canvas }); renderer.setSize(window.innerWidth, window.innerHeight); // 创建地图 const geometry = new THREE.BoxGeometry(1, 1, 1); const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const cube = new THREE.Mesh(geometry, material); scene.add(cube); // 渲染场景 function animate() { requestAnimationFrame(animate); cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render(scene, camera); } animate(); }, }; ``` 以上代码创建了一个简单的场景,其添加了一个绿色的立方体,并通过动画使其旋转起来。 这只是一个简单示例,你可以根据自己的需求在Three.js创建更复杂的地图。希望对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值