SEO 信息
- **SEO 标题**:【共创季稿事节】HarmonyOS 6.1 沉浸光感实战:ArkUI 底部 Tab 毛玻璃材质改造
- **SEO 摘要**:本文以一个 HarmonyOS NEXT / ArkTS 实战项目“动图魔方”为例,记录底部 Tab 从灰色实底改造成沉浸光感材质导航的全过程。文章覆盖 ArkUI `backgroundBlurStyle`、`BlurStyle.COMPONENT_THICK`、透明背景、径向光池、深浅色主题、模拟器验收与踩坑复盘。
- **关键词**:HarmonyOS, ArkTS, ArkUI, 沉浸光感, 毛玻璃, BlurStyle, 底部导航, 主题切换
- **文章封面**:建议使用 `doc/screenshots_current/gifrubik_glass_material_editor_final.jpeg` 或底部 Tab 对比拼图
- **投稿方向**:HarmonyOS 6.1 创新特性适配实战
- **项目环境**:HarmonyOS SDK `6.1.0(23)`,ArkTS,DevEco Studio,GIFRubiksCube 项目
这篇文章来自“动图魔方”项目的一次真实 UI 改造:底部 Tab 看起来像一块灰色实底,用户一眼就能感到“不透明、没有材质感”。本文记录我如何在 ArkUI 中把它改成更接近 HarmonyOS 6.1 沉浸光感的底部导航,并把这个过程整理成可复用的工程方法。
一、真实问题背景
“动图魔方”是一个本地优先的 GIF 创作工具,核心功能包括图片/视频/GIF 素材导入、帧处理、比例和帧率控制、GIF89a 编码、作品保存与分享。
在功能逐步补齐后,底部 Tab 成了一个很明显的视觉短板:它承担首页、作品、创建、发现、我的五个入口,但模拟器里看起来更像一个灰色胶囊浮层。尤其在编辑页内容滚动到底部时,导航栏没有透出下层内容,也没有 HarmonyOS 6.1 里“沉浸光感”强调的材质层次。
这类问题不是简单换个颜色就能解决。因为用户感知到的是三件事叠在一起:
- 底部导航是否像一个浮在内容上方的系统材质层;
- 页面背景是否给材质提供了足够的光感和层次;
- 深浅色、选中态、创建按钮是否在不同页面里保持一致。
所以这次改造没有只改一个backgroundColor,而是同时处理 Tab 背景、模糊材质、页面环境光、主题切换和页面内容的关系。
二、目标与边界
这次改造的目标很明确:
1. 底部 Tab 不再是灰色实底,而是半透明材质层;
2. 导航栏要能透出下层 App 内容,而不是只模糊壁纸;
3. 浅色、深色、跟随系统三种主题都要可用;
4. 选中态、创建按钮、阴影和边框要保持稳定,不因为文字或状态变化抖动;
5. 不引入不确定 API,不用网上未经验证的“光照接口”。
边界也同样重要:
- 当前版本优先使用标准 ArkUI 能力完成稳定落地;
- 真正的 HDS 组件化 Tabs 或 UIDesignKit 后续可再整体替换;
- 本次不改变 GIF 导出、素材选择、作品管理等业务链路;
- 模拟器验收以“可见半透明材质”和“深浅色不崩”为准。
三、方案拆分
我把底部导航拆成三层来看:
| 层级 | 责任 | 对应实现 |
|---|---|---|
| 页面背景层 | 提供环境光和底色 | `pageBg()` + `radialGradient()` |
| 材质容器层 | 负责半透明、模糊、阴影、边框 | `BottomNav()` |
| 交互项层 | 负责选中态、创建按钮和页面切换 | `NavItem()` + `CreateNavItem()` |
最关键的一点是:如果底部容器本身使用高不透明灰色背景,即使加了backgroundBlurStyle,用户也几乎看不到模糊效果。模糊被实底盖住,自然会显得“假”。
因此最终方案是:
- 使用 `BlurStyle.COMPONENT_THICK` 作为底部 Tab 的模糊材质;
- 背景色只保留低透明度浅/深色调;
- 边框使用半透明白色增强轮廓;
- 页面根背景增加径向光池,让材质层有可感知的透光对象;
- “我的”页加入主题切换入口,让用户能主动切换系统、浅色、深色模式。
四、关键实现
4.1 底部 Tab:从实底改成组件材质模糊
底部导航核心代码在entry/src/main/ets/pages/Index.ets的BottomNav()。
@Builder BottomNav() { Row() { this.NavItem('首页', 'home') this.NavItem('作品', 'works') this.CreateNavItem() this.NavItem('发现', 'discover') this.NavItem('我的', 'profile') } .height(76) .width('90%') .padding({ left: 9, right: 9, top: 9, bottom: 9 }) .margin({ bottom: 14 }) .borderRadius(28) // 毛玻璃:用 COMPONENT 材质模糊下层 App 内容; // 背景仅留极低透明度的浅色/深色调,避免盖住模糊材质。 .backgroundBlurStyle(BlurStyle.COMPONENT_THICK) .backgroundColor(this.darkPreview ? '#33242235' : '#38FFFFFF') .border({ width: 1, color: this.darkPreview ? '#40FFFFFF' : '#80FFFFFF' }) .shadow({ radius: 22, color: this.darkPreview ? '#66000000' : '#1F000000', offsetX: 0, offsetY: 8 }) }这里有两个细节值得单独说。
第一个是BlurStyle.COMPONENT_THICK。我一开始使用过BACKGROUND_THIN,但它更偏向窗口背景/壁纸一类的模糊,放在 App 内部浮层上时,用户很难看到底部内容被模糊后的效果。改成COMPONENT_THICK后,底部 Tab 能直接作用于下层 App 内容,视觉上才更像真正浮在内容上方。
第二个是背景透明度。浅色模式下使用#38FFFFFF,深色模式下使用#33242235。这两个颜色都不是为了“铺底”,而是给材质一个非常轻的调色。这样既不会露得太空,也不会把模糊效果盖住。
4.2 页面根背景:给材质一个“可透”的环境
只有 Tab 自己半透明还不够。如果页面背景是一整块单调颜色,毛玻璃也很难有层次。因此在页面根节点上增加了径向光池:
build() { Stack({ alignContent: Alignment.Bottom }) { Column() { if (this.page === 'editor') { this.EditorPage() } else if (this.page === 'works') { this.WorksPage() } else if (this.page === 'discover') { this.DiscoverPage() } else if (this.page === 'profile') { this.ProfilePage() } else { this.HomePage() } } .width(this.contentWidth()) .height('100%') this.BottomNav() } .width('100%') .height('100%') .backgroundColor(this.pageBg()) .radialGradient(this.darkPreview ? { center: ['50%', '6%'], radius: '140%', colors: [['#2E2950', 0.0], ['#1B1929', 0.5], ['#121120', 1.0]] } : { center: ['50%', '5%'], radius: '130%', colors: [['#FFFFFF', 0.0], ['#F8F6FF', 0.45], ['#EFEAFE', 1.0]] }) }这一步的作用不是做炫酷背景,而是让页面有一个从顶部中心扩散的环境光。底部材质层透出内容时,用户能看到背景明暗变化,导航栏才不会像一块孤立贴片。
4.3 主题切换:把“我的”页变成偏好中心
改视觉时还有一个连带问题:发现页和我的页如果都只是空状态,就会显得没有拆分意义。于是我把“我的”页定位成本地偏好、隐私和工作台,主题切换放到这里。
private async loadThemeMode(): Promise<void> { const mode = await StorageService.loadThemeMode(this.ctx()); this.themeMode = mode; this.darkPreview = mode === 'dark'; this.applyColorMode(mode); } private applyColorMode(mode: string): void { try { if (mode === 'dark') { this.ctx().getApplicationContext() .setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK); } else if (mode === 'light') { this.ctx().getApplicationContext() .setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT); } else { this.ctx().getApplicationContext() .setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); } } catch (err) { } }对应 UI 是三个分段选项:
Row({ space: 8 }) { this.ThemeChoice('跟随系统', 'system') this.ThemeChoice('浅色', 'light') this.ThemeChoice('深色', 'dark') }这里没有把主题切换藏在设置二级页里,是因为它正好服务于本次视觉改造的验收:用户可以直接在“我的”页切换浅色、深色、跟随系统,并观察底部 Tab、页面卡片、文字颜色是否同步变化。
五、踩坑与修正
5.1 为什么加了 blur 还是像灰底
最初的问题不是没有调用模糊,而是模糊被不透明背景盖住了。比如一个接近 70% 不透明度的灰色底,再叠实色渐变,视觉结果一定更接近“灰胶囊”,而不是“玻璃”。
修正思路是反过来:先保证材质可见,再用极低透明度背景给它调色。
5.2 `BACKGROUND_THIN` 不适合这个浮层场景
BACKGROUND_THIN在某些场景下看不到明显变化,是因为它并不等价于“模糊当前组件下面的 App 内容”。底部 Tab 是 App 内部的浮层,需要的是组件层级下方内容参与模糊,因此改用COMPONENT_THICK。
这也是本次最有价值的结论:做 App 内部毛玻璃时,先确认模糊对象是谁。模糊壁纸和模糊组件下层内容,是两个完全不同的视觉结果。
5.3 光感不能靠不存在的 API
调研过程中也看到过一些社区文章提到类似lighting.setLightEffect的接口,但没有在官方能力里确认到稳定可用形态。为了避免给项目引入不可靠依赖,本次没有采用这类写法,而是使用 ArkUI 标准背景、模糊、透明度、阴影组合实现。
这对工程项目很重要:视觉效果可以迭代,但底层 API 必须可靠。尤其是准备上架或参赛投稿时,文章里要能说明“为什么这么做”,而不是只贴一段不可验证代码。
六、调试与验收
本次验收主要看三组截图:
- 改造前:底部 Tab 呈灰色实底,材质感弱;
- 改造后浅色:底部导航能透出编辑页内容,边框和阴影轻量;
- 改造后深色:导航栏仍有可辨识层次,选中态与创建按钮不丢失。
本项目当前可用截图位于:
- `doc/screenshots_current/gifrubik_immersive_bottom_nav.jpeg`
- `doc/screenshots_current/gifrubik_immersive_light_bottom_nav.jpeg`
- `doc/screenshots_current/gifrubik_glass_material_editor_final.jpeg`
- `doc/screenshots_current/gifrubik_profile_expanded_dark_nav.jpeg`
构建方面,项目 SDK 配置已经修正为:
{ "targetSdkVersion": "6.1.0(23)", "compatibleSdkVersion": "6.1.0(23)" }Hvigor 构建结果为:
BUILD SUCCESSFUL构建仍有部分警告,例如后台任务能力、弃用 API 和异常处理提示,但这些不影响本次底部导航视觉改造的编译通过。涉及系统受限能力的部分,项目中已通过能力门控和降级处理隔离。
七、工程复盘
这次改造看似只是底部 Tab 的 UI 调整,实际复盘下来有三个工程经验。
第一,材质感不是单个属性。backgroundBlurStyle、透明背景、边框、阴影、页面底色和内容层次共同决定最终观感。只加 blur,通常不够。
第二,页面功能分区会影响视觉可信度。发现页负责模板、教程和 Image2 灵感;我的页负责主题、隐私和设备能力。页面职责明确后,底部 Tab 的五个入口才不是摆设。
第三,视觉改造也要写验收条件。比如“半透明”不能只凭主观描述,需要在模拟器截图里看到下层内容参与材质效果;“深色可用”也不能只改背景色,需要检查文字、卡片、按钮和导航选中态。
八、验收清单
| 验收项 | 结果 | 说明 |
|---|---|---|
| 底部 Tab 不再使用高不透明灰底 | 通过 | 背景改为低透明浅/深色 |
| App 内部内容参与模糊 | 通过 | 使用 `BlurStyle.COMPONENT_THICK` |
| 页面具备环境光层次 | 通过 | 根节点增加 `radialGradient` |
| 浅色主题可用 | 通过 | 文字、卡片、导航保持对比度 |
| 深色主题可用 | 通过 | 主题切换入口在“我的”页 |
| 跟随系统可用 | 通过 | 使用应用级 `setColorMode` |
| 构建通过 | 通过 | Hvigor 输出 `BUILD SUCCESSFUL` |
| 未引入不可靠光照 API | 通过 | 只使用标准 ArkUI 能力 |
九、小结
HarmonyOS 6.1 的沉浸光感,不应该被理解成“加一层半透明背景”。在真实工程里,它更像一个系统化的视觉关系:内容要在下面流动,材质要能透出层次,主题要能切换,入口职责要清楚。
对“动图魔方”来说,这次改造的价值不只是底部导航变好看了,而是让整个创作工具从“功能堆叠”往“可持续扩展的产品界面”走了一步。后续如果继续接入 HDS Tabs 或 UIDesignKit,也可以在这套页面职责和主题体系上平滑替换。
十、下一篇衔接
下一篇我会继续拆“动图魔方”的核心能力:如何在 HarmonyOS 6.1 中使用 Media Kit 从视频抽帧,并把帧序列送入 GIF89a 编码链路。那一篇会更偏底层:AVImageGenerator、PixelMap、帧率限制、尺寸压缩、LZW 编码和导出进度。