2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > vue+three.js实现模型发光 视角改变 相机移动 管道流动动画

vue+three.js实现模型发光 视角改变 相机移动 管道流动动画

时间:2021-11-06 05:20:28

相关推荐

vue+three.js实现模型发光 视角改变 相机移动 管道流动动画

前端小白正在学习three.js,有感兴趣的大家一起相互交流

代码主要包括模型发光、3D卡片、视角转换、管道流动等在数字孪生、智慧工厂中常用的一些方法

我这里用的三个box,在实际应用中多为obj+mtl格式、或者gltf的模型,这些方法也是一样通用的

<template><div class="about"><div id="model"><div id="canvas1" style="display: none">canvas1</div><div id="canvas2" style="display: none">canvas2</div><div id="canvas3" style="display: none">canvas3</div><div id="canvas4" style="display: none"><span @click.stop="deviceDetail(1)">设备详情</span></div><div id="canvas5" style="display: none"><span @click="deviceDetail(2)">设备详情</span></div><div id="canvas6" style="display: none"><span @click.stop="deviceDetail(3)">设备详情</span></div><div id="btns"><span @click.stop="toView(1)">主视角</span><span @click.stop="toView(2)">俯视角</span></div></div></div></template><script>import * as THREE from 'three'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'import TWEEN from '@tweenjs/tween.js'import $ from 'jquery'// import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'// import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'import {CSS3DRenderer,CSS3DObject,} from 'three/examples/jsm/renderers/CSS3DRenderer'export default {name: '',data() {return {scene: null,camera: null,renderer: null,controls: null,cssScene: null,cssRender: null,cssControls: null,light: null,light2: null,flowingLineTexture: null,group: new THREE.Group(),composer: null, // 控制发光outlinePass: null,renderPass: null,// 选中的模型selectedObjects: [],mouse: new THREE.Vector2(),raycaster: new THREE.Raycaster(),tween: null,// 数据显示面板 1-3 数据 4-6为设备详情按钮dataPanel: {plane1: null,plane2: null,plane3: null,plane4: null,plane5: null,plane6: null,},// 管道纹理flowingLineTexture1: null,flowingLineTexture2: null,}},components: {},computed: {},watch: {},created() {},mounted() {if (this.scene !== null) {this.scene = null}this.draw()},destroyed() {this.scene.autoUpdate = falsecancelAnimationFrame(this.animate)this.renderer.domElement.innerHTML = ''this.renderer.forceContextLoss()this.renderer.dispose()this.scene.children = []},methods: {initScene() {this.scene = new THREE.Scene()this.cssScene = new THREE.Scene()},initCamera() {this.camera = new THREE.PerspectiveCamera(45,window.innerWidth / window.innerHeight,0.1,50000)this.camera.position.set(0, 1000, 2900)// this.camera.lookAt(new THREE.Vector3(0,0,0))},initLight() {var ambientLight = new THREE.AmbientLight(0x404040)this.scene.add(ambientLight)this.light = new THREE.DirectionalLight(0x333333)this.light.position.set(20, 20, 20)this.light2 = new THREE.DirectionalLight(0xdddddd)this.light2.position.set(-20, 20, -20)this.scene.add(this.light)this.scene.add(this.light2)},initRender() {//dom元素渲染器this.cssRender = new CSS3DRenderer({antialias: true })this.cssRender.setSize(window.innerWidth, window.innerHeight)this.cssRender.domElement.style.position = 'absolute'this.cssRender.domElement.style.top = '0'this.cssRender.domElement.style.outline = 'none'document.getElementById('model').appendChild(this.cssRender.domElement)this.renderer = new THREE.WebGLRenderer({antialias: true })this.renderer.setSize(window.innerWidth, window.innerHeight)this.renderer.setClearColor(new THREE.Color(0x01050f))//window.devicePixelRatio 当前设备的物理分辨率与css分辨率之比this.renderer.setPixelRatio(window.devicePixelRatio)//按层级先后渲染this.renderer.sortObjects = truedocument.getElementById('model').appendChild(this.renderer.domElement)},initModel() {//坐标系var axes = new THREE.AxesHelper(4000)this.scene.add(axes)var planGeometry = new THREE.PlaneGeometry(3000, 2000)var planeMaterial = new THREE.MeshLambertMaterial({color: 0xcccccc,side: THREE.DoubleSide,// wireframe:true})var plane = new THREE.Mesh(planGeometry, planeMaterial)plane.rotation.x = -Math.PI / 2plane.position.set(0, 0, 0)this.scene.add(plane)var cubeGeometry = new THREE.BoxGeometry(400, 200, 200)var cubeMaterial = new THREE.MeshLambertMaterial({color: 0x00ff00,transparent: true,opacity: 0.8,})var cube = new THREE.Mesh(cubeGeometry, cubeMaterial)cube.position.set(0, 120, 0)cube.name = 'cube1'this.scene.add(cube)var cubeGeometry2 = new THREE.BoxGeometry(400, 200, 200)var cubeMaterial2 = new THREE.MeshLambertMaterial({color: 0x00ffff,transparent: true,opacity: 0.8,})var cube2 = new THREE.Mesh(cubeGeometry2, cubeMaterial2)cube2.position.set(1000, 120, -500)cube2.name = 'cube2'this.scene.add(cube2)var cubeGeometry3 = new THREE.BoxGeometry(400, 200, 200)var cubeMaterial3 = new THREE.MeshLambertMaterial({color: 0xff0000,transparent: true,opacity: 0.8,})var cube3 = new THREE.Mesh(cubeGeometry3, cubeMaterial3)cube3.position.set(-1000, 100, 500)cube3.name = 'cube3'this.scene.add(cube3)// 生成管道this.generatePipe(1)this.generatePipe(2)// 生成数据显示面板this.generateDataPanel(1, 0, 400, 0)this.generateDataPanel(2, 1000, 400, -500)this.generateDataPanel(3, -1000, 400, 500)this.generateDataPanel(4, 100, 0, 120)this.generateDataPanel(5, 1000, 0, -400)this.generateDataPanel(6, -1000, 100, 700)// 出现数据显示面板for (var i = 1; i <= 3; i++) {$('#canvas' + i).css('display', 'block')}$('#btns').css('display', 'block')},//高亮显示模型(呼吸灯)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 = 5.0 // 高光边缘边框的亮度this.outlinePass.edgeGlow = 1 // 光晕[0,1] 边缘微光强度this.outlinePass.usePatternTexture = false // 是否使用父级的材质,纹理覆盖this.outlinePass.edgeThickness = 1 // 边框宽度,高光厚度this.outlinePass.downSampleRatio = 1 // 边框弯曲度this.outlinePass.pulsePeriod = 3 // 呼吸闪烁的速度,数值越大,律动越慢this.outlinePass.visibleEdgeColor.set(parseInt(0xff800)) // 呼吸显示的颜色this.outlinePass.hiddenEdgeColor = new THREE.Color(0, 0, 0) // 呼吸消失的颜色// this.outlinePass.clear = poser.addPass(this.outlinePass) // 加入高光特效this.outlinePass.selectedObjects = selectedObjects // 需要高光的模型},// 鼠标点击模型onMouseClick(event) {//通过鼠标点击的位置计算出raycaster所需要的点的位置,以屏幕中心为原点,值的范围为-1到1this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1// 通过鼠标点的位置和当前相机的矩阵计算出raycasterthis.raycaster.setFromCamera(this.mouse, this.camera)// 获取raycaster直线和所有模型相交的数组集合var intersects = this.raycaster.intersectObjects(this.scene.children)if (intersects[0].object.geometry instanceof THREE.PlaneGeometry) {return} else {this.selectedObjects = []this.selectedObjects.push(intersects[0].object)this.outlineObj(this.selectedObjects)if (intersects[0].object.name === 'cube1') {this.initTween(0, 850, 1000)$('#canvas4').css('display', 'block')} else if (intersects[0].object.name === 'cube2') {this.initTween(1500, 500, -1000)$('#canvas5').css('display', 'block')} else if (intersects[0].object.name === 'cube3') {this.initTween(-1400, 400, 1000)$('#canvas6').css('display', 'block')}}},// 相机移动动画initTween(targetX, targetY, targetZ) {// 需要保留thisvar initPosition = {x: this.camera.position.x,y: this.camera.position.y,z: this.camera.position.z,}var tween = new TWEEN.Tween(initPosition).to({x: targetX, y: targetY, z: targetZ }, 2000).easing(TWEEN.Easing.Sinusoidal.InOut)var onUpdate = (pos) => {var x = pos.xvar y = pos.yvar z = pos.zthis.camera.position.set(x, y, z)}tween.onUpdate(onUpdate)tween.start()this.controls.target.set(0, 0, 0)this.cssControls.target.set(0, 0, 0)},// 生成数据显示面板generateDataPanel(id, x, y, z) {// CSS3DObject实现3D卡片var dataplane = document.getElementById('canvas' + id)this.dataPanel['plane' + id] = new CSS3DObject(dataplane)this.dataPanel['plane' + id].scale.set(1.2, 1.2, 1.2)this.dataPanel['plane' + id].position.set(x, y, z)this.cssScene.add(this.dataPanel['plane' + id])},// 生成管道generatePipe(id) {var curvevar tubeGeometryvar materialif (id === 1) {curve = new THREE.CatmullRomCurve3([new THREE.Vector3(0, 200, 0),new THREE.Vector3(0, 200, -500),new THREE.Vector3(10, 200, -500),new THREE.Vector3(20, 200, -500),new THREE.Vector3(1000, 200, -500),])tubeGeometry = new THREE.TubeGeometry(curve, 80, 10)this.flowingLineTexture1 = new THREE.TextureLoader().load('static/arrow.png')this.flowingLineTexture1.wrapS = this.flowingLineTexture1.wrapT =THREE.RepeatWrappingthis.flowingLineTexture1.repeat.set(10, 1)this.flowingLineTexture1.needsUpdate = truematerial = new THREE.MeshBasicMaterial({map: this.flowingLineTexture1,side: THREE.DoubleSide,transparent: true,})} else if (id === 2) {curve = new THREE.CatmullRomCurve3([new THREE.Vector3(0, 200, 0),new THREE.Vector3(0, 200, 500),new THREE.Vector3(-10, 200, 500),new THREE.Vector3(-500, 200, 500),new THREE.Vector3(-800, 200, 500),])tubeGeometry = new THREE.TubeGeometry(curve, 80, 10)this.flowingLineTexture2 = new THREE.TextureLoader().load('static/arrow.png')this.flowingLineTexture2.wrapS = this.flowingLineTexture2.wrapT =THREE.RepeatWrappingthis.flowingLineTexture2.repeat.set(10, 1)this.flowingLineTexture2.needsUpdate = truematerial = new THREE.MeshBasicMaterial({map: this.flowingLineTexture2,side: THREE.DoubleSide,transparent: true,})}let tube = new THREE.Mesh(tubeGeometry, material)this.scene.add(tube)},// 视角改变toView(id) {if (id === 1) {this.initTween(0, 1000, 2900)} else if (id === 2) {this.initTween(0, 4000, 0)}},// 设备详情页面跳转deviceDetail(id) {console.log(id)},onWindowResize() {this.cssRender.setSize(window.innerWidth, window.innerHeight)this.renderer.setSize(window.innerWidth, window.innerHeight)this.camera.aspect = window.innerWidth / window.innerHeightthis.camera.updateProjectionMatrix()},initControls() {this.cssControls = new OrbitControls(this.camera,this.cssRender.domElement)// //动态阻尼系数 即鼠标拖拽旋转的灵敏度// this.cssControls.dampingFactor = 0.25// this.cssControls.target.set(0, 900, 0)// //摄像机距离原点的距离// this.cssControls.minDistance = 1// this.cssControls.maxDistance = 20000// //上下旋转范围this.cssControls.minPolarAngle = 0this.cssControls.maxPolarAngle = 1.5// //左右旋转范围// this.cssControls.minAzimuthAngle = -Math.PI * 2// this.cssControls.maxAzimuthAngle = Math.PI * 2//是否开启右键拖拽this.cssControls.enabledPan = falsethis.controls = new OrbitControls(this.camera, this.renderer.domElement)this.controls.dampingFactor = 0.25// // //缩放倍数this.controls.zoomSpeed = 1.0this.controls.minDistance = 1this.controls.maxDistance = 20000this.minPolarAngle = 0this.maxPolarAngle = 1.5this.minAzimuthAngle = -Math.PI * 2this.maxAzimuthAngle = Math.PI * 2this.controls.enabledPan = false},animate() {this.render()this.controls.update()this.cssControls.update()if (poser) {poser.render()}requestAnimationFrame(this.animate)},render() {// 数据显示面板的旋转角度与相机的旋转角度一致this.dataPanel.plane1.rotation.copy(this.camera.rotation)this.dataPanel.plane1.updateMatrix()this.dataPanel.plane2.rotation.copy(this.camera.rotation)this.dataPanel.plane2.updateMatrix()this.dataPanel.plane3.rotation.copy(this.camera.rotation)this.dataPanel.plane3.updateMatrix()this.dataPanel.plane4.rotation.copy(this.camera.rotation)this.dataPanel.plane4.updateMatrix()this.dataPanel.plane5.rotation.copy(this.camera.rotation)this.dataPanel.plane5.updateMatrix()this.dataPanel.plane6.rotation.copy(this.camera.rotation)this.dataPanel.plane6.updateMatrix()// 管道流动,更新管道纹理的偏移量// 管道流动速度var speed=0.01this.flowingLineTexture1.offset.x+=speedthis.flowingLineTexture2.offset.x+=speed// 一定要激活TWEEN.update()this.renderer.render(this.scene, this.camera)this.cssRender.render(this.cssScene, this.camera)},draw() {this.initScene()this.initCamera()this.initLight()this.initRender()this.initModel()this.initControls()this.animate()window.onresize = this.onWindowResizewindow.onclick = this.onMouseClick},},}</script><style scoped lang="scss">.about {width: 100%;height: 100%;#model {width: 100%;height: 100%;position: relative;overflow: hidden;#canvas1,#canvas2,#canvas3 {width: 150px;height: 200px;// background-color: rgba(8, 29, 54, 0.5);background-color: aquamarine;border-radius: 3px;// opacity: 0.5;color: red;font-size: 20px;}#canvas4,#canvas5,#canvas6{span{display: block;width: 100px;height: 40px;background-color: cornflowerblue;color: #fff;position: absolute;text-align: center;line-height: 40px;cursor: pointer;}}#btns {position: absolute;bottom: 0px;width: 100%;padding: 10px;text-align: center;z-index: 9999;span {width: 100px;display: inline-block;height: 30px;background-color: red;margin-right: 20px;cursor: pointer;}}}}</style>

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