在UniApp中集成Three.js:打造跨平臺3D可視化應用
引言
在最近的一個項目中,我們需要在UniApp應用中展示3D模型,并實現實時交互功能。經過技術選型和實踐,我們選擇了Three.js作為3D渲染引擎。本文將分享我們在UniApp中集成Three.js的完整過程,以及在鴻蒙系統上的適配經驗。
技術棧選擇
我們的技術棧組合如下:
- UniApp + Vue3:提供跨平臺開發能力
- Three.js r150:3D渲染引擎
- Stats.js:性能監控
- GLTF Loader:3D模型加載
- HMS Core Graphics:鴻蒙圖形加速
環境搭建
首先,我們需要在UniApp項目中安裝必要的依賴:
# 安裝Three.js
npm install three@0.150.0# 安裝類型聲明(如果使用TypeScript)
npm install @types/three --save-dev# 安裝加載器和控制器
npm install three-orbit-controls
npm install three-gltf-loader
核心實現
1. 基礎場景搭建
首先創建一個基礎的3D場景組件:
<!-- components/ThreeScene.vue -->
<template><view class="three-container"><canvas type="webgl" id="threejs-canvas"@touchstart="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd"></canvas></view>
</template><script lang="ts">
import { defineComponent, onMounted, onBeforeUnmount } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';export default defineComponent({name: 'ThreeScene',setup() {let scene: THREE.Scene;let camera: THREE.PerspectiveCamera;let renderer: THREE.WebGLRenderer;let controls: OrbitControls;let canvas: any;let animationFrameId: number;// 初始化場景const initScene = () => {// 創建場景scene = new THREE.Scene();scene.background = new THREE.Color(0xf0f0f0);// 創建相機camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000);camera.position.set(0, 5, 10);// 獲取canvas上下文const query = uni.createSelectorQuery();query.select('#threejs-canvas').node().exec((res) => {canvas = res[0].node;// 初始化渲染器renderer = new THREE.WebGLRenderer({canvas,antialias: true,alpha: true});// 適配設備像素比const pixelRatio = uni.getSystemInfoSync().pixelRatio;renderer.setPixelRatio(pixelRatio);// 設置渲染尺寸const { windowWidth, windowHeight } = uni.getSystemInfoSync();renderer.setSize(windowWidth, windowHeight);// 初始化控制器controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true;// 添加光源addLights();// 添加示例模型addSampleModel();// 開始動畫循環animate();});};// 添加光源const addLights = () => {const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);scene.add(ambientLight);const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);directionalLight.position.set(10, 10, 10);scene.add(directionalLight);};// 添加示例模型const addSampleModel = () => {// 創建一個簡單的立方體const geometry = new THREE.BoxGeometry(2, 2, 2);const material = new THREE.MeshStandardMaterial({color: 0x00ff00,metalness: 0.5,roughness: 0.5});const cube = new THREE.Mesh(geometry, material);scene.add(cube);};// 動畫循環const animate = () => {animationFrameId = requestAnimationFrame(animate);// 更新控制器controls.update();// 渲染場景renderer.render(scene, camera);};// 觸摸事件處理const handleTouchStart = (event: any) => {const touch = event.touches[0];controls.onTouchStart(touch);};const handleTouchMove = (event: any) => {const touch = event.touches[0];controls.onTouchMove(touch);};const handleTouchEnd = () => {controls.onTouchEnd();};// 生命周期鉤子onMounted(() => {initScene();});onBeforeUnmount(() => {cancelAnimationFrame(animationFrameId);renderer?.dispose();});return {handleTouchStart,handleTouchMove,handleTouchEnd};}
});
</script><style>
.three-container {width: 100%;height: 100vh;
}canvas {width: 100%;height: 100%;
}
</style>
2. GLTF模型加載器
對于復雜的3D模型,我們通常使用GLTF格式。以下是模型加載的實現:
// utils/modelLoader.ts
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import type { GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
import type { Group } from 'three';export class ModelLoader {private loader: GLTFLoader;constructor() {this.loader = new GLTFLoader();}async loadModel(url: string): Promise<Group> {try {const gltf: GLTF = await new Promise((resolve, reject) => {this.loader.load(url,resolve,(xhr) => {console.log((xhr.loaded / xhr.total * 100) + '% loaded');},reject);});const model = gltf.scene;// 自動計算包圍盒model.traverse((child: any) => {if (child.isMesh) {child.castShadow = true;child.receiveShadow = true;}});return model;} catch (error) {console.error('模型加載失敗:', error);throw error;}}
}
3. 鴻蒙系統適配
在鴻蒙系統上,我們需要特別注意以下幾點:
// platform/harmony/graphicsOptimizer.ts
export class HarmonyGraphicsOptimizer {private graphicsAPI: any;constructor() {if (uni.getSystemInfoSync().platform === 'harmony') {this.graphicsAPI = uni.requireNativePlugin('graphics');}}optimize(renderer: THREE.WebGLRenderer) {if (!this.graphicsAPI) return;try {// 啟用硬件加速this.graphicsAPI.enableHardwareAcceleration();// 設置最佳性能模式renderer.setPixelRatio(1); // 在鴻蒙系統上固定像素比renderer.powerPreference = 'high-performance';// 啟用自定義幀率控制this.graphicsAPI.setPreferredFrameRate(60);} catch (error) {console.warn('鴻蒙圖形優化失敗:', error);}}
}
性能優化
在實際應用中,我們采取了以下優化措施:
- 模型優化
- 使用LOD(Level of Detail)技術
- 壓縮紋理資源
- 實現模型預加載
- 渲染優化
- 使用實例化渲染
- 實現視錐體剔除
- 優化光照計算
- 內存管理
- 及時釋放資源
- 實現資源池
- 控制最大內存使用
實戰案例:產品展示
以下是一個實際的產品3D展示組件:
<!-- components/ProductViewer.vue -->
<template><view class="product-viewer"><three-scene ref="threeScene" /><view class="controls"><button @tap="rotateModel">旋轉</button><button @tap="zoomIn">放大</button><button @tap="zoomOut">縮小</button></view></view>
</template><script lang="ts">
import { defineComponent, ref } from 'vue';
import ThreeScene from './ThreeScene.vue';
import { ModelLoader } from '@/utils/modelLoader';
import type { Group } from 'three';export default defineComponent({components: {ThreeScene},setup() {const threeScene = ref(null);let productModel: Group | null = null;const loadProductModel = async () => {const loader = new ModelLoader();try {productModel = await loader.loadModel('/static/models/product.gltf');threeScene.value?.addToScene(productModel);} catch (error) {uni.showToast({title: '模型加載失敗',icon: 'none'});}};const rotateModel = () => {if (!productModel) return;productModel.rotation.y += Math.PI / 2;};const zoomIn = () => {threeScene.value?.zoomCamera(1.2);};const zoomOut = () => {threeScene.value?.zoomCamera(0.8);};return {threeScene,rotateModel,zoomIn,zoomOut};}
});
</script>
常見問題與解決方案
在開發過程中,我們遇到了一些典型問題,這里分享解決方案:
- 內存泄漏
- 問題:長時間使用后內存占用過高
- 解決:實現完整的資源釋放機制,包括幾何體、材質、紋理等
- 觸摸控制
- 問題:多點觸控不流暢
- 解決:優化事件處理邏輯,實現事件節流
- 性能問題
- 問題:在低端設備上幀率不穩定
- 解決:實現自適應渲染質量,動態調整分辨率和細節級別
未來展望
隨著WebGL和Three.js的發展,以及鴻蒙生態的完善,我們期待在以下方面有更多突破:
- 技術升級
- 支持WebGPU
- 優化渲染管線
- 提升AR/VR體驗
- 功能擴展
- 支持物理仿真
- 添加后期處理
- 優化交互體驗
總結
通過在UniApp中集成Three.js,我們不僅實現了跨平臺的3D展示功能,還在鴻蒙系統適配方面積累了寶貴經驗。希望本文的實踐分享能為大家在類似項目開發中提供參考和啟發。
記住,3D應用開發是一個需要不斷優化和改進的過程,建議在實際項目中根據具體需求和設備特點進行針對性優化。同時,隨著技術的發展,也要及時更新知識儲備,保持對新技術的跟進和學習。