Vue+Three.js可視化實戰
theme: geek-black
實戰目的
根據不同的產品配合接口展示相應的描述。根據選擇的場景及其物品實現可視化的產品展示效果。效果展示
支持不同位置展示不同描述:配合數據配置渲染不同楨的效果
根據選中的產品,切換相應產品效果
根據選中場景,切換相應的場景
實現思路
封裝一個Three的函數,支持設置相機、場景、渲染函數,添加模型解析器,添加物品,整合渲染效果,添加事件監聽,完善模型動畫展示
具體實現
使用vite搭建一個項目,後安裝Three支持,進行具體實現
準備vue項目
- 使用Vite + Vue 搭建 ```bash
npm 6.x
npm init [email protected] my-vue-app --template vue
npm 7+, 需要額外的雙橫線:
npm init [email protected] my-vue-app -- --template vue
yarn
yarn create vite my-vue-app --template vue
pnpm
pnpm create vite my-vue-app -- --template vue
- 根據自己的環境選擇自己的搭建代碼
npm init [email protected] my-vue-app -- --template vue
```
- 根據提示創建項目
- 確認項目正常訪問
安裝 Three
npm install --save three
刪除無用代碼,添加渲染節點
增加一個場景展示的div,用於渲染3D信息
Three 實戰
加載場景及控制器
初始化場景HDR圖片
// 初始化場景
initScene() {
this.scene = new THREE.Scene();
this.setEnvMap("001");
}
// 場景設置
setEnvMap(hdr) {
new RGBELoader().setPath("./hdr/").load(`${hdr}.hdr`, (texture) => {
texture.mapping = THREE.EquirectangularRefractionMapping;
this.scene.background = texture;
this.scene.environment = texture;
});
}
確定相機位置
initCamera() {
this.camera = new THREE.PerspectiveCamera(
45, // 角度
window.innerWidth / window.innerHeight, // 比例
0.25, // 近
200 // 遠
);
// 相機位置
this.camera.position.set(-1.8, 0.6, 2.7);
}
渲染:根據相機位置和場景圖渲染初始畫面
js
render() {
this.renderer.render(this.scene, this.camera);
}
動畫:渲染初始畫面
js
animate() {
this.renderer.setAnimationLoop(this.render.bind(this));
}
此時,這個頁面只能展示出部分的靜態畫面,要想通過鼠標控制相機的位置,則需要增加控制器
引入控制器
// 控制器
initControls() {
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
}
加入控制器後,則可以通過鼠標的滑動控制相機的角度
增加產品模型
引入模型解析器
js
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
添加模型到場景裏
js
setModel(modelName) {
const loader = new GLTFLoader().setPath("/gltf/");
loader.load(modelName, (gltf) => {
this.model = gltf.scene.children[0];
this.scene.add(gltf.scene);
});
}
// 添加模型
async addMesh() {
let res = await this.setModel("bag2.glb");
}
模型已經加入到場景裏,但是模型不在場景中間🤔️,模型比較亮,真實物品看不清楚
打印一下模型解析後的數據,我們可以看到模型有自己的相機場景動畫等信息,我們可以把當前相應的設置調整成模型的設置
模型調整
調整相機為模型相機
js
setModel(modelName) {
...
// 修改相機為模型相機
this.camera = gltf.cameras[0];
...
}
調整後模型位置在畫面中間
調整場景其他配置
```js // 設置模型 setModel(modelName) { return new Promise((resolve, reject) => { const loader = new GLTFLoader().setPath("./gltf/"); loader.load(modelName, (gltf) => { console.log(gltf); this.model && this.model.removeFromParent(); this.model = gltf.scene.children[0]; if (modelName === "bag2.glb" && !this.dish) { this.dish = gltf.scene.children[5]; // 修改相機為模型相機 this.camera = gltf.cameras[0]; // 調用動畫 this.mixer = new THREE.AnimationMixer(gltf.scene.children[1]); this.animateAction = this.mixer.clipAction(gltf.animations[0]); // 設置動畫播放時長 this.animateAction.setDuration(20).setLoop(THREE.LoopOnce); // 設置播放後停止 this.animateAction.clampWhenFinished = true; // 設置燈光 this.spotlight1 = gltf.scene.children[2].children[0]; this.spotlight1.intensity = 1; this.spotlight2 = gltf.scene.children[3].children[0]; this.spotlight2.intensity = 1; this.spotlight3 = gltf.scene.children[4].children[0]; this.spotlight3.intensity = 1;
// this.scene.add(this.dish);
}
this.scene.add(gltf.scene);
resolve(`${this.modelName}模型添加成功`);
});
});
} ```
調整參數後的產品展示效果
定時器和滾輪監聽動畫
```js // 添加定時器 render() { var delta = this.clock.getDelta(); this.mixer && this.mixer.update(delta); this.renderer.render(this.scene, this.camera); }
// 監聽滾輪事件 window.addEventListener("mousewheel", this.onMouseWheel.bind(this));
// 監聽滾輪事件 onMouseWheel(e) { let timeScale = e.deltaY > 0 ? 1 : -1; this.animateAction.setEffectiveTimeScale(timeScale); this.animateAction.paused = false; this.animateAction.play(); if (this.timeoutId) { clearTimeout(this.timeoutId); } this.timeoutId = setTimeout(() => { this.animateAction.halt(0.3); }, 300); } ```
場景和產品模型都添加成功,結合動畫,可以進行產品的預覽
添加窗口監聽事件
調整頁面窗口時,保證場景的全屏展示
// 監聽場景大小改變,調整渲染尺寸 window.addEventListener("resize", this.onWindowResize.bind(this)); // 監聽尺寸 onWindowResize() { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(window.innerWidth, window.innerHeight); }
優化加載
模型加載成功後在展示
js constructor(selector, onFinish) { this.onFinish = onFinish; } // 添加物品增加回調函數 async addMesh() { let res = await this.setModel("bag2.glb"); this.onFinish(res); }
增加商品的介紹
根據duration和time 計算當前處於哪部門節點
js
window.addEventListener("mousewheel", (e) => {
let duration = data.base3d.animateAction._clip.duration;
let time = data.base3d.animateAction.time;
let index = Math.floor((time / duration) * 4);
data.descIndex = index;
});
增加選擇場景和產品
創建數據增加操作的dom
```js
```
增加操作事件
js
function changeModel(prod, pI) {
data.pIndex = pI;
data.base3d.setModel(prod.modelName);
}
function changeHdr(scene, index) {
data.sceneIndex = index;
data.base3d.setEnvMap(scene);
}
大功吿成
支持不同位置展示不同描述:配合數據配置渲染不同楨的效果
根據選中的產品,切換相應產品效果
根據選中場景,切換相應的場景
總結
- 通過類的方式創建的方法,能夠很好的保存了創建過程中3D模型的所具備的屬性和功能,在實例化後,可以很便捷的獲取到相應的屬性
- 在創建場景/模型時,可以根據要突出的效果調整相應的參數,我們可以認真觀察創建出來的實例對象中包含的屬性和方法,方便我們渲染使用
參考包/支持
- Vite中文網 腳手架工具
- Three.js 參考文檔
- 本項目參考視頻【老陳打碼 WEB 3D系統體系課程-Three.js可視化企業實戰WEBGL課】