第三章

文章目录

  • 拖动
    • DragControls
  • 旋转
    • 获取鼠标选中的3D模型
    • 添加旋转按钮以及旋转方法
  • 小贴士

虽然现在基本的模型都创建出来了,但是我们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会对鼠标发出的射线经过的所有物体进行统计,所以在拖动物体时,摄像机角度可能也会被拖动,所以也要在开始拖动的回调函数中禁用轨道控制器,不让摄像机被旋转。

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

  1. 使用webgl(three.js)搭建一个3D建筑,3D消防模拟——第三课

    使用webgl(three.js)搭建一个3D建筑,3D消防模拟--第三课 项目背景 消防安全一直是各大都市关注的重要课题,在消防体系中,特别是高楼消防体系中,消防系统整体布控与监控,火情有效准确定位 ...

  2. Unity与C#创建一个3D平台游戏 Learn to Create a 3D Platformer Game with Unity C#

    游戏开发变得容易了.使用Unity学习C#并创建您自己的3D平台! 你会学到什么 学习现代通用编程语言C#. 了解Unity中3D开发的功能 发展强大的可转移的解决问题的技能 了解游戏开发过程 了解面 ...

  3. OPenGL笔记--创建一个3D场景

    文章目录 一.前言 二.效果展示 三.详细流程 3.1.World.txt文件规则 3.2.加载World.txt 3.3.绘制场景 3.4.交互 四.详细代码 五.举一反三 一.前言 通过前面的学习 ...

  4. 利用photoshop创建一个3D绚丽的文…

    本photoshop教程转载于10steps.sg,by Johnson Koh on 11-11-2008,(原创翻译,转载请注明出处,标注链接,多谢) 简介:我们将会运用到illustrate的3 ...

  5. Creating a 3D Logo in Photoshop Photoshop系列教程之如何创建一个3D Logo Lynda课程中文字幕

    Creating a 3D Logo in Photoshop 中文字幕 Photoshop系列教程之如何创建一个3D Logo 中文字幕Creating a 3D Logo in Photoshop ...

  6. Java 创建一个线程的三种方式

    Java 创建一个线程的三种方式 更多内容,点击了解: https://how2j.cn/k/thread/thread-start/353.html 创建多线程有3种方式,分别是继承线程类,实现Ru ...

  7. Vue学习笔记:创建一个Vue实例

    目录 1.访问Vue官网https://cn.vuejs.org,进入学习模块下的教程 2.创建页面index.html 3.通过script标签在head部分引入vue.js 4.在body部分创建 ...

  8. 搭建vue项目环境以及创建一个简单的vue的demo

    一.vue-cli脚手架的搭建步骤 1.首先,确定你的电脑上已经安装了nodejs,可以使用npm包管理器安装环境,如果还没有安装node环境,则需要安装node.js 这个很简单    默认点击安装 ...

  9. 用php编写一个正方体,three.js画一个3D立体的正方体教程

    Three.js是一个3DJavaScript库,基于右手坐标系,可以创建简单或是比较复杂的三维图形并应用丰富多彩的纹理和材质,可以添加五光十色的光源,可以在3D场景中移动物体或是添加脚本动画等等.本 ...

最新文章

  1. 低速自动驾驶车辆的定位与建图
  2. C#+AE 调整TOCControl控件中图层的显示顺序
  3. 陈松松:视频营销成交率低,这三个因素没到位
  4. [C++] vector 定义和初始化
  5. 系统管理员必学的30个Linux实用命令
  6. 打印机可以打印不能扫描怎么弄_为什么打印机可以通过电脑打印可不能扫描呢...
  7. 史上最全:PostgreSQL DBA常用SQL查询语句(建议收藏学习)
  8. Docker在linux下的安装
  9. 计算机学术英语常见词汇短语总结
  10. 漫画:什么是 ZooKeeper?
  11. sql 求相交的行_SQL相交
  12. Linux系统安装教程之一:VM14虚拟机+Ubuntu16安装
  13. ubuntu安装cuda驱动
  14. MATLAB线性回归方程与非线性回归方程的相关计算
  15. HTML制作简单课程表
  16. 2017年十本必读的大数据人工智能领域书籍,你都读过吗?
  17. 九个小妙方缩小毛孔立竿见影! - 生活至上,美容至尚!
  18. 数学规划模型总结(附MatLab代码)
  19. springboot项目Banner配置
  20. html是一种网页设计语言,html网页设计教程

热门文章

  1. 哪一种验证方法最好?形式验证、硬件加速还是动态仿真?
  2. Perseus-BERT——业内性能极致优化的BERT训练方案【阿里云弹性人工智能】 1
  3. 线程池2nd卷:虎落平阳被犬欺
  4. 读 RocketMQ 源码,学习并发编程三大神器
  5. 【深度】谭铁牛院士谈人工智能发展新动态
  6. 每天一篇论文 289/365Deep Reinforcement Learning for Robotic Pushing and Picking in Cluttered Environment
  7. html设置行的水平对齐
  8. 北大数学英才班,没有一名新生经历高三
  9. ios应用跳转小程序Universal Links链接验证不通过
  10. GIC/ITS代码分析(1)MADT表