這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

最近幾天在學習three.js ,因為我相信只有實踐才能出真理,搗鼓搗鼓做了一個簡易的全景圖,這里主要是分享做這個vue版全景圖中遇到的問題,有些代碼可能與其他做過全景圖的大佬有些相似畢竟原因都差不多 ??
本文屬于技術總結類的文章
將介紹在 vue中如何安裝并使用 three.js 以及一些配套插件 , 使用three.js 實作全景圖的原理 , vue打包后圖片顯示的問題 ,及在32位谷歌49版本的瀏覽器無法使用three.js等問題,至于如何安裝 Node服務這里就不再贅述了
在 vue 中安裝 three.js 以及配套插件
npm 安裝 three.js npm install three 然后在對應頁面上將three的功能模塊全部匯入進來
three.js - npm地址
<script> import * as THREE from "three"; ... </script>
npm 安裝 OrbitControls.js 操作三維場景插件 npm install three-orbit-controls 在引入插件時必須保證three被成功引入否則頁面會報錯,如果你不想通過npm下載 , 其實在 npm three 的時候已經下載對應的插件, 在 node_modules 檔案夾下找到 three/examples/jsm/controls/OrbitControls 這個路徑里面也能找到對應的插件, 通過下面注釋里面的形式也能匯入,但是不推薦這樣匯入因為在谷歌32位49版本的瀏覽器中這樣匯入控制器是無法使用的
<script>
import * as THREE from "three";
const OrbitControls = require('three-orbit-controls')(THREE);
//import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls'
...
</script>
npm 安裝.obj 和.mtl 檔案的插件 npm i --save three-obj-mtl-loader 加載 .obj 模型檔案 , .mtl 材質資訊檔案這里我就不過多贅述了想要試試的小伙伴可以看看 郭隆邦老師的電子書指南-第14小節 , 還有fbx模型檔案 ,也就是除了包含幾何、材質資訊,還可以存盤骨骼影片等資料的模型檔案 ,可以通過 npm i three-fbx-loader進行安裝
說到這些材質檔案的匯入我要忍不住吐槽兩句 .stl格式 , obj檔案 大多數按照官方的匯入方法來做是沒有問題的 , 但.fbx格式就很特殊 , 之前我在網上下載的比較多的fbx格式的模型和對應的材質但大多數都用不了 ,我真的是裂開了,
后面到處查原因,總結了一下就是插件的兼容性不好 網上下載的fbx影片大多數都是用不了的,有版本問題、也有檔案本身的問題 后面找到一篇大佬的開荒文章 THREE.js中加載不同格式的模型及影片(fbx、json和obj) 上面寫的很詳細 ,有問題的同學可以去看看 ??
<script>
import * as THREE from "three";
import {OBJLoader,MTLLoader} from 'three-obj-mtl-loader';
const OrbitControls = require('three-orbit-controls')(THREE);
...
</script>
npm 安裝性能檢測插件 , npm i three-stats 主要作用就是 主要用于檢測影片運行時的幀數
<script>
import * as THREE from "three";
import {OBJLoader,MTLLoader} from 'three-obj-mtl-loader';
const OrbitControls = require('three-orbit-controls')(THREE);
import * as ThreeStats from 'three-stats'
...
</script>
文章到這里當前專案的組態檔就已經介紹完畢了,后面我就會開始介紹一些three.js的最基本的原理以及全景圖的實作方式 ,完整的代碼我會貼到文章的最下方
three.js 的基本原理 (渲染器-renderer, 場景-scene,相機-camera)
這里只對原理進行簡單的講述,想要詳細了解的同學請進 郭隆邦老師的電子書指南

舉個栗子 ,假如我是一名導演, 我已經準備了最好了演員 ,還請了島國一流的拍攝團隊 , 最后物色一塊風水寶地 ,準備拍一部讓人熱血沸騰的青春偶像動作片 ,一戰成名 , 然后走向人生巔峰 ??
- 渲染器-renderer 就好比剛剛物色的那塊風水寶地 ,我什么都準備好了總要找個合適的地方進行拍攝嘛 ,這里就是通過渲染器來創建一個自定義大小的拍攝地點 渲染器中文檔案
new THREE.WebGLRenderer(); //創建渲染器
<template>
<div ref='threeDom'></div>
</template>
<script>
rendererInit(){ //初始化渲染器
var width = 1000; //視窗寬度 window.innerWidth 瀏覽器視窗可視區寬度(不包括瀏覽器控制臺、選單欄、工具列)包含滾條
var height = 800; //視窗高度 window.innerHeight
this.renderer = new THREE.WebGLRenderer(); //創建渲染器
this.renderer.setClearColor(0xffffff); //添加背景顏色
this.renderer.setSize(width, height); // 設定渲染器尺寸
this.$refs.threeDom.appendChild(this.renderer.domElement); //通過 this.$refs獲取頁面的dom將場景初始化上去
},
</script>
- 場景-scene 就是你拍攝地點找好了,但里面什么都沒有一片漆黑伸手不見五指 ,作為導演的我們是不是應該把光源裝上去在把演員請進來呢 (這里光源就代表-環境光 , 演員-就代表創建好的模型) 不然我們這個導演就當的不合格,那還怎么走向人生巔峰啊
sceneInit(){ //初始化場景 并向場景添加光源和輔助坐標系
this.scene = new THREE.Scene(); //初始化場景
var ambient = new THREE.AmbientLight(0x444444, 3); //添加光源 顏色和光照強度
var axisHelper = new THREE.AxesHelper(600); //添加輔助坐標系 引數位輔助坐標系的長度
this.scene.add(ambient, axisHelper); //向場景中添加光源 和 輔助坐標系
},
modelling(){ //開始建立模型
this.mygroup = new THREE.Group(); //建立一個分組
var textureLoader = new THREE.TextureLoader(); //創建紋理貼圖
var img = textureLoader.load(require('../../public/img/qjt.jpeg'));
var geometry = new THREE.SphereGeometry(130, 256, 256); // 球體網格模型
var material = new THREE.MeshLambertMaterial({
map: img, //設定顏色貼圖屬性值
side: THREE.DoubleSide, //雙面渲染
});
var meshSphere = new THREE.Mesh(geometry, material); //網格模型物件Mesh
meshSphere.name = '球體容器';
this.mygroup.add(meshSphere);
this.scene.add(this.mygroup);
...
}
- 相機-camera 相機顧名思義就是拍攝用的道具 , 相機的視角也就是我們最侄訓面呈現的視角 ,這里我們使用透視相機因為透視相機的視角更貼近真實人眼看的視角,透視相機具體引數可以看 透視相機中文檔案
cameraInit() { //初始化相機
var width = 800; //視窗寬度
var height = 800; //視窗高度
this.camera = new THREE.PerspectiveCamera(90, width / height, 1, 1000); //使用透視相機
this.camera.position.set(0, 0, 10); //設定相機位置
this.camera.lookAt(new THREE.Vector3(0, 0, 0)); // 相機看向
},
-
開始實作簡易的全景圖
終于到這里了現在正式開搞 ?? , 先通過上面介紹的基礎原理把 渲染器-renderer, 場景-scene,相機-camera , 弄出來 , 然后全景圖實作原理是,首先在坐標軸的中心創建一個,帶圖片紋理的小球 當前這里不一定要用球體 ,其他形狀也是可以實作的 , 具體根據使用場景來定義

首先創建一個球體網格模型和對應的紋理貼圖
建立球體模型以及使用 TextureLoader 生成紋理貼圖 - 紋理貼圖中文檔案 紋理貼圖默認渲染模式為 THREE.FrontSide 前面渲染 , 設定配置的時候需要注意一下
modelling(){ //開始建立模型
this.mygroup = new THREE.Group();
var textureLoader = new THREE.TextureLoader(); //創建紋理貼圖
var img = textureLoader.load(require('../../public/img/home3.jpeg'));
var geometry = new THREE.SphereGeometry(130, 256, 256); // 球體網格模型
var material = new THREE.MeshLambertMaterial({
map: img, //設定顏色貼圖屬性值
side: THREE.DoubleSide, //雙面渲染
});
var meshSphere = new THREE.Mesh(geometry, material); //網格模型物件Mesh
meshSphere.name = '球體容器';
this.mygroup.add(meshSphere);
this.scene.add(this.mygroup);
},
},
建立矩形平面自定義文字 three.js中自定義文字的方式大概分為以下幾種
| 形成文字的方式 | 實作方案 | 優點 | 缺點 |
|---|---|---|---|
| DOM + CSS | 一般的實作方式使用絕對定位和足夠大的z-index讓組件或者文字在3D圖形的上方 | 實作簡單效果強大 | 3d效果和物體聯動性差 |
| THREE.CanvasTexture | 在canvas中繪制文字,然后使用CanvasTexture作為紋理進行貼圖 | 文字效果較為豐富 | 一旦生成,解析度固定,放大會產生失真 |
| THREE.TextGeometry | 使用原生的TextGeometry進行渲染生成 | 效果好,可與場景進行同步 | 字體的顏色和影片制作較為復雜,特別耗費資源 |
| 3d字體模型 | 使用3d制作的字體模型,使用threejs進行加載控制 | 效果好,可定制效果 | 加載模型耗費資源,字體內容無法自定義 |
| 位圖字體 | 通過BmpFont生成文字模板,然后進行加載顯示 | 可自定義字體和效果 | 加載模型耗費資源,字體內容無法自定義 |
| Three.Sprite精靈材質 | Sprite加載影像紋理 | 永遠面向相機的平面,適合作為標簽顯示 | 一旦生成,解析度固定,放大會產生失真 |

這里我選擇的是canvas繪制文字 , 至于為什么,就是因為不用匯入圖片,并且自定義文字比較方便
modelling(){ //開始建立模型
this.mygroup = new THREE.Group();
var canvasText = this.getcanvers('進門'); //生成一個canvers 文字圖案物件
var texture = new THREE.CanvasTexture(canvasText);
var geometryText = new THREE.PlaneGeometry(16, 10, 60, 60); //生成一個平面模型
var materialText = new THREE.MeshPhongMaterial({
map: texture, // 設定紋理貼圖
side: THREE.DoubleSide, //雙面渲染
});
var meshText = new THREE.Mesh(geometryText, materialText);
meshText.name = '進門';
meshText.position.set(40, 20, -90)
this.mygroup.add(meshText);
this.scene.add(this.mygroup);
},
getcanvers(text) { //生成一個canvers圖案
var canvasText = document.createElement("canvas");
var c = canvasText.getContext('2d');
// 矩形區域填充背景
c.fillStyle = "#FFFFFF"; //canver背景
c.fillRect(0, 0, 300, 200); //生成一個矩形
c.translate(160, 80);
c.fillStyle = "#000000"; //文本填充顏色
c.font = "bold 100px 宋體"; //字體樣式設定
c.textBaseline = "middle"; //文本與
c.textAlign = "center"; //文本居中
c.fillText(text, 0, 0);
var texture = new THREE.CanvasTexture(canvasText); //Canvas紋理
var geometryText = new THREE.PlaneGeometry(16, 10, 60, 60); //生成一個矩形平面
var materialText = new THREE.MeshPhongMaterial({
map: texture, // 設定紋理貼圖
side: THREE.DoubleSide, //雙面渲染
});
var meshText = new THREE.Mesh(geometryText, materialText);
meshText.name = text;
meshText.position.set(40, 20, -90);
return canvasText;
},
},
通過點擊矩形平面切換場景
在一般的 HTML 中觸發點擊事件只需要給對應的dom系結事件即可 , 但是在three.js 里面就行不通 , 因為three生成的圖形頁面其實就是一張canvas畫布無法直接取到對應的dom , 更不用說了給dom系結事件了 ,不過好在three.js 提供了一個 new THREE.Raycaster() 光線投射 (用于拾取滑鼠的位置以及在三維空間中計算出滑鼠移過了什么物體)

射線會記錄與之相交幾何體,并以陣列的形式從近到遠回傳對應模型的mesh ,只需要向射線中傳入滑鼠的位置和當前相機即可,這樣我們就可以根據模型的名稱獲取當前點擊的那個模型并觸發對應的事件
init(){
this.$refs.threeDom.addEventListener('dblclick', this.onMouseDblclick); //監聽雙擊事件
},
onm ouseDblclick(event){ //觸發雙擊事件
// 獲取 raycaster 和所有模型相交的陣列,其中的元素按照距離排序,越近的越靠前
var intersects = this.getIntersects(event);
...
},
getIntersects(event) { // 獲取與射線相交的物件陣列
event.preventDefault();
// 宣告 raycaster 和 mouse 變數
var raycaster = new THREE.Raycaster(); //生成射線
var mouse = new THREE.Vector2();
var container = this.$refs.threeDom;
let getBoundingClientRect = container.getBoundingClientRect();
// 通過滑鼠點擊位置,計算出 raycaster 所需點的位置 分量,以螢屏為中心點,范圍 -1 到 1
mouse.x = ((event.clientX - getBoundingClientRect.left) / container.offsetWidth) * 2 - 1;
mouse.y = -((event.clientY - getBoundingClientRect.top) / container.offsetHeight) * 2 + 1;
//通過滑鼠點擊的位置(二維坐標)和當前相機的矩陣計算出射線位置
raycaster.setFromCamera(mouse, this.camera);
// 獲取與射線相交的物件陣列,其中的元素按照距離排序,越近的越靠前
var intersects = raycaster.intersectObjects(this.scene.children[2].children);
//回傳選中的物件
return intersects;
},
定義相機的位置
我們需要將透視投影相機放在球體的中心模擬人在在房間里面的位置 ,調整相機位置和相機看向即可

cameraInit() { //初始化相機
var width = 800; //視窗寬度
var height = 800; //視窗高度
this.camera = new THREE.PerspectiveCamera(90, width / height, 1, 1000); //使用透視相機
this.camera.position.set(0, 0, 10); //設定相機位置
this.camera.lookAt(new THREE.Vector3(0, 0, 0)); // 相機看向
},
初始化控制器
控制器也就是我們最開始引入的 OrbitControls.js 操作三維場景插件 , OrbitControls 的重繪機制是當控制器監聽到頁面改變時不停的高頻率執行重新渲染的操作動態改變頁面
controlInit(){ //初始化控制器
this.controls = new OrbitControls(this.camera, this.$refs.threeDom); // 初始化控制器
this.controls.target.set(0, 0, 0); // 設定控制器的焦點,使控制器圍繞這個焦點進行旋轉
this.controls.minDistance = 10; // 設定移動的最短距離(默認為零)
this.controls.maxPolarAngle = Math.PI; //繞垂直軌道的距離(范圍是0-Math.PI,默認為Math.PI)
this.controls.maxDistance = 30; // 設定移動的最長距離(默認為無窮)
this.controls.enablePan = false; //禁用右鍵功能
this.controls.addEventListener('change', this.refresh); //監聽滑鼠、鍵盤事件 讓整個控制元件可以拖動
},
refresh(){ //重繪頁面
this.renderer.render(this.scene, this.camera); //執行渲染操作
this.stats.update(); //更新性能監控的值
},
定義可控制的自動旋轉影片
上面幾個步驟做完后,全景圖功能差不多都實作了 , 但是頁面不會自動旋轉總感覺少了點意思 ,現在就給這個專案加上自動旋轉的功能同時能根據按鈕來停止和開啟自動旋轉 , 實作方案時通過three.js 準備好的 new THREE.KeyframeTrack() 定義關鍵幀 , new THREE.AnimationClip() 剪輯keyframe物件 , new THREE.AnimationMixer() 影片混合實體
想要詳細了解一下影片基本原理的小伙伴可以看下大佬寫的這篇文章 Three.js - KeyframeTrack 幀影片
addAnimation(){ //添加并開啟影片
this.clock = new THREE.Clock(); // three.js 時鐘物件
var times = [0, 3600]; // 創建幀影片序列
var position_x = [0, 360]; //離散屬性值
var keyframe = new THREE.KeyframeTrack('meshSphere.rotation[y]', times, position_x);
var duration = 100; //持續時間
var cilp = new THREE.AnimationClip('sphereRotate', duration, [keyframe]); //剪輯 keyframe物件
this.mixer = new THREE.AnimationMixer(this.mygroup); //影片混合實體
this.action = this.mixer.clipAction(cilp);
this.action.timeScale = 1; //播放速度
this.action.setLoop(THREE.LoopPingPong).play(); //開始播放 像乒乓球一樣在起始點與結束點之間來回回圈
this.animate(); //開啟影片
},
animate() { //回圈渲染
this.rotateAnimate = requestAnimationFrame(this.animate);
this.renderer.render(this.scene, this.camera);
this.update();
},
全景圖完整代碼
<template>
<div >
<el-card >
<div slot="header">
<div >
<span>簡易版全景圖</span>
<div >
<span ref='property'></span>
</div>
</div>
</div>
<div >
<div ref='threeDom' ></div>
<div >
<span >控制臺</span>
<div >
<span >是否自動旋轉</span>
<el-radio-group v-model="isRotate" @change="isSpin">
<el-radio :label="1">開啟</el-radio>
<el-radio :label="0">關閉</el-radio>
</el-radio-group>
</div>
</div>
</div>
</el-card>
</div>
</template>
<script>
import axios from 'axios';
import * as THREE from "three";
import * as TrackballControls from 'three-trackballcontrols'
import * as ThreeStats from 'three-stats'
import { OBJLoader, MTLLoader } from 'three-obj-mtl-loader';
const OrbitControls = require('three-orbit-controls')(THREE);
export default {
props: {
msg: String
},
data() {
return {
renderer: '', //渲染器
scene: '', //場景
light: '', //光源
camera: '', //相機
controls: '', //控制器
stats: '', //性能監控器
mygroup: '', //模型組
action: '', //控制影片的值
clock: '', //時鐘
mixer: '', //混合實體
rotateAnimate: '', //旋轉影片
isRotate: 1, //是否開啟旋轉
}
},
mounted() {
this.init(); //初始化
},
methods: {
init() {
this.$refs.threeDom.addEventListener('dblclick', this.onMouseDblclick); //監聽雙擊事件
this.rendererInit(); //創建渲染器
this.sceneInit(); //創建場景 包含光源和輔助坐標系
this.cameraInit(); //創建相機
this.controlInit(); //初始化控制器
this.propertyInit(); //性能監控
this.modelling(); //建立模型
},
modelling(){ //開始建立模型
this.mygroup = new THREE.Group();
var textureLoader = new THREE.TextureLoader(); //創建紋理貼圖
var img = textureLoader.load(require('../../public/img/home3.jpeg'));
var geometry = new THREE.SphereGeometry(130, 256, 256); // 球體網格模型
var material = new THREE.MeshLambertMaterial({
map: img, //設定顏色貼圖屬性值
side: THREE.DoubleSide, //雙面渲染
});
var meshSphere = new THREE.Mesh(geometry, material); //網格模型物件Mesh
meshSphere.name = '球體容器';
this.mygroup.add(meshSphere);
var canvasText = this.getcanvers('進門'); //生成一個canvers 文字圖案物件
var texture = new THREE.CanvasTexture(canvasText);
var geometryText = new THREE.PlaneGeometry(16, 10, 60, 60);
var materialText = new THREE.MeshPhongMaterial({
map: texture, // 設定紋理貼圖
side: THREE.DoubleSide, //雙面渲染
});
var meshText = new THREE.Mesh(geometryText, materialText);
meshText.name = '進門';
meshText.position.set(40, 20, -90)
this.mygroup.add(meshText);
this.scene.add(this.mygroup);
this.addAnimation(); //添加并開啟影片
this.refresh();
},
isSpin(val) { //開啟和關閉旋轉
if (val == 0) { //關閉控制臺
this.action.paused = true;
} else {
this.action.paused = false;
}
},
addAnimation() { //添加并開啟影片
this.clock = new THREE.Clock(); // three.js 時鐘物件
var times = [0, 3600]; // 創建幀影片序列
var position_x = [0, 360]; //離散屬性值
var keyframe = new THREE.KeyframeTrack('meshSphere.rotation[y]', times, position_x);
var duration = 100; //持續時間
var cilp = new THREE.AnimationClip('sphereRotate', duration, [keyframe]); //剪輯 keyframe物件
this.mixer = new THREE.AnimationMixer(this.mygroup); //影片混合實體
this.action = this.mixer.clipAction(cilp);
this.action.timeScale = 1; //播放速度
this.action.setLoop(THREE.LoopPingPong).play(); //開始播放 像乒乓球一樣在起始點與結束點之間來回回圈
this.animate(); //開啟影片
},
animate() { //回圈渲染
this.rotateAnimate = requestAnimationFrame(this.animate);
this.renderer.render(this.scene, this.camera);
this.update();
},
update() { //資料更新
this.stats.update();
this.mixer.update(this.clock.getDelta());
},
rendererInit() { //初始化渲染器
var width = 1000; //視窗寬度
var height = 800; //視窗高度
this.renderer = new THREE.WebGLRenderer(); //創建渲染器
this.renderer.setClearColor(0xffffff); //添加背景顏色
this.renderer.setSize(width, height); // 設定渲染器尺寸
this.$refs.threeDom.appendChild(this.renderer.domElement);
},
sceneInit() { //初始化場景 并向場景添加光源和輔助坐標系
this.scene = new THREE.Scene();
var ambient = new THREE.AmbientLight(0x444444, 3); //添加光源 顏色和光照強度
var axisHelper = new THREE.AxesHelper(600); //添加輔助坐標系
this.scene.add(ambient, axisHelper);
},
cameraInit() { //初始化相機
var width = 800; //視窗寬度
var height = 800; //視窗高度
this.camera = new THREE.PerspectiveCamera(90, width / height, 1, 1000); //使用透視相機
this.camera.position.set(0, 0, 10); //設定相機位置
this.camera.lookAt(new THREE.Vector3(0, 0, 0)); // 相機看向
},
controlInit() { //初始化控制器
this.controls = new OrbitControls(this.camera, this.$refs.threeDom); // 初始化控制器
this.controls.target.set(0, 0, 0); // 設定控制器的焦點,使控制器圍繞這個焦點進行旋轉
this.controls.minDistance = 10; // 設定移動的最短距離(默認為零)
this.controls.maxPolarAngle = Math.PI; //繞垂直軌道的距離(范圍是0-Math.PI,默認為Math.PI)
this.controls.maxDistance = 30; // 設定移動的最長距離(默認為無窮)
this.controls.enablePan = false; //禁用右鍵功能
this.controls.addEventListener('change', this.refresh); //監聽滑鼠、鍵盤事件 讓整個控制元件可以拖動
},
propertyInit() { //初始化性能監控
this.stats = new ThreeStats.Stats(); // 創建一個性能監視器
this.stats.dom.style.position = 'absolute';
this.stats.dom.style.top = '-4px';
this.$refs.property.appendChild(this.stats.dom);
this.stats.update();
},
getcanvers(text) { //生成一個canvers圖案
var canvasText = document.createElement("canvas");
var c = canvasText.getContext('2d');
// 矩形區域填充背景
c.fillStyle = "#FFFFFF"; //canver背景
c.fillRect(0, 0, 300, 200); //生成一個矩形
c.translate(160, 80);
c.fillStyle = "#000000"; //文本填充顏色
c.font = "bold 100px 宋體"; //字體樣式設定
c.textBaseline = "middle"; //文本與
c.textAlign = "center"; //文本居中
c.fillText(text, 0, 0);
var texture = new THREE.CanvasTexture(canvasText); //Canvas紋理
var geometryText = new THREE.PlaneGeometry(16, 10, 60, 60); //生成一個矩形平面
var materialText = new THREE.MeshPhongMaterial({
map: texture, // 設定紋理貼圖
side: THREE.DoubleSide, //雙面渲染
});
var meshText = new THREE.Mesh(geometryText, materialText);
meshText.name = text;
meshText.position.set(40, 20, -90);
return canvasText;
},
refresh(){ //重繪頁面
this.renderer.render(this.scene, this.camera); //執行渲染操作
this.stats.update(); //更新性能監控的值
},
onMouseDblclick(event) { //觸發雙擊事件
// 獲取 raycaster 和所有模型相交的陣列,其中的元素按照距離排序,越近的越靠前
var intersects = this.getIntersects(event);
if (intersects.length != 0) {
for (var item of intersects) {
if (item.object.name != '') { //找到第一個不等于空的模型 就是自定義最近的模型
this.action.paused = true; //停止旋轉
this.$confirm('是否切換場景?', '提示', {
confirmButtonText: '切換',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.action.paused = false; //開啟旋轉
if (item.object.name == '進門') {
this.changeScene('enter'); //改變頁面場景
} else if (item.object.name == '回傳') {
this.changeScene('backtrack'); //改變頁面場景
}
}).catch(() => {
this.action.paused = false; //開啟旋轉
});
break;
}
}
} else { //這里是未選中狀態
}
},
changeScene(type) {
var img = '';
var names = '';
var canvasText = '';
var textureLoader = new THREE.TextureLoader(); //創建紋理貼圖
if (type == 'enter') {
img = textureLoader.load(require('../../public/img/home1.jpg')); //vue加載圖表需要用 require形式
canvasText = this.getcanvers('回傳'); //生成一個canvers 文字圖案物件
names = '回傳';
} else if (type == 'backtrack') { //回傳房間
img = textureLoader.load(require('../../public/img/home3.jpeg')); //vue加載圖表需要用 require形式
canvasText = this.getcanvers('進門'); //生成一個canvers 文字圖案物件
names = '進門';
}
for (var item of this.scene.children[2].children) {
if (item.name == '球體容器') { //切換貼圖 進入下一張貼圖
var material = new THREE.MeshLambertMaterial({
map: img, //設定顏色貼圖屬性值
side: THREE.DoubleSide, //雙面渲染
});
item.material = material;
} else if (item.name == '進門' || item.name == '回傳') {
var texture = new THREE.CanvasTexture(canvasText);
var materialText = new THREE.MeshPhongMaterial({
map: texture, // 設定紋理貼圖
side: THREE.DoubleSide, //雙面渲染
});
item.name = names; //改名模型的名字
item.material = materialText;
}
}
setTimeout(() => { //延遲重繪
this.refresh();
}, 100)
},
getIntersects(event) { // 獲取與射線相交的物件陣列
event.preventDefault();
// 宣告 raycaster 和 mouse 變數
var raycaster = new THREE.Raycaster(); //生成射線
var mouse = new THREE.Vector2();
var container = this.$refs.threeDom;
let getBoundingClientRect = container.getBoundingClientRect();
// 通過滑鼠點擊位置,計算出 raycaster 所需點的位置 分量,以螢屏為中心點,范圍 -1 到 1
mouse.x = ((event.clientX - getBoundingClientRect.left) / container.offsetWidth) * 2 - 1;
mouse.y = -((event.clientY - getBoundingClientRect.top) / container.offsetHeight) * 2 + 1;
//通過滑鼠點擊的位置(二維坐標)和當前相機的矩陣計算出射線位置
raycaster.setFromCamera(mouse, this.camera);
// 獲取與射線相交的物件陣列,其中的元素按照距離排序,越近的越靠前
var intersects = raycaster.intersectObjects(this.scene.children[2].children);
//回傳選中的物件
return intersects;
},
}
}
</script>
<style>
.homePage {
position: absolute;
height: 100%;
width: 100%;
font-size: 14px;
color: #303133;
display: flex;
align-items: center;
justify-content: center;
}
.card {
width: 1300px;
height: 900px;
}
.card-title {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-title span {
font-weight: 600;
font-size: 18px;
}
.card-property {
position: relative;
width: 70px;
height: 40px;
}
.card-content {
display: flex;
flex-direction: row;
}
.model {
border: 1px solid #DCDFE6;
}
.control {
display: flex;
flex-direction: column;
width: 300px;
height: 800px;
border: 1px solid #DCDFE6;
border-left: none;
}
.control-title {
font-size: 18px;
font-weight: 600;
text-align: center;
color: #409EFF;
padding: 10px;
border-bottom: 1px solid #DCDFE6;
}
.control-block {
padding: 10px;
border-bottom: 1px solid #DCDFE6;
}
.control-block-title {
display: block;
margin-bottom: 5px;
}
/* 自定義element樣式 */
.el-card__header {
padding: 10px 20px;
}
</style>
構建這個全景圖時遇到的問題
-
vue中直接放入圖片失敗
因為我們使用的是node.js啟動的前端服務所以引入本地圖片需要使用 require('../../public/img/home3.jpeg') 形式進行引入 ,直接使用地址圖片是不會顯示的
-
vue 打包后圖片不顯示


使用 require('') 打包后node把檔案協議改為 file:// 形式的協議用于訪問本地打包后的圖片 , 然而 textureLoader.load(); 只接收 http:// 形式的檔案所以打包后圖片無法顯示 , 這里只需要把自己的圖片放在tomcat服務器上 , 在取自己tomcat服務器上的圖片就可以了
-
vue構建的專案放在谷歌32位49版本的瀏覽器中無法打開的問題
為什么我老是會提起谷歌低版本畢竟現在一般人都不會使用低版本了 , 但我這邊的客戶還是有一小部分群體在使用 XP系統 我們雖然不能給他做到兼容 IE , 但至少谷歌低版本要給別人弄好 !
具體是什么原因我也不是特別清楚 , 大概就是低版本瀏覽器獲取值的方式和高版本不怎么一樣,解決方案就是在 node_modules\three\build 找到 three.module.js

注釋掉 this.setSession 這個獲取方法

還沒完還得找到 node_modules\three-trackballcontrols 中的 index.js 檔案并注釋掉 const getMouseOnScreen 以及 const getMouseOnCircle 這兩個方法

至于為什么要注釋這幾個方法我也沒有特地去研究 ,對于低版本我的理念就是能解決問題就OK了! 如果有知道具體解決方案的大佬可以在留言里面告訴我一下, 阿里嘎多

本文轉載于:
https://juejin.cn/post/6927193628724953096
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/503135.html
標籤:其他
