2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > VUE使用Three.js实现模型 点击交互 相机旋转视角跟随移动(Threejs中使用Tweenjs 含demo源码)

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

时间:2019-05-12 15:27:56

相关推荐

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是什么?

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

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

1.npm安装

npm install threenpm install @tweenjs/tween.js

2.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,}); //材质对象Materiallet mesh = new THREE.Mesh(geometry, material); //网格模型对象Meshmesh.position.set(0, index * 4, 0); //设置mesh3模型对象的xyz坐标为120,0,0this.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,}); //材质对象Materiallet mesh = new THREE.Mesh(geometry, material); //网格模型对象Meshmesh.position.set(-long / 2 + (index - 1) * 4, tall / 2, 0); //设置mesh3模型对象的xyz坐标为120,0,0this.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步骤(完成各种事件和效果)

在简单使用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><buttonid="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.25this.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(效果组合器)对象,然后在该对象上添加后期处理通道。poser = new EffectComposer(this.renderer);// 新建一个场景通道 为了覆盖到原理来的场景上this.renderPass = new RenderPass(this.scene, this.camera);poser.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 = poser.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到1this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;this.mouse.y = -(event.clientY / (window.innerHeight-50)) * 2 + 1;// 通过鼠标点的位置和当前相机的矩阵计算出raycasterthis.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 (poser) {poser.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>

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

四、源码地址

建议认真看每一部分的代码,实在不清楚可以看源码,毕竟最近才做这方面的项目,写的不好的地方,还请各位嘴下留情。

货架三维模型: 简单的货架三维模型实现点击交互,相机旋转等功能/halsixsixsix/3d-model-of-shelf.git

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。