鸿蒙OS&在UniApp中集成Three.js:打造跨平台3D可视化应用#三方框架 #Uniapp

发布于:2025-06-03 ⋅ 阅读:(23) ⋅ 点赞:(0)

在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);
    }
  }
}

性能优化

在实际应用中,我们采取了以下优化措施:

  1. 模型优化
  • 使用LOD(Level of Detail)技术
  • 压缩纹理资源
  • 实现模型预加载
  1. 渲染优化
  • 使用实例化渲染
  • 实现视锥体剔除
  • 优化光照计算
  1. 内存管理
  • 及时释放资源
  • 实现资源池
  • 控制最大内存使用

实战案例:产品展示

以下是一个实际的产品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>

常见问题与解决方案

在开发过程中,我们遇到了一些典型问题,这里分享解决方案:

  1. 内存泄漏
  • 问题:长时间使用后内存占用过高
  • 解决:实现完整的资源释放机制,包括几何体、材质、纹理等
  1. 触摸控制
  • 问题:多点触控不流畅
  • 解决:优化事件处理逻辑,实现事件节流
  1. 性能问题
  • 问题:在低端设备上帧率不稳定
  • 解决:实现自适应渲染质量,动态调整分辨率和细节级别

未来展望

随着WebGL和Three.js的发展,以及鸿蒙生态的完善,我们期待在以下方面有更多突破:

  1. 技术升级
  • 支持WebGPU
  • 优化渲染管线
  • 提升AR/VR体验
  1. 功能扩展
  • 支持物理仿真
  • 添加后期处理
  • 优化交互体验

总结

通过在UniApp中集成Three.js,我们不仅实现了跨平台的3D展示功能,还在鸿蒙系统适配方面积累了宝贵经验。希望本文的实践分享能为大家在类似项目开发中提供参考和启发。

记住,3D应用开发是一个需要不断优化和改进的过程,建议在实际项目中根据具体需求和设备特点进行针对性优化。同时,随着技术的发展,也要及时更新知识储备,保持对新技术的跟进和学习。


网站公告

今日签到

点亮在社区的每一天
去签到