《HarmonyOS 7 ArkGraphics 3D 空间设计开发实战》08:动画、生命周期与SpaceRoom工程化收尾【鸿蒙心迹】

最后不堆新功能,把整个工程收回来。

前言

七篇做下来,SpaceRoom 已经有:场景、模型、坐标、材质、灯光、相机交互、拾取选择、性能优化。一个能看能点的 3D 空间设计器差不多齐了。

最后一篇不加新功能,做工程收尾:补动画、理生命周期、把模块拆清楚。很多 Demo 看着能跑,真要做成工程就发现到处都是耦合:Scene 和 UI 绑死了,模型加载和页面生命周期搅在一起,退出页面以后资源还在内存里。

这一篇就把这些问题收拾干净,让 SpaceRoom 从一个 Demo 变成一个结构清楚的工程。


一、先补动画:柜门、抽屉、灯光渐变

先加几个简单动画,让场景活起来:

typescript 复制代码
import { scene } from '@kit.ArkGraphics3DKit';

export class AnimationController {
  private animations: Map<string, scene.Animation> = new Map();

  /**
   * 播放柜门开启动画
   */
  playDoorOpen(doorNode: scene.Node): void {
    // 从0度转到90度,500ms
    const anim = doorNode.createComponent(scene.NodeComponentType.ANIMATION) as scene.Animation;
    anim.duration = 500;
    anim.from = {rotation: {x: 0, y: 0, z: 0}};
    anim.to = {rotation: {x: 0, y: 90, z: 0}};
    anim.easing = 'easeOut';
    anim.play();

    this.animations.set('door', anim);
    console.info('AnimationController: door opened');
  }

  /**
   * 播放灯光渐变
   */
  playLightFade(lightNode: scene.Node, targetIntensity: number): void {
    const light = lightNode.getComponent(scene.NodeComponentType.LIGHT) as scene.Light;
    const anim = lightNode.createComponent(scene.NodeComponentType.ANIMATION) as scene.Animation;
    anim.duration = 800;
    anim.from = {intensity: light.intensity};
    anim.to = {intensity: targetIntensity};
    anim.easing = 'easeInOut';
    anim.play();
  }

  /**
   * 停止所有动画
   */
  stopAll(): void {
    this.animations.forEach(anim => {
      anim.stop();
    });
    this.animations.clear();
  }
}

动画本身不复杂,关键是动画的生命周期:页面退到后台的时候要暂停,回到前台的时候继续;页面销毁的时候要停掉所有动画。


二、生命周期:页面退了以后,谁来清理

这是工程里最容易出问题的地方。3D 对象的生命周期和 ArkUI 页面的生命周期不一样。

页面走了,Scene 可能还在后台跑动画、跑渲染,白白耗电。

写个统一的入口:

typescript 复制代码
@Entry
@Component
struct SpaceRoomPage {
  private sceneMgr: SceneManager = new SceneManager();
  private cameraCtrl: CameraController | null = null;
  private modelMgr: ModelResourceManager | null = null;
  private lightMgr: LightManager | null = null;
  private pickingMgr: PickingManager | null = null;
  private animCtrl: AnimationController | null = null;
  private perfMonitor: ScenePerformanceMonitor | null = null;

  // 页面出现:初始化所有模块
  onPageShow(): void {
    this.initScene();
    this.animCtrl?.resumeAll();
  }

  // 页面隐藏:暂停动画,不要后台跑
  onPageHide(): void {
    this.animCtrl?.pauseAll();
    this.perfMonitor?.stop();
  }

  // 页面销毁:释放所有资源
  aboutToDisappear(): void {
    // 1. 停动画
    this.animCtrl?.stopAll();

    // 2. 释放模型资源
    this.modelMgr?.releaseAll();

    // 3. 释放场景
    this.sceneMgr.release();

    console.info('SpaceRoomPage: all resources released');
  }

  private initScene(): void {
    // 按顺序初始化
    this.sceneMgr.setupScene();
    this.cameraCtrl = new CameraController(this.sceneMgr.getScene()!);
    this.cameraCtrl.setupCamera();

    this.lightMgr = new LightManager(this.sceneMgr.getScene()!);
    this.lightMgr.setupSceneLights(this.sceneMgr.getScene()!);

    this.modelMgr = new ModelResourceManager(this.sceneMgr.getScene()!);
    this.pickingMgr = new PickingManager(this.sceneMgr.getScene()!);
    this.animCtrl = new AnimationController();
    this.perfMonitor = new ScenePerformanceMonitor();
  }

  build() {
    Stack() {
      scene.SceneView({ scene: this.sceneMgr.getScene() ?? undefined })
        .width('100%')
        .height('100%')
    }
    .width('100%')
    .height('100%')
  }
}

关键是三个生命周期方法:

  • onPageShow:页面出来,恢复动画和监控
  • onPageHide:页面隐藏,暂停动画,不要后台空跑
  • aboutToDisappear:页面销毁,把所有资源释放干净

很多人只做了 onAppear 初始化,没做 aboutToDisappear 释放,结果就是用户进出几次页面,内存涨好几倍。


三、最后把模块理清楚

回顾一下整个工程的模块划分:

模块 负责什么 不负责什么
SceneManager Scene 生命周期,创建和释放 不管模型、不管灯光
CameraController 相机位置、旋转、缩放 不管手势识别
ModelResourceManager 模型加载、缓存、引用计数 不管模型放哪
LightManager 灯光设置、氛围预设 不管材质
MaterialManager 材质和纹理 不管灯光
PickingManager 点击拾取、选中状态 不管 UI 展示
AnimationController 动画播放和暂停 不管动画内容
PerformanceMonitor 帧率和统计 不管优化策略
SpaceRoomPage 组装所有模块、接 UI 不写具体 3D 逻辑

这个划分的原则是:每个模块只做一件事。以后要加新功能,比如加个截图功能,就新建一个 ScreenshotManager,不要往 Page 里堆。


四、整个系列回顾

八篇走下来,技术线是这样的:

  1. 01 基础场景:Scene、Camera、Light、Node 创建成功 ≠ 能看见
  2. 02 坐标体系:世界坐标和局部坐标,父子节点 Transform 叠加
  3. 03 模型加载:Resource 和 Instance 分开,引用计数管生命周期
  4. 04 材质灯光:质感是材质和灯光配合出来的,不是模型好看就行
  5. 05 相机交互:二维手势映射成三维相机旋转,轨道相机模型
  6. 06 3D 拾取:从屏幕点反推射线,找到命中的物体
  7. 07 性能优化:先测数据再优化,视锥裁剪、共享材质、减少每帧计算
  8. 08 工程收尾:动画、生命周期、模块划分

整个过程里踩的坑,其实都不是 API 不会用,是 3D 开发的思维方式和 ArkUI 不一样:

  • ArkUI 里一个组件就是一个对象,3D 里 Resource 和 Instance 是分开的
  • ArkUI 里位置就是相对于父容器,3D 里要考虑世界坐标和局部坐标
  • ArkUI 里页面销毁就完了,3D 里还有 GPU 资源、纹理、模型要释放

这些概念理清楚了,后面做更复杂的 3D 应用就不会乱。


总结

第八篇是整个系列的收尾:

  1. 动画要管生命周期:后台暂停,销毁停掉
  2. 页面销毁时必须释放所有资源,不然内存越积越多
  3. 模块按职责拆分,每个模块只做一件事
  4. 3D 开发的思维和 ArkUI 不一样,核心是理解"渲染资源"和"业务对象"是分开的

SpaceRoom 到这里就完整了。从一个黑屏的空场景,到一个能转视角、能选家具、有材质有灯光的 3D 房间设计器。后面如果要继续做,可以加模型导入、保存分享、多人协作这些功能,但基础的 3D 开发链路已经跑通了。

相关推荐
轻口味2 小时前
HarmonyOS 7 新特性2:音频编创——轻音台里的降噪、环绕与格式转换
华为·音视频·harmonyos·鸿蒙·音频编创
m0_738185822 小时前
Flutter 鸿蒙化实战:flutter_blue_plus 适配 OpenHarmony,蓝牙扫描连接开箱即用
flutter·华为·harmonyos·鸿蒙
李游Leo2 小时前
HarmonyOS 7 + ArkUI + Adaptive Layout 学习笔记:折叠屏多形态布局适配与窗口状态响应机制【鸿蒙心迹】
笔记·学习·harmonyos
贾伟康3 小时前
【HarmonyOS 7新能力|067】LTPO可变帧率实战:让动画流畅而静态页面省电
harmonyos·arkts·低功耗·ltpo·可变帧率
LucianaiB3 小时前
华为云码道 AI 编程实战:我用 CodeArts 智能体打造了一款 HarmonyOS 游戏化专注应用「智办 ZhiBan」
人工智能·ai·华为云·harmonyos·codearts·材料
李游Leo3 小时前
HarmonyOS 7 + ArkTS + Image Kit 学习笔记:图像超分处理链路与 PixelMap 数据流实践【鸿蒙心迹】
笔记·学习·harmonyos
xq95273 小时前
JSON to ArkTS Model 王者归来
harmonyos
m0_738185824 小时前
Flutter 鸿蒙化实战:flutter_background_service 适配 OpenHarmony,后台服务
flutter·华为·harmonyos·鸿蒙
less_121384 小时前
HarmonyOS WPS Open SDK 快速入门:HAR 集成到 sendRequest 打开文档
华为·harmonyos·wps