位置: IT常识 - 正文

VUE使用Three.js实现模型,点击交互,相机旋转视角跟随移动(Threejs中使用Tweenjs,含demo源码)(vue three.js)

编辑:rootadmin
VUE使用Three.js实现模型,点击交互,相机旋转视角跟随移动(Threejs中使用Tweenjs,含demo源码)

目录

一、Three.js是什么?

二、VUE简单使用Three.js步骤

1.npm安装

2.template模板

3.引入库

4.定义全局变量

5.初始化场景

6.初始化相机

7.初始化灯光

8.初始化渲染器

9.创建模型(这里我搭建的模型是一个简单双面货架模型)

10.根据浏览器窗口自适应

11.初始化函数,页面加载完成时调用(mounted()中调用)

12.Style样式

三、VUE进阶使用Three.js步骤(完成各种事件和效果)

在简单使用Three.js的基础上,添加以下控件和代码

1.引入库及需要使用的组件

2.template模板

3.定义全局变量

4.使用OrbitControls控制给模型添加缩放,旋转,平移和拖拽等效果

5.点击模型后的模型样式效果

6.相机跟随点击事件移动动画效果

7.返回主视角按钮事件

8.添加鼠标点击模型事件,并调用相机移动方法(第6点)和点击后样式方法(第5点)

9.运行动画

10.初始化函数,页面加载完成时调用(mounted()中调用)

11.Style样式

四、源码地址


一、Three.js是什么?

推荐整理分享VUE使用Three.js实现模型,点击交互,相机旋转视角跟随移动(Threejs中使用Tweenjs,含demo源码)(vue three.js),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:vue3的,vue3使用jsx,vue3使用jsx,threejs vue3,vue3使用jsx,threejs vue3,vue+threejs,vue+threejs,内容如对您有帮助,希望把文章链接给更多的朋友!

Three.js是一款基于原生WebGL封装通用Web 3D引擎,在小游戏、产品展示、物联网、数字孪生、智慧城市园区、机械、建筑、全景看房、GIS等各个领域基本上都有three.js的身影

二、VUE简单使用Three.js步骤1.npm安装npm install threenpm install @tweenjs/tween.js2.template模板<template> <div class="container"> <div id="model"> </div> </div></template>3.引入库import * as THREE from "three";4.定义全局变量data() { return { isDisabled: true, scene: null, camera: null, renderer: null, light: null, light2: null, //定义模型架子的长度 long: 100, //定义模型架子的宽度 tall: 24, //定义模型架子横纵板的宽度 thickness: 0.4, };}5.初始化场景//初始化场景initScene() { this.scene = new THREE.Scene();},6.初始化相机//初始化相机initCamera() { const { long, tall } = this; this.camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 50000 ); this.camera.position.set(0, -(5 * tall) / 12, (6 * long) / 5);}7.初始化灯光//初始化灯光initLight() { let ambientLight = new THREE.AmbientLight(0x404040); this.scene.add(ambientLight); //定义灯,并设置位置 this.light = new THREE.DirectionalLight(0x333333); this.light.position.set(60, 30, 40); this.light2 = new THREE.DirectionalLight(0xdddddd); this.light2.position.set(-20, 20, -20); this.scene.add(this.light); this.scene.add(this.light2);},8.初始化渲染器//初始化渲染器initRender() { //dom元素渲染器 this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); this.renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染区域尺寸 this.renderer.setClearColor(0x000000, 0); // 设置背景颜色 //window.devicePixelRatio 当前设备的物理分辨率与css分辨率之比 this.renderer.setPixelRatio(window.devicePixelRatio); //按层级先后渲染 this.renderer.sortObjects = true; document.getElementById("model").appendChild(this.renderer.domElement);},9.创建模型(这里我搭建的模型是一个简单双面货架模型)//创建载入模型initModel() { const { long, tall, thickness } = this; //坐标系 // let axes = new THREE.AxesHelper(4000); // this.scene.add(axes); //所有横板 for (let index = 1; index <= tall / 4 - 1; index++) { let geometry = new THREE.BoxGeometry(long, thickness, 12); let material = new THREE.MeshLambertMaterial({ color: 0x808080, opacity: 0.7, transparent: true, }); //材质对象Material let mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh mesh.position.set(0, index * 4, 0); //设置mesh3模型对象的xyz坐标为120,0,0 this.scene.add(mesh); //网格模型添加到场景中 } //所有纵板 for (let index = 1; index <= long / 4 + 1; index++) { let geometry = new THREE.BoxGeometry(thickness, tall, 12); let material = new THREE.MeshLambertMaterial({ color: 0x808080, opacity: 0.7, transparent: true, }); //材质对象Material let mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh mesh.position.set(-long / 2 + (index - 1) * 4, tall / 2, 0); //设置mesh3模型对象的xyz坐标为120,0,0 this.scene.add(mesh); //网格模型添加到场景中 } //正面所有箱子 let list1 = new Array(tall / 4); for (let i = 0; i < list1.length; i++) { list1[i] = new Array(long / 4); //不一定写for循环赋值,还可以直接赋值,在数量有限的情况下 for (let j = 0; j < long / 4; j++) { // a[i][j] = i + j; let geometry = new THREE.BoxGeometry(3, 3, 5); let material = new THREE.MeshLambertMaterial({ color: 0x00b1f7, opacity: 0.4, transparent: true, }); let mesh = new THREE.Mesh(geometry, material); //定义每个箱子的名称(或者编号) mesh.name = i + 1 + "-" + (j + 1); //设置每个箱子的位置 mesh.position.set(-long / 2 + j * 4 + 2, i * 4 + 2, 3); this.scene.add(mesh); } } //背面所有箱子 let list2 = new Array(tall / 4); for (let i = 0; i < list2.length; i++) { list2[i] = new Array(long / 4); //不一定写for循环赋值,还可以直接赋值,在数量有限的情况下 for (let j = 0; j < long / 4; j++) { // a[i][j] = i + j; let geometry = new THREE.BoxGeometry(3, 3, 5); let material = new THREE.MeshLambertMaterial({ color: 0x00b1f7, opacity: 0.4, transparent: true, }); let mesh = new THREE.Mesh(geometry, material); //定义每个箱子的名称(或者编号) mesh.name = "-" + (i + 1) + "-" + (j + 1); //设置每个箱子的位置 mesh.position.set(-long / 2 + j * 4 + 2, i * 4 + 2, -3); this.scene.add(mesh); } }}10.根据浏览器窗口自适应//根据浏览器窗口自适应onWindowResize() { this.renderer.setSize(window.innerWidth, window.innerHeight); this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix();}11.初始化函数,页面加载完成时调用(mounted()中调用)//初始化函数,页面加载完成时调用init() { this.initScene(); this.initCamera(); this.initLight(); this.initRender(); this.initModel(); this.renderer.render(this.scene, this.camera); window.onresize = this.onWindowResize;}12.Style样式<style scoped>.container { width: 100%; height: 100%; position: relative;}/*模型样式*/#model { width: 100%; height: 100%; position: relative; overflow: hidden;}</style>

呈现效果,如下图。

VUE使用Three.js实现模型,点击交互,相机旋转视角跟随移动(Threejs中使用Tweenjs,含demo源码)(vue three.js)

但是这样仅仅只是将自制的模型创建并渲染了出来,无法满足我们大多数情况的使用场景,例如旋转,点击,拖拽平移,相机视角跟随移动等,接下来就实现这些效果。

三、VUE进阶使用Three.js步骤(完成各种事件和效果)在简单使用Three.js的基础上,添加以下控件和代码1.引入库及需要使用的组件import TWEEN from "@tweenjs/tween.js";import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";2.template模板<template> <div class="container"> <div id="model"></div> <button id="btns" :style="{ cursor: isDisabled ? '' : 'pointer' }" :disabled="isDisabled" type="isDisabled:" @click="toHomeView(1)" > 主视角 </button> </div></template>3.定义全局变量

这里就是所有的全局变量了,直接复制替换就行了

data() { return { isDisabled: true, scene: null, camera: null, renderer: null, controls: null, light: null, light2: null, group: new THREE.Group(), composer: null, // 控制发光 outlinePass: null, renderPass: null, // 选中的模型 selectedObjects: [], mouse: new THREE.Vector2(), raycaster: new THREE.Raycaster(), tween: null, //定义模型架子的长度 long: 100, //定义模型架子的宽度 tall: 24, //定义模型架子横纵板的宽度 thickness: 0.4, positionObj: null, };}4.使用OrbitControls控制给模型添加缩放,旋转,平移和拖拽等效果//使用OrbitControls控制三维场景缩放和旋转等功能initControls() { this.controls = new OrbitControls(this.camera, this.renderer.domElement); //动态阻尼系数 即鼠标拖拽旋转的灵敏度 this.controls.dampingFactor = 0.25; // this.controls.target.set(0, 900, 0) // //上下旋转范围 this.controls.minPolarAngle = 0; this.controls.maxPolarAngle = 1.5; this.controls.autoRotate = true; //惯性滑动,滑动大小默认0.25 this.controls.dampingFactor = 0.25; //滚轮是否可控制zoom,zoom速度默认1 //缩放倍数 this.controls.zoomSpeed = 1.0; //最大最小相机移动距离(景深相机) this.controls.minDistance = 1; this.controls.maxDistance = Infinity; //水平方向视角限制 this.minAzimuthAngle = -Math.PI * 2; this.maxAzimuthAngle = Math.PI * 2; this.controls.enabledPan = true; this.keyPanSpeed = 7.0;}5.点击模型后的模型样式效果//高亮显示模型(呼吸灯)outlineObj(selectedObjects) { // 创建一个EffectComposer(效果组合器)对象,然后在该对象上添加后期处理通道。 this.composer = new EffectComposer(this.renderer); // 新建一个场景通道 为了覆盖到原理来的场景上 this.renderPass = new RenderPass(this.scene, this.camera); this.composer.addPass(this.renderPass); // 物体边缘发光通道 this.outlinePass = new OutlinePass( new THREE.Vector2(window.innerWidth, window.innerHeight), this.scene, this.camera, selectedObjects ); this.outlinePass.edgeStrength = 8.0; // 高光边缘边框的亮度 this.outlinePass.edgeGlow = 1; // 光晕[0,1] 边缘微光强度 this.outlinePass.usePatternTexture = false; // 是否使用父级的材质,纹理覆盖 this.outlinePass.edgeThickness = 3; // 边框宽度,高光厚度 this.outlinePass.downSampleRatio = 1; // 边框弯曲度 this.outlinePass.pulsePeriod = 2; // 呼吸闪烁的速度,数值越大,律动越慢 this.outlinePass.visibleEdgeColor.set(parseInt(0x00f6ff)); // 呼吸显示的颜色 this.outlinePass.hiddenEdgeColor = new THREE.Color(0, 0, 0); // 呼吸消失的颜色 // this.outlinePass.clear = true this.composer.addPass(this.outlinePass); // 加入高光特效 this.outlinePass.selectedObjects = selectedObjects; // 需要高光的模型}6.相机跟随点击事件移动动画效果// 相机移动动画initTween(targetX, targetY, targetZ) { // 获取当前相机位置 let initPosition = { x: this.camera.position.x, y: this.camera.position.y, z: this.camera.position.z, }; //定义相机移动方法 let tween = new TWEEN.Tween(initPosition) .to({ x: targetX, y: targetY, z: targetZ }, 2000) .easing(TWEEN.Easing.Sinusoidal.InOut); //格子位置参数 let onUpdate = (pos) => { let x = pos.x; let y = pos.y; let z = pos.z; //z<0为背面格子,z>0为正面格子,并设置相机的位置 if (pos.z < 0) { this.camera.position.set(x, y, z - 12); } else { this.camera.position.set(x, y, z + 12); } }; //调用相机方法并传入格子位置参数 tween.onUpdate(onUpdate); tween.start(); //设置相机target位置(相机看向格子的位置) if (this.positionObj.z < 0) { this.controls.target.set( this.positionObj.x, this.positionObj.y - 0.4, -12 ); } else { this.controls.target.set( this.positionObj.x, this.positionObj.y - 0.4, 12 ); }}7.返回主视角按钮事件//相机返回主视角动画toHomeView(id) { const { long, tall } = this; if (id === 1) { // 获取当前相机位置 let initPosition = { x: this.camera.position.x, y: this.camera.position.y, z: this.camera.position.z, }; //定义相机移动方法 let tween = new TWEEN.Tween(initPosition) .to({ x: 0, y: -(5 * tall) / 12, z: (6 * long) / 5 }, 2000) .easing(TWEEN.Easing.Sinusoidal.InOut); //主视角相机位置(点击前原位置) let onUpdate = (pos) => { let x = pos.x; let y = pos.y; let z = pos.z; this.camera.position.set(x, y, z); }; tween.onUpdate(onUpdate); tween.start(); //设置相机target位置(看向坐标轴零点的位置) this.controls.target.set(0, 0, 0); //相机返回原点后,返回主视角禁用 this.isDisabled = true; //相机返回原点后,开启模型自动旋转 this.controls.autoRotate = true; }}8.添加鼠标点击模型事件,并调用相机移动方法(第6点)和点击后样式方法(第5点)// 鼠标点击模型onMouseClick(event) { //通过鼠标点击的位置计算出raycaster所需要的点的位置,以屏幕中心为原点,值的范围为-1到1 this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1; this.mouse.y = -(event.clientY / (window.innerHeight-50)) * 2 + 1; // 通过鼠标点的位置和当前相机的矩阵计算出raycaster this.raycaster.setFromCamera(this.mouse, this.camera); // 获取raycaster直线和所有模型相交的数组集合 let intersects = this.raycaster.intersectObjects(this.scene.children); if (!intersects[0]) { return; } else { //这样会获取所有模型,因为我们这里只需要小格子 //所以只需要获取我们之前设置name!=""属性的object即可 if (!intersects[0].object.name == "") { this.selectedObjects = []; this.selectedObjects.push(intersects[0].object); //调用高亮显示模型(呼吸灯)的方法给点击的格子添加点击后的样式 this.outlineObj(this.selectedObjects); //拿到格子的position坐标 this.positionObj = { x: intersects[0].object.position.x, y: intersects[0].object.position.y, z: intersects[0].object.position.z, }; //调用机移动动画,将相机移动至被点击的格子处 this.initTween( this.positionObj.x, this.positionObj.y, this.positionObj.z ); //点击格子后,开放返回主视角的点击权限 this.isDisabled = false; //点击格子后,禁止模型自动旋转 this.controls.autoRotate = false; } }}9.运行动画//运行动画animate() { //运行相机旋转动画 TWEEN.update(); //渲染场景和相机 this.renderer.render(this.scene, this.camera); this.controls.update(); if (this.composer) { this.composer.render(); } //使用requestAnimationFrame周期性渲染 requestAnimationFrame(this.animate);}10.初始化函数,页面加载完成时调用(mounted()中调用)//初始化函数,页面加载完成时调用init() { this.initScene(); this.initCamera(); this.initLight(); this.initRender(); this.initModel(); this.renderer.render(this.scene, this.camera); this.initControls(); this.animate(); window.onresize = this.onWindowResize; window.onclick = this.onMouseClick;}11.Style样式<style scoped>.container { width: 100%; height: 100%; position: relative;}#btns { position: absolute; background-color: #031b34; bottom: 0; left: 50%; width: 80px; height: 30px; line-height: 30px; transform: translate(-50%, -50%); text-align: center; z-index: 9999; color: #00eeff; font-weight: bold; box-shadow: 0px 0px 2px 1px #00f6ff; border-radius: 6px; border: 1px solid #00f6ff; padding: 0;}/*模型样式*/#model { width: 100%; height: 100%; position: relative; overflow: hidden;}</style>

呈现效果如下图,如下图。

四、源码地址

建议认真看每一部分的代码,实在不清楚可以看源码,毕竟最近才做这方面的项目,写的不好的地方,还请各位嘴下留情。货架三维模型: 简单的货架三维模型实现点击交互,相机旋转等功能https://gitee.com/halsixsixsix/3d-model-of-shelf.git

本文链接地址:https://www.jiuchutong.com/zhishi/288754.html 转载请保留说明!

上一篇:世界著名气泡酒产地普罗塞克山丘,意大利威尼托 (© Olimpio Fantuz/Sime/eStock Photo)(世界著名气泡酒公司)

下一篇:科尼西宫的花园,佛罗伦萨 (© Will Perrett/Alamy)(科尼西卡神庙在哪里)

  • 怎样创造博客网络营销模式影响力(创建博客的步骤)

    怎样创造博客网络营销模式影响力(创建博客的步骤)

  • 饮水机有塑料味怎么办(饮水机有塑料味正常吗)

    饮水机有塑料味怎么办(饮水机有塑料味正常吗)

  • iqoo8怎么截长图(iqoo手机怎么截长图)

    iqoo8怎么截长图(iqoo手机怎么截长图)

  • 荣耀60可以遥控空调吗(荣耀60可以遥控电视吗)

    荣耀60可以遥控空调吗(荣耀60可以遥控电视吗)

  • 华为荣耀8x手机屏幕几英寸(华为荣耀8x手机怎么样)

    华为荣耀8x手机屏幕几英寸(华为荣耀8x手机怎么样)

  • 主机与cpu的区别(cpu和主机的区别)

    主机与cpu的区别(cpu和主机的区别)

  • 为什么苹果慢动作屏闪(为什么苹果慢动作视频发出去不是慢动作)

    为什么苹果慢动作屏闪(为什么苹果慢动作视频发出去不是慢动作)

  • p40pro屏幕尺寸(p40pro屏幕尺寸mm)

    p40pro屏幕尺寸(p40pro屏幕尺寸mm)

  • 什么语言是用助记符来代替二进制指令的面向机器的语言(什么语言是用助记符来代替二进制指令)

    什么语言是用助记符来代替二进制指令的面向机器的语言(什么语言是用助记符来代替二进制指令)

  • 华为nova7pro耳机怎么用(华为nova7pro耳机插上去没反应)

    华为nova7pro耳机怎么用(华为nova7pro耳机插上去没反应)

  • ctrlf6是什么快捷键(ctrl f快捷键的作用)

    ctrlf6是什么快捷键(ctrl f快捷键的作用)

  • 腾讯课堂学生可以看自己的在线时长吗(腾讯课堂学生可以设置虚拟背景吗)

    腾讯课堂学生可以看自己的在线时长吗(腾讯课堂学生可以设置虚拟背景吗)

  • 美版苹果11pro是双卡吗(美版苹果11pro是单卡还是双卡)

    美版苹果11pro是双卡吗(美版苹果11pro是单卡还是双卡)

  • 微信领红包显示账号异常怎么回事(微信领红包显示当前交易存在欺诈风险)

    微信领红包显示账号异常怎么回事(微信领红包显示当前交易存在欺诈风险)

  • 微信不能抢群红包怎么办(为什么群里抢红包功能限制了)

    微信不能抢群红包怎么办(为什么群里抢红包功能限制了)

  • 手机屏幕右上角出现一个HD怎么关闭(手机屏幕右上角有个电话打个X)

    手机屏幕右上角出现一个HD怎么关闭(手机屏幕右上角有个电话打个X)

  • AirPodsPro入耳检测在哪(airpodspro入耳检测不灵敏)

    AirPodsPro入耳检测在哪(airpodspro入耳检测不灵敏)

  • 微信二维码收款码怎么取消(微信二维码收款语音播报怎么弄)

    微信二维码收款码怎么取消(微信二维码收款语音播报怎么弄)

  • 苹果手机抬头灯显示在哪里设置(苹果手机抬头灯怎么关)

    苹果手机抬头灯显示在哪里设置(苹果手机抬头灯怎么关)

  • 铃声多多视频怎么下载(铃声多多视频怎么做)

    铃声多多视频怎么下载(铃声多多视频怎么做)

  • 淘宝消息未读能撤回吗(淘宝消息已读变成未读)

    淘宝消息未读能撤回吗(淘宝消息已读变成未读)

  • 手机64g内存够用吗

    手机64g内存够用吗

  • 电话被轰炸有什么办法解决(电话被轰炸有什么影响)

    电话被轰炸有什么办法解决(电话被轰炸有什么影响)

  • iOS 紧急通知(苹果紧急提醒)

    iOS 紧急通知(苹果紧急提醒)

  • 向下递归以及向上递归(递归是从底向上逐层计算的)

    向下递归以及向上递归(递归是从底向上逐层计算的)

  • 房产税城镇土地使用税税率
  • 新公司税务报到网上操作流程
  • 增值税预缴申报和正常申报的区别
  • 补报以前年度收入后当年所得税怎么处理
  • 转入公账怎么做账
  • 出口货物如何申报
  • 如何开增值税专用发票视频
  • 管理费用劳保费属于
  • 开具红字信息表后怎么开负数发票
  • 营改增无形资产
  • 超市送现金券怎么做账
  • 业务有提成个税怎么扣
  • 代收代付的发票开给谁
  • 地产商自持是什么意思
  • 企业收取房屋维修费用
  • 外贸企业出口退税账务处理
  • 以旧机器抵货款合法吗
  • 销项税现金流量表在附表中如何列示
  • 企业所得税分期收款确认收入的时间政策
  • 失控发票如何转出
  • windows10如何卸载edge
  • win10为什么毛病这么多
  • 家具厂打磨工工作图片
  • 行业收购溢价
  • 电脑自带网速测试
  • 苹果手机nfc录门禁卡
  • 周转材料要计入增值税吗
  • 不良贷款转让需遵循的原则
  • token过期处理
  • 坎伯兰森林西部
  • 前端工程化的理解简书
  • 分红派息钱去哪里了
  • 可供出售和交易性金融资产的区别
  • php绘制图片
  • vue封装组建
  • php统计系统
  • 库存现金盘亏盘盈
  • 对公账户和私人账户怎么区分
  • 物权转移的几种情形
  • 没收的定金收入怎么入账
  • 残疾人保障金所属期怎么填
  • sqlserver2005使用教程
  • 私募基金怎么运作
  • 只有进项税月末要转入未交增值税么
  • 主营业务成本是什么科目
  • 出口退免税的条件
  • 合并报表抵消分录的基本原理
  • 支付结算规定的基本要求
  • 把上级机关来文转给下级机关的通知
  • 非居民企业所得税税率
  • 公司新装宽带怎么安装
  • 公司法人往来款账务处理
  • 旅游服务住宿费可以抵扣吗
  • 公司备用金使用后没有发票抵扣
  • 会计政策变更的追溯调整法和未来适用法
  • mysql数据库数据迁移
  • windows10创意者更新
  • 怎么将windowsxp换成windows7
  • 让windows server 2003 32位支持8G内存大内存
  • xp系统无法更换桌面壁纸
  • mac中通过python关闭浏览器中的finder弹框
  • linux服务器常用命令
  • 如何重设苹果手机的ID密码
  • ubuntu安装指南
  • win7如何设置关机快捷方式
  • win7不能玩dnf
  • Node.js Sequelize如何实现数据库的读写分离
  • jquery滑动效果
  • shell循环结构
  • javascript面向对象编程指南
  • linux无法使用yum命令
  • 怎么用python下载付费音乐
  • 健壮的什么
  • javascript面向对象吗
  • 国家税务总局增值税发票查验平台官网
  • 广西税务局123
  • 税务管理职责
  • 河南省地方税务局公告2011年第10号
  • 国家税务总局辽宁省税务局
  • 青岛个人所得税咨询电话是多少
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设