<template> <!-- 项目模型实景管理 --> <div class="projectModalM"> <div id="QuanJing" ref="threeDom" v-if="ifHasUrl" v-loading="modalLoading"></div> <!-- 暂无数据 --> <div v-else class="noData">暂无全景图片</div> </div> </template> <script setup> import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; //控制器 import gsap from 'gsap'; import { CSSRulePlugin } from 'gsap/CSSRulePlugin'; import bus from '@/bus'; const { proxy } = getCurrentInstance(); gsap.registerPlugin(CSSRulePlugin); // 引入css插件,完成某些css动画 const AllData = ref({ show: false, numer: '0%', imgUrl: '', //渲染的目标图片 imgWidth: 1200, //渲染宽度px imgHeight: 560, //渲染高度px }); const ifHasUrl = ref(true); const modalLoading = ref(true); // 初始化模型 let SceneModal = new THREE.Scene(); const CameraModal = new THREE.PerspectiveCamera(30, AllData.value.imgWidth / AllData.value.imgHeight, 0.1, 5000); const Renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, //开启alpha }); // 控制器 const Controls = new OrbitControls(CameraModal, Renderer.domElement); // 注意:此处为threejs的DOM,需要将threejs的场景渲染进去 const threeDom = ref(); // 首页进入相机的视角,这个视角可以在三维模型中建立一个摄像机获取摄像机的坐标,如C4D,非常准确. const cameraPosition = { x: 500, y: 400, z: -200, }; const cameraLookat = { x: 0, y: 0, z: 0, }; // 声明一个方法传入参数可以在不同的地方调用相机 const cameraReset = (position, lookAt, time = 1) => { gsap.to(CameraModal.position, { x: position.x, y: position.y, z: position.z, duration: time, ease: 'power4.out', // onComplete: function () { // 这是相机运动完成的回调,可以执行其他的方法. // } }); gsap.to(CameraModal.lookAt, { x: lookAt.x, y: lookAt.y, z: lookAt.z, duration: time, ease: 'power4.out', }); gsap.to(CameraModal.target, { x: lookAt.x, y: lookAt.y, z: lookAt.z, duration: time, ease: 'power4.out', }); }; const initThreeScene = () => { // 使动画循环使用时阻尼或自转 意思是否有惯性 Controls.enableDamping = true; // 动态阻尼系数 就是鼠标拖拽旋转灵敏度 Controls.dampingFactor = 0.1; // 是否可以旋转 Controls.enableRotate = true; // 是否可以缩放与速度 Controls.enableZoom = true; // 设置相机距离原点的最远距离 // Controls.minDistance = 1; // 设置相机距离原点的最远距离 Controls.maxDistance = 900; Controls.autoRotate = true; Controls.autoRotateSpeed = 1.0; // 是否开启右键拖拽 Controls.enablePan = false; // Controls.addEventListener("change", render); //render的相关设置 // Renderer.setPixelRatio(window.devicePixelRatio); Renderer.setSize(AllData.value.imgWidth, AllData.value.imgHeight); Renderer.inputEncoding = true; // Renderer.outputEncoding = THREE.sRGBEncoding; // Renderer.setClearColor(0xd0d0d0, 1); // 将renderer渲染进DOM里面 threeDom.value.appendChild(Renderer.domElement); }; // 设置页面自适应 const onWindowResize = () => { CameraModal.aspect = AllData.value.imgWidth / AllData.value.imgHeight; CameraModal.updateProjectionMatrix(); Renderer.setSize(AllData.value.imgWidth, AllData.value.imgHeight); }; window.addEventListener('resize', onWindowResize, false); // 完成以上步骤基本的场景已经配置完成 cameraReset(cameraPosition, cameraLookat); const Render = () => { requestAnimationFrame(Render); Controls.update(); // 轨道控制器的更新 Renderer.clear(); // 清除画布 Renderer.render(SceneModal, CameraModal); }; // 初始化加载模型 function initModal(imgUrl) { modalLoading.value = true; let event = {}; // 单张纹理图的加载 event.onLoad = function () { console.log('图片加载完成'); AllData.value.show = false; modalLoading.value = false; }; event.onProgress = function (url, num, total) { console.log('图片加载完成:', url); console.log('图片加载进度:', num); console.log('图片总数:', total); let value = ((num / total) * 100).toFixed(2) + '%'; console.log('加载进度的百分比:', value); AllData.value.numer = value; modalLoading.value = false; }; event.onError = function (e) { console.log('图片加载出现错误'); console.log(e); AllData.value.show = false; modalLoading.value = false; }; // 设置加载管理器 const loadingManager = new THREE.LoadingManager(event.onLoad, event.onProgress, event.onError); const textureLoader = new THREE.TextureLoader(loadingManager); var material = new THREE.MeshBasicMaterial(); //材质 var texture = textureLoader.load(imgUrl); material.map = texture; // var skyBox = new THREE.Mesh(new THREE.SphereBufferGeometry(600, 600, 600), material); var skyBox = new THREE.Mesh(new THREE.SphereGeometry(600, 600, 600), material); skyBox.geometry.scale(1, 1, -1); SceneModal.add(skyBox); } onMounted(() => { bus.on('getProjectDate', v => { ifHasUrl.value = v.projectpicture.length > 0 ? true : false; if (ifHasUrl.value) { initModal(v.projectpicture[0].url); //初始化 initThreeScene(); Render(); } }); }); onBeforeUnmount(() => { SceneModal.remove(skyBox); //删除组 SceneModal = null; }); </script> <style lang="scss" scoped> .projectModalM { #QuanJing { width: 100%; height: 100%; } .noData { font-size: 20px; text-align: center; margin-top: 10%; color: #fff; } } </style>