文章目录
-
- 前言
- [一、Component3D 基础用法](#一、Component3D 基础用法)
-
- [1.1 SceneOptions 配置](#1.1 SceneOptions 配置)
- [1.2 条件渲染:加载态与渲染态分离](#1.2 条件渲染:加载态与渲染态分离)
- [二、SURFACE 模式渲染原理](#二、SURFACE 模式渲染原理)
-
- [2.1 两种渲染模式对比](#2.1 两种渲染模式对比)
- [2.2 SURFACE 模式的触摸事件问题](#2.2 SURFACE 模式的触摸事件问题)
- [三、HitTestMode 与事件层叠架构](#三、HitTestMode 与事件层叠架构)
-
- [3.1 正确的 Stack 层叠结构](#3.1 正确的 Stack 层叠结构)
- [3.2 HitTestMode 三种模式说明](#3.2 HitTestMode 三种模式说明)
- [四、渲染尺寸与 renderWidth / renderHeight](#四、渲染尺寸与 renderWidth / renderHeight)
-
- [4.1 独立渲染分辨率](#4.1 独立渲染分辨率)
- 总结
前言
在 HarmonyOS 6 的 3D 渲染体系中,Component3D 是连接 ArkUI 布局体系与底层图形引擎的桥梁组件。它看似只是一个普通的 UI 组件,背后却涉及 Surface 合成、渲染管线以及 HitTest 机制等多个底层概念。本文将深度剖析 Component3D 的两种渲染模式------SURFACE 与 TEXTURE,重点讲解 SURFACE 模式的渲染原理、触摸事件拦截问题的根因,以及在实际项目中如何通过正确的层叠结构解决事件穿透难题。
运行效果如下:

一、Component3D 基础用法
1.1 SceneOptions 配置
Component3D 通过 SceneOptions 接收场景配置,两个核心字段缺一不可:
typescript
this.sceneOpt = { scene: this.scene, modelType: ModelType.SURFACE } as SceneOptions;
typescript
if (this.sceneOpt) {
Component3D(this.sceneOpt)
.width('100%')
.height('100%')
}
| SceneOptions 字段 | 类型 | 说明 |
|---|---|---|
| scene | Scene | 已加载的 3D 场景对象 |
| modelType | ModelType | 渲染模式,SURFACE 或 TEXTURE |
提示:
scene字段也可以直接传入ResourceStr(如$rawfile('82.glb')),此时引擎内部自动加载,但无法获取 Scene 对象引用,无法控制相机和动画。推荐始终先用Scene.load()加载后再传入。
1.2 条件渲染:加载态与渲染态分离
sceneOpt 初始为 null,利用 ArkUI 的条件渲染实现平滑的加载占位:
typescript
if (this.sceneOpt) {
Component3D(this.sceneOpt)
.width('100%')
.height('100%')
} else {
Column() {
LoadingProgress()
.width(64)
.height(64)
.color(Color.White)
Text('正在加载 3D 模型...')
.fontSize(16)
.fontColor(Color.White)
.margin({ top: 14 })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor('#FF000000')
}
这种写法的优势:
- 加载期间展示
LoadingProgress动画,用户感知到进度 sceneOpt赋值后 ArkUI 响应式框架自动重建节点树,Component3D在场景完全就绪后才创建- 避免
Component3D以空 Scene 状态初始化导致黑屏或崩溃
二、SURFACE 模式渲染原理
2.1 两种渲染模式对比
typescript
// SURFACE 模式
this.sceneOpt = { scene: this.scene, modelType: ModelType.SURFACE } as SceneOptions;
// TEXTURE 模式(对比)
// this.sceneOpt = { scene: this.scene, modelType: ModelType.TEXTURE } as SceneOptions;
| 特性 | SURFACE 模式 | TEXTURE 模式 |
|---|---|---|
| 渲染方式 | 独立 Surface 层,由硬件合成器直接合成 | 渲染到纹理,由 GPU 合成到 ArkUI 渲染树 |
| 性能 | 更高(bypass GPU composition) | 相对较低 |
| 触摸事件 | Surface 层在 ArkUI 层之上,拦截所有触摸 | 与普通 ArkUI 组件相同,事件正常分发 |
| 透明叠加 | 不支持(Surface 层不透明) | 支持 ArkUI 组件叠加在 3D 内容上方 |
| 推荐场景 | 全屏 3D 展示、高性能要求 | 3D 内容与 ArkUI 组件混合显示 |
主要特点:
- SURFACE 模式性能更优,适合全屏 3D 展示场景
- TEXTURE 模式与 ArkUI 组件无缝混合,适合 AR 信息叠加
- 两种模式通过
modelType字段切换,无需修改其他代码
2.2 SURFACE 模式的触摸事件问题
SURFACE 模式下,3D 渲染内容通过独立的 Surface 合成层显示,该层在 Z 轴上位于所有 ArkUI 层之上。这意味着:
- 直接在
Component3D上调用.onTouch()无法收到事件 - 在
Component3D上方叠加透明 ArkUI 层,也会被 Surface 层拦截
核心优势:
- 理解这一机制后,解决方案就清晰了:将触摸事件捕获放在 与 Surface 层并列的 ArkUI 层,而非覆盖在其上
Column(){}空容器配合hitTestBehavior是最轻量的解决方案
三、HitTestMode 与事件层叠架构
3.1 正确的 Stack 层叠结构
typescript
Stack({ alignContent: Alignment.Bottom }) {
// 第一层:Component3D(SURFACE 渲染层)
if (this.sceneOpt) {
Component3D(this.sceneOpt)
.width('100%')
.height('100%')
}
// 第二层:顶部标题(HitTestMode.Transparent 允许触摸穿透)
if (this.sceneOpt) {
Row() {
Text('Hunyuan 3D 预览')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor(Color.White)
}
.width('100%')
.padding({ top: 52, left: 24, bottom: 20 })
.linearGradient({
direction: GradientDirection.Bottom,
colors: [['#CC000000', 0.0], ['#00000000', 1.0]]
})
.alignSelf(ItemAlign.Start)
.hitTestBehavior(HitTestMode.Transparent)
}
// 第三层:底部控制栏(HitTestMode.Default 正常响应按钮点击)
if (this.sceneOpt) {
Row() {
// ... 按钮
}
.hitTestBehavior(HitTestMode.Default)
}
// 第四层:全屏触摸捕获层
if (this.sceneOpt) {
Column(){}
.width('100%')
.height('100%')
.hitTestBehavior(HitTestMode.Transparent)
.onTouch((event: TouchEvent) => {
// 处理旋转手势
})
}
}
3.2 HitTestMode 三种模式说明
| HitTestMode | 行为 | 用途 |
|---|---|---|
| Default | 自身响应触摸,子组件也参与 HitTest | 普通按钮、可交互组件 |
| Transparent | 自身响应触摸,但不阻止事件继续向下传递 | 渐变蒙层、触摸捕获层 |
| None | 不参与 HitTest,完全透明 | 纯视觉装饰层 |
触摸分发顺序(Stack 中从上到下):
- 第四层触摸捕获层(
Transparent):接收onTouch事件,同时允许事件继续传递 - 第三层底部控制栏(
Default):按钮区域正常响应onClick - 第二层顶部标题(
Transparent):不拦截触摸 - 第一层 Component3D:SURFACE 层,ArkUI 事件不到达此处
提示:
HitTestMode.Transparent与HitTestMode.None的区别在于:Transparent的onTouch回调仍然会被调用 ,而None完全不参与事件分发,onTouch不会触发。本项目使用Transparent正是利用了这一特性。
四、渲染尺寸与 renderWidth / renderHeight
4.1 独立渲染分辨率
Component3D 支持独立设置渲染分辨率,与显示尺寸解耦:
typescript
Component3D(this.sceneOpt)
.width('100%')
.height('100%')
// 可选:降低渲染分辨率提升性能
// .renderWidth('60%')
// .renderHeight('60%')
本项目使用全分辨率渲染(不设置 renderWidth/renderHeight),以获得最佳显示效果。在性能敏感场景下,可以将渲染分辨率设为 60%,由引擎自动上采样到显示尺寸,画质略有下降但帧率显著提升。
主要特点:
- 渲染分辨率与显示尺寸完全解耦
- 低端设备可降低渲染分辨率保证流畅度
- 高端设备可使用原生分辨率或更高渲染精度
总结
Component3D 是 HarmonyOS 6 3D 渲染的核心组件,SURFACE 模式 凭借硬件合成的性能优势是全屏 3D 场景的首选,但需要理解其触摸事件机制。通过合理设计 Stack 层叠结构,配合 HitTestMode.Transparent,可以在 SURFACE 模式下同时实现高性能渲染和完整的手势交互。本文涉及的层叠架构设计思路,同样适用于其他需要在 3D 内容上叠加 UI 的场景。
如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!
需要源码的记得私聊哦