vue2+three.js实现类似VR、3D全景效果

效果图:
在这里插入图片描述
俩图标是我自己加的前进后退按钮,也是百度了好久,再加上GPT的帮助,才给搞出来。因为需求急,都不看官方文档,百度到一个能跑的demo之后改吧改吧,就先用着了。

下面是代码:

<template>
    <div>
        <div id="container" ref="container"></div>
    </div>
  </template>
  
  <script>
    import * as THREE from 'three';
    import TWEEN from '@tweenjs/tween.js'
    import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"
    export default {
      data () {
          return {
            sceneIndex:0,
            door:null,
              bigImg: require("../../../assets/555.jpg"), // 图片路径
              container: null, // 页面容器
              camera: null, // 相机
              renderer: null, // 渲染器
              scene: null, // 场景
              material: null,  // 添加材质
              texture: null,// 创建纹理贴图
              skyBox: null, // 网格
              controls: null, // 轨道控制
              clock: null, // 轨道更新时间
              // 鼠标属性
              bMouseDown: false,
              x: -1,
              y: -1,
              isClickCamera: false, // 是否点运动相机
              raycaster: null,
              mouse: null,
              poiObjects:[],
              scenesList:[],//场景列表
              ImageList:[
                '111.jpg',
                '222.jpg',
                '333.jpg',
                '444.jpg',
                '555.jpg',
              ],//场景图片了列表
          }
      },
      mounted () {
          this.$nextTick(() => {
              this.init();
              this.animate();
          })
      },
      created () {
      },
      methods: {
          // 初始化轨道控制
          initControls() {
              this.controls = new OrbitControls(this.camera, this.renderer.domElement);
              this.controls.target = new THREE.Vector3(0, 0, 0);
              this.controls.minDistance = 18; // 相机最近
              this.controls.maxDistance = 500; // 相机最远
              this.controls.autoRotate = false; // 图片自动旋转
              this.controls.enableDamping = false; // 使动画循环使用时阻尼或自转 意思是否有惯性
              this.controls.enablePan = false; // 是否开启右键拖拽
              this.controls.autoRotateSpeed = 0.5; // 阻尼系数
          },
          init() {
              // 页面容器
              this.container = document.getElementById('container');
  
              // 创建渲染器
              this.renderer = new THREE.WebGLRenderer();
              this.renderer.setPixelRatio(window.devicePixelRatio);
  
              // 设置画布的宽高
              this.renderer.setSize(window.innerWidth, window.innerHeight);
  
              // 判断容器中子元素的长度判断当前容器元素中是否已经包含了canvas元素,如果已经有了,则删除原来的canvas元素,添加新的canvas元素;如果没有,则直接添加canvas元素。
              let childs = this.container.childNodes;
              if (this.container.childNodes.length > 0) {
                  this.container.removeChild(childs[0]);
                  this.container.appendChild(this.renderer.domElement);
              } else {
                  this.container.appendChild(this.renderer.domElement);
              }
              // 创建场景
              this.scene = new THREE.Scene();
  
              // 创建相机
              this.camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 1, 10000);
              this.camera.position.set(5, 0, 0);
              this.camera.lookAt(new THREE.Vector3(1, 0, 0)); //让相机指向原点 
  
              // 创建轨道控制器
              this.initControls();
  
              // 添加材质
              this.material = new THREE.MeshBasicMaterial();
              // 创建纹理贴图
              this.texture = new THREE.TextureLoader().load(this.bigImg);
              this.material.map = this.texture;
  
              // 创建网格对象
              this.skyBox = new THREE.Mesh(new THREE.SphereBufferGeometry(100, 100, 100), this.material);
              this.skyBox.geometry.scale(1, 1, -1);
              // 显示坐标光线
            //   var axisHelper = new THREE.AxisHelper(600); // 显示光线(红色代表X轴,绿色代表Y轴,蓝色代表Z轴)
                  // 添加到场景中去
            //   this.scene.add(axisHelper);
              this.scene.add(this.skyBox);

            //   添加一个箭头
            //   const arrow1 = new THREE.ArrowHelper(new THREE.Vector3(1, 0, 0), new THREE.Vector3(10, 0, 0), 5, 0xff0000);
            //   arrow1.name = 'arrow1';
            //   this.scene.add(arrow1);
          
              // 鼠标事件监听
              this.renderer.domElement.addEventListener('pointerdown', this.onMouseDown, false);
              this.renderer.domElement.addEventListener('pointerup', this.onMouseUp, false);
              this.renderer.domElement.addEventListener('pointermove', this.onMouseMove, false);
  
              // 监听布局变化
              window.addEventListener('resize', this.onWindowResize, false);
              this.createMoreImage(0)
             
          },
           // 更新相机动画
          tweenCamera(position, target) {
              new TWEEN.Tween(this.camera.position).to({
                  x: position.x,
                  y: position.y,
                  z: position.z
              }, 600).easing(TWEEN.Easing.Sinusoidal.InOut).start();
  
              new TWEEN.Tween(this.controls.target).to({
                  x: target.x,
                  y: target.y,
                  z: target.z
              }, 600).easing(TWEEN.Easing.Sinusoidal.InOut).start();
          },
          // 鼠标按下
          onMouseDown(event) {
              event.preventDefault(); // 取消默认事件
              console.log("---onMouseDown---");
            
              var raycaster = new THREE.Raycaster();
              var 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 );
                     
              var intersects = raycaster.intersectObjects( this.scene.children,true);
              if(intersects.length>0){
                console.log(intersects);
                let name = intersects[0].object.name
                if (!name) {
                  return
                }
                if (name == '前进') {
                  this.sceneIndex +=1;
                  if (this.sceneIndex > this.ImageList.length - 1) {
                    this.sceneIndex = this.ImageList.length - 1
                    return
                  }
                }else if (name == '后退') {
                  this.sceneIndex--
                  if (this.sceneIndex < 0) {
                    this.sceneIndex = 0
                  }
                }
                this.createMoreImage(this.sceneIndex)
                this.render.render(this.scene,this.camera)
                this.camera.position.set(5,0,0)
                
              }

              this.isClickCamera = true;
          },
          // 鼠标放开
          onMouseUp(event) {
              event.preventDefault(); // 取消默认事件
              console.log("---onMouseUp---");
            
          },
          // 鼠标移动
          onMouseMove(event) {
              event.preventDefault(); // 取消默认事件
              console.log("---onMouseMove---");
              this.isClickCamera = false;
          },
          onWindowResize() {
              // 窗口缩放的时候,保证场景也跟着一起缩放
              this.camera.aspect = window.innerWidth / window.innerHeight;
              this.camera.updateProjectionMatrix();
              this.renderer.setSize(window.innerWidth, window.innerHeight);
          },
          animate() {
              requestAnimationFrame(this.animate);
              this.controls.update(); // 更新轨道控制
              TWEEN.update();
              this.renderer.render(this.scene, this.camera);
          },
        //   循环创建场景
        createMoreImage(index){
          let scene = new THREE.Scene();
          scene.name = index
          let texture = new THREE.TextureLoader().load(require('../../../assets/' + this.ImageList[index]));
          let material = new THREE.MeshBasicMaterial();
          material.map = texture;

          let skyBox = new THREE.Mesh(new THREE.SphereBufferGeometry(100, 100, 100), material);
          skyBox.geometry.scale(1, 1, -1);
          scene.add(skyBox)

          var hotPoints=[
                {
                    position:{
                        x:-98,
                        y:13,
                        z:10
                    },
                    detail:{
                        "title":"前进"
                    }
                },
                {
                    position:{
                        x:-58,
                        y:-37,
                        z:70
                    },
                    detail:{
                        "title":"后退"
                    }
                }
            ];
            var pointTexture = new THREE.TextureLoader().load(require('../../../assets/6.png'));
            var material1 = new THREE.SpriteMaterial( { map: pointTexture,fog: true,color: 0xffffff,} );
            for(var i=0;i<hotPoints.length;i++){
                var sprite = new THREE.Sprite( material1 );
                sprite.scale.set( 10,10,10);
                sprite.position.set( hotPoints[i].position.x, hotPoints[i].position.y, hotPoints[i].position.z );
                sprite.name = hotPoints[i].detail.title
                sprite.detail = hotPoints[i].detail;
                sprite.material.depthTest = false;
                sprite.renderOrder = i;
                sprite.center = new THREE.Vector2(0.5, 0.5); // 设置锚点为精灵对象中心
                this.poiObjects.push(sprite);
            
                scene.add( sprite );
            }
            this.scene = scene

            // 播放相机缓慢移动的动画
            function animateCamera(targetPos, targetRot, duration) {
              var startPos = camera.position.clone();
              var startRot = camera.rotation.clone();
              var startTime = Date.now();

              function animate() {
                var now = Date.now();
                var timeElapsed = now - startTime;
                var progress = timeElapsed / duration;
            
                if (progress >= 1.0) {
                  camera.position.copy(targetPos);
                  camera.rotation.copy(targetRot);
                  return;
                }
            
                var easedProgress = Math.sin(progress * Math.PI / 2);
            
                camera.position.lerpVectors(startPos, targetPos, easedProgress);
                camera.rotation.slerpQuaternions(startRot, targetRot, easedProgress);
            
                requestAnimationFrame(animate);
              }
          
              animate();
            }

        },
      }
    }
  </script>

这里

这里 tween.js 应该是动画库,切换场景动画,或者视角转动动画等,我好像没用上,

其他没啥好说的,我也不咋会,反正能跑起来,有啥不会问GPT吧。

然会这里面还有一个坑就是,要把body和html的margin和padding变成0 ,不然检测不到点击添加的那俩图标,
他好像是根据点击页面的i像素,来换算成坐标的,有padding或者margin就换算的不准确了,我排查了好久。。。。

实在不行就把调试窗口给单独拎出来

代码有很多用不到的地方和需要优化的地方,我是来不及改了,就先这样吧。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐