文章目录
-
- 每日一句正能量
- 前言
- [一、沉浸光感与悬浮导航:HarmonyOS 6 的视觉革命](#一、沉浸光感与悬浮导航:HarmonyOS 6 的视觉革命)
-
- [1.1 核心特性解析](#1.1 核心特性解析)
- [1.2 技术架构对比](#1.2 技术架构对比)
- 二、项目实战:"光影工作台"架构设计
-
- [2.1 应用场景与功能规划](#2.1 应用场景与功能规划)
- [2.2 技术架构图](#2.2 技术架构图)
- 三、环境配置与模块依赖
-
- [3.1 模块依赖配置](#3.1 模块依赖配置)
- [3.2 窗口沉浸配置(EntryAbility.ets)](#3.2 窗口沉浸配置(EntryAbility.ets))
- 四、核心组件实战
-
- [4.1 沉浸光感标题栏(HdsNavigation + systemMaterialEffect)](#4.1 沉浸光感标题栏(HdsNavigation + systemMaterialEffect))
- [4.2 悬浮页签导航(HdsTabs + 沉浸光感 + 自适应材质)](#4.2 悬浮页签导航(HdsTabs + 沉浸光感 + 自适应材质))
- [4.3 多窗口光效同步管理器](#4.3 多窗口光效同步管理器)
- [4.4 主页面:光效联动与自适应渲染](#4.4 主页面:光效联动与自适应渲染)
- 五、关键技术总结
-
- [5.1 沉浸光感实现清单](#5.1 沉浸光感实现清单)
- [5.2 悬浮导航适配要点](#5.2 悬浮导航适配要点)
- [5.3 PC 端多窗口光效协同](#5.3 PC 端多窗口光效协同)
- 六、调试与性能优化
-
- [6.1 真机调试建议](#6.1 真机调试建议)
- [6.2 无障碍与降级适配](#6.2 无障碍与降级适配)
- 七、总结与展望

每日一句正能量
人跟人之间的感情就像织毛衣,建立的时候一针一线,小心而漫长,拆除的时候只要轻轻一拉,卸载永远比安装快,失去永远比得到快。
前言
摘要 :HarmonyOS 6(API 23)在
@kit.UIDesignKit中引入了systemMaterialEffect(系统材质效果)能力,其中**沉浸光感(IMMERSIVE)**通过模拟真实物理光照模型,为标题栏和底部导航带来细腻的"光晕"与"反射"反馈。本文将基于 HDS 设计系统,实战开发一款面向 HarmonyOS PC 的"光影工作台"多窗口协作应用,展示悬浮页签、自适应材质与空间光效的深度融合。
一、沉浸光感与悬浮导航:HarmonyOS 6 的视觉革命
1.1 核心特性解析
HarmonyOS 6(API 23)带来了两大 UI 革新特性 :
沉浸光感(Immersive Light Effects):
- 基于物理光照模型,组件内部模拟真实的光晕与反射效果
- 支持 HdsNavigation (标题栏)和 HdsTabs(底部悬浮页签)两大核心组件
- 交互时产生细腻的触控反馈,增强控件立体感
悬浮导航(Float Navigation):
- 导航栏脱离底部边界,以"四周留白、圆角卡片"形态悬浮于内容之上
- 支持 强(85%)、平衡(70%)、弱(55%) 三档透明度自定义
- 与系统全面屏手势无缝衔接,内容区域智能避让
1.2 技术架构对比
| 特性 | HarmonyOS 5.x | HarmonyOS 6(API 23) |
|---|---|---|
| 标题栏材质 | 纯色/简单模糊 | systemMaterialEffect 物理光照模型 |
| 底部导航 | 固定 TabBar | 悬浮卡片 + 沉浸光感 |
| 窗口管理 | 单窗口全屏 | 多自由窗口 + 光效同步 |
| 交互反馈 | 基础点击效果 | 光晕反射 + 微震动 |
二、项目实战:"光影工作台"架构设计
2.1 应用场景与功能规划
面向 HarmonyOS PC 的多窗口协作场景,核心功能包括:
- 悬浮导航工作台:底部悬浮页签支持"文档/表格/演示/白板"四大工作区切换
- 沉浸光感标题栏:根据当前工作区主题色动态变化光效,激活窗口边缘发光
- 多窗口协同:支持主窗口 + 浮动工具窗口(如计算器、便签)的光效联动
- 自适应材质:根据窗口焦点状态、鼠标位置、时间(日间/夜间)自动调整光感强度
2.2 技术架构图
┌─────────────────────────────────────────────────────────┐
│ HDS Design System (UIDesignKit) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ HdsNavigation│ │ HdsTabs │ │ systemMaterial │ │
│ │ (沉浸光感标题栏)│ │ (悬浮页签) │ │ Effect │ │
│ └──────┬──────┘ └──────┬──────┘ └─────────────────┘ │
└─────────┼────────────────┼─────────────────────────────────┘
│ │
┌─────────▼────────────────▼─────────────────────────────┐
│ ArkUI 渲染层 │
│ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ 主窗口 (Main) │ │ 浮动工具窗口 │ │
│ │ · 沉浸光效背景 │ │ · 跟随主窗口光效 │ │
│ │ · 自适应材质 │ │ · 置顶/阴影/圆角 │ │
│ │ · 鼠标追踪光效 │ │ · 边缘发光指示 │ │
│ └─────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
三、环境配置与模块依赖
3.1 模块依赖配置
在 oh-package.json5 中添加 HDS 设计系统与窗口管理依赖:
json
{
"dependencies": {
"@kit.UIDesignKit": "^6.1.0",
"@kit.ArkUI": "^6.1.0",
"@kit.AbilityKit": "^6.1.0",
"@kit.BasicServicesKit": "^6.1.0"
}
}
3.2 窗口沉浸配置(EntryAbility.ets)
代码亮点:HarmonyOS PC 应用需配置自由窗口模式,启用窗口阴影与圆角,并设置透明背景以允许光效穿透。
typescript
// entry/src/main/ets/entryability/EntryAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
export default class EntryAbility extends UIAbility {
private windowStage: window.WindowStage | null = null;
onWindowStageCreate(windowStage: window.WindowStage): void {
this.windowStage = windowStage;
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
console.error('Failed to load content:', JSON.stringify(err));
return;
}
console.info('Succeeded in loading content.');
this.setupPCWindow(windowStage);
});
}
/**
* 配置PC端自由窗口模式
* 关键设置:
* 1. FREE窗口模式:支持自由缩放、移动
* 2. 自定义标题栏:使用HdsNavigation替代系统标题栏
* 3. 窗口阴影与圆角:增强视觉层次
* 4. 透明背景:允许沉浸光效穿透至窗口边缘
*/
private async setupPCWindow(windowStage: window.WindowStage): Promise<void> {
try {
const mainWindow = windowStage.getMainWindowSync();
// 1. 设置为自由窗口模式(PC端核心特性)
await mainWindow.setWindowSizeType(window.WindowSizeType.FREE);
await mainWindow.setWindowMode(window.WindowMode.FULLSCREEN);
// 2. 隐藏系统标题栏,使用HdsNavigation自定义
await mainWindow.setWindowTitleBarEnable(false);
// 3. 启用窗口阴影和圆角
await mainWindow.setWindowShadowEnabled(true);
await mainWindow.setWindowCornerRadius(12);
// 4. 设置透明背景,允许光效穿透
await mainWindow.setWindowBackgroundColor('#00000000');
// 5. 配置系统栏属性
await mainWindow.setWindowSystemBarProperties({
statusBarColor: '#00000000',
navigationBarColor: '#00000000',
statusBarContentColor: '#FFFFFF',
navigationBarContentColor: '#FFFFFF'
});
// 6. 启用安全区避让
await mainWindow.setWindowAvoidAreaOption({
type: window.AvoidAreaType.TYPE_SYSTEM,
enabled: true
});
// 7. 保存窗口实例到全局,供子窗口管理使用
AppStorage.setOrCreate('main_window', mainWindow);
console.info('PC window setup completed');
} catch (error) {
console.error('Failed to setup PC window:', (error as BusinessError).message);
}
}
onWindowStageDestroy(): void {
this.windowStage = null;
}
}
四、核心组件实战
4.1 沉浸光感标题栏(HdsNavigation + systemMaterialEffect)
代码亮点 :使用 @kit.UIDesignKit 的 HdsNavigation 组件,通过 systemMaterialEffect 设置 IMMERSIVE 材质,实现物理光照模型的光晕与反射效果。标题栏根据当前工作区主题色动态变化,窗口激活时边缘发光增强。
typescript
// components/ImmersiveTitleBar.ets
import { HdsNavigation, SystemMaterialEffect } from '@kit.UIDesignKit';
import { window } from '@kit.ArkUI';
/**
* 工作区主题配置
*/
interface WorkspaceTheme {
name: string;
primaryColor: string;
accentColor: string;
icon: Resource;
}
@Component
export struct ImmersiveTitleBar {
@Prop currentWorkspace: number;
@State isWindowFocused: boolean = true;
@State titleBarColor: string = '#4A90E2';
// 四大工作区主题配置
private workspaces: WorkspaceTheme[] = [
{ name: '文档', primaryColor: '#4A90E2', accentColor: '#5BA0F2', icon: $r('app.media.ic_doc') },
{ name: '表格', primaryColor: '#00C853', accentColor: '#00E676', icon: $r('app.media.ic_sheet') },
{ name: '演示', primaryColor: '#FF6D00', accentColor: '#FF9100', icon: $r('app.media.ic_slide') },
{ name: '白板', primaryColor: '#AA00FF', accentColor: '#E040FB', icon: $r('app.media.ic_board') }
];
aboutToAppear(): void {
this.monitorWindowFocus();
this.updateThemeColor();
}
/**
* 监听窗口焦点变化,调整光效强度
* 激活窗口:光效增强(边缘发光)
* 非激活窗口:光效减弱(降低干扰)
*/
private async monitorWindowFocus(): Promise<void> {
try {
const mainWindow = await window.getLastWindow();
mainWindow.on('windowFocusChange', (isFocused: boolean) => {
this.isWindowFocused = isFocused;
// 焦点变化时触发光效脉冲
if (isFocused) {
this.triggerLightPulse();
}
});
} catch (error) {
console.error('Failed to monitor window focus:', error);
}
}
/**
* 根据当前工作区更新主题色
*/
private updateThemeColor(): void {
this.titleBarColor = this.workspaces[this.currentWorkspace].primaryColor;
// 同步到全局,供其他组件使用
AppStorage.setOrCreate('global_theme_color', this.titleBarColor);
}
/**
* 触发光效脉冲(窗口获得焦点时的视觉反馈)
*/
private triggerLightPulse(): void {
// 通过状态变化触发ArkUI动画
const originalColor = this.titleBarColor;
this.titleBarColor = this.workspaces[this.currentWorkspace].accentColor;
setTimeout(() => {
this.titleBarColor = originalColor;
}, 300);
}
build() {
Column() {
// 顶部边缘光晕(窗口激活状态指示)
if (this.isWindowFocused) {
Column()
.width('100%')
.height(2)
.backgroundColor(this.titleBarColor)
.opacity(0.8)
.shadow({
radius: 10,
color: this.titleBarColor,
offsetX: 0,
offsetY: 2
})
}
// HdsNavigation 沉浸光感标题栏
HdsNavigation({
title: this.workspaces[this.currentWorkspace].name,
titleColor: '#FFFFFF',
// 核心:设置系统材质效果为 IMMERSIVE(沉浸光感)
systemMaterialEffect: SystemMaterialEffect.IMMERSIVE,
// 背景色使用主题色+透明度,配合材质效果产生光晕
backgroundColor: `${this.titleBarColor}${this.isWindowFocused ? 'E6' : '99'}`, // E6=90%, 99=60%
// 左侧操作区
leading: this.buildLeading(),
// 右侧操作区
actions: this.buildActions()
})
.width('100%')
.height(56)
// 关键:扩展至安全区顶部(状态栏区域)
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
// 背景模糊增强光感层次
.backgroundBlurStyle(BlurStyle.REGULAR)
// 底部微光分割线
.border({
width: { bottom: 1 },
color: 'rgba(255,255,255,0.1)',
style: BorderStyle.Solid
})
// 光效反射层(模拟物理光照的底部反射)
Column()
.width('100%')
.height(20)
.backgroundColor(this.titleBarColor)
.opacity(this.isWindowFocused ? 0.05 : 0.02)
.blur(20)
.linearGradient({
direction: GradientDirection.Top,
colors: [
[this.titleBarColor, 0.0],
['transparent', 1.0]
]
})
}
.width('100%')
}
@Builder
buildLeading(): void {
Row({ space: 12 }) {
// 工作区图标
Image(this.workspaces[this.currentWorkspace].icon)
.width(24)
.height(24)
.fillColor('#FFFFFF')
// 窗口控制按钮(PC端特色)
Row({ space: 8 }) {
Button() { Text('−').fontColor('#FFFFFF').fontSize(14) }
.type(ButtonType.Circle)
.backgroundColor('rgba(255,255,255,0.1)')
.width(24)
.height(24)
.onClick(() => this.minimizeWindow())
Button() { Text('□').fontColor('#FFFFFF').fontSize(10) }
.type(ButtonType.Circle)
.backgroundColor('rgba(255,255,255,0.1)')
.width(24)
.height(24)
.onClick(() => this.maximizeWindow())
Button() { Text('×').fontColor('#FFFFFF').fontSize(14) }
.type(ButtonType.Circle)
.backgroundColor('rgba(255,255,255,0.1)')
.width(24)
.height(24)
.onClick(() => this.closeWindow())
}
}
}
@Builder
buildActions(): void {
Row({ space: 16 }) {
// 搜索按钮(带光效反馈)
Button() {
Image($r('app.media.ic_search'))
.width(20)
.height(20)
.fillColor('#FFFFFF')
}
.type(ButtonType.Circle)
.backgroundColor('transparent')
.width(36)
.height(36)
// 点击时的光效反馈
.stateStyles({
pressed: {
.backgroundColor('rgba(255,255,255,0.15)')
.shadow({ radius: 8, color: 'rgba(255,255,255,0.3)' })
}
})
// 协作状态指示
Stack() {
Image($r('app.media.ic_collab'))
.width(20)
.height(20)
.fillColor('#FFFFFF')
// 在线状态光点
Column()
.width(8)
.height(8)
.backgroundColor('#00E676')
.borderRadius(4)
.position({ x: 14, y: -2 })
.shadow({ radius: 4, color: '#00E676' })
}
// 用户头像(带沉浸光感边框)
Stack() {
Image($r('app.media.avatar_user'))
.width(32)
.height(32)
.borderRadius(16)
// 头像光晕
Column()
.width(36)
.height(36)
.backgroundColor(this.titleBarColor)
.borderRadius(18)
.opacity(0.3)
.blur(4)
.position({ x: -2, y: -2 })
}
}
}
// 窗口控制方法
private async minimizeWindow(): Promise<void> {
const win = await window.getLastWindow();
await win.minimize();
}
private async maximizeWindow(): Promise<void> {
const win = await window.getLastWindow();
const isMax = win.getWindowProperties().isMaximize;
if (isMax) {
await win.recover();
} else {
await win.maximize();
}
}
private async closeWindow(): Promise<void> {
const win = await window.getLastWindow();
await win.close();
}
}
4.2 悬浮页签导航(HdsTabs + 沉浸光感 + 自适应材质)
代码亮点 :使用 HdsTabs 组件构建底部悬浮页签,通过 systemMaterialEffect 实现沉浸光感。页签支持"强/平衡/弱"三档透明度调节,根据窗口焦点状态和鼠标位置自适应调整光效强度。
typescript
// components/FloatTabNavigation.ets
import { HdsTabs, HdsTabBar, SystemMaterialEffect } from '@kit.UIDesignKit';
import { window } from '@kit.ArkUI';
/**
* 透明度档位枚举
*/
export enum TransparencyLevel {
STRONG = 0.85, // 强效果:高透明度,玻璃感明显
BALANCED = 0.70, // 平衡效果:适中透明度
WEAK = 0.55 // 弱效果:低透明度,更清晰
}
/**
* 页签配置
*/
interface TabConfig {
label: string;
icon: Resource;
iconActive: Resource;
badge?: number;
}
@Component
export struct FloatTabNavigation {
@Prop currentIndex: number;
@Prop onTabChange: (index: number) => void;
@State navTransparency: number = TransparencyLevel.BALANCED;
@State isExpanded: boolean = false; // 是否展开透明度调节面板
@State isWindowFocused: boolean = true;
@State mouseY: number = 0; // 鼠标Y坐标,用于光效追踪
@State bottomAvoidHeight: number = 0;
// 四大工作区页签配置
private tabs: TabConfig[] = [
{ label: '文档', icon: $r('app.media.ic_doc'), iconActive: $r('app.media.ic_doc_filled'), badge: 3 },
{ label: '表格', icon: $r('app.media.ic_sheet'), iconActive: $r('app.media.ic_sheet_filled') },
{ label: '演示', icon: $r('app.media.ic_slide'), iconActive: $r('app.media.ic_slide_filled'), badge: 1 },
{ label: '白板', icon: $r('app.media.ic_board'), iconActive: $r('app.media.ic_board_filled') }
];
aboutToAppear(): void {
this.getBottomAvoidArea();
this.monitorWindowFocus();
}
/**
* 获取底部安全区高度(导航栏避让)
* HarmonyOS 6支持多种避让类型:TYPE_SYSTEM, TYPE_NAVIGATION_INDICATOR等
*/
private async getBottomAvoidArea(): Promise<void> {
try {
const mainWindow = await window.getLastWindow();
const avoidArea = mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
this.bottomAvoidHeight = avoidArea.bottomRect.height;
// 保存到全局供其他组件使用
AppStorage.setOrCreate('bottom_avoid_height', this.bottomAvoidHeight);
} catch (error) {
console.error('Failed to get avoid area:', error);
}
}
private async monitorWindowFocus(): Promise<void> {
const mainWindow = await window.getLastWindow();
mainWindow.on('windowFocusChange', (isFocused: boolean) => {
this.isWindowFocused = isFocused;
});
}
build() {
Stack({ alignContent: Alignment.Bottom }) {
// 内容区域(由父组件传入)
Column() {
this.contentBuilder()
}
.width('100%')
.height('100%')
// 关键:底部留出安全区+导航栏高度,避免内容被遮挡
.padding({ bottom: this.bottomAvoidHeight + 100 })
// 悬浮页签容器
Column() {
// 玻璃拟态背景层(多层叠加实现沉浸光感)
Stack() {
// 底层:系统级毛玻璃效果
Column()
.width('100%')
.height('100%')
.backgroundBlurStyle(BlurStyle.REGULAR)
.opacity(this.navTransparency)
.backdropBlur($r('sys.blur.20'))
// 中层:主题色微光晕染
Column()
.width('100%')
.height('100%')
.backgroundColor(this.getThemeColor())
.opacity(this.isWindowFocused ? 0.08 : 0.03)
.blur(40)
// 顶层:渐变高光(模拟物理光照的顶部反射)
Column()
.width('100%')
.height('100%')
.linearGradient({
direction: GradientDirection.Top,
colors: [
['rgba(255,255,255,0.15)', 0.0],
['rgba(255,255,255,0.05)', 0.5],
['rgba(255,255,255,0.0)', 1.0]
]
})
}
.width('100%')
.height('100%')
.borderRadius(24) // 圆角悬浮卡片
.shadow({
radius: 20,
color: 'rgba(0,0,0,0.15)',
offsetX: 0,
offsetY: -4
})
// 鼠标悬停时增强光效(PC端特色)
.onHover((isHover: boolean) => {
this.navTransparency = isHover
? Math.min(this.navTransparency + 0.1, TransparencyLevel.STRONG)
: TransparencyLevel.BALANCED;
})
// HdsTabs 沉浸光感页签
HdsTabs({
index: this.currentIndex,
barPosition: BarPosition.Bottom,
// 核心:启用沉浸光感材质
tabBarStyle: {
systemMaterialEffect: SystemMaterialEffect.IMMERSIVE,
// 选中指示器光效
indicatorColor: this.getThemeColor(),
indicatorHeight: 3,
indicatorWidth: 24,
// 文字样式
labelColor: '#999999',
activeLabelColor: '#FFFFFF',
labelFontSize: 12,
activeLabelFontSize: 13,
// 图标样式
iconSize: 24,
activeIconColor: '#FFFFFF',
inactiveIconColor: '#999999'
}
}) {
// 页签内容通过ForEach动态生成
ForEach(this.tabs, (tab: TabConfig, index: number) => {
TabContent() {
// 内容由外部传入
}
.tabBar(this.buildTabBarItem(tab, index))
})
}
.width('100%')
.height(80)
.onChange((index: number) => {
this.onTabChange(index);
this.triggerTabSwitchFeedback(index);
})
// 透明度调节面板(长按展开)
if (this.isExpanded) {
this.buildTransparencyPanel()
}
}
.width('92%') // 左右留白,形成悬浮效果
.height(this.isExpanded ? 130 : 80)
.margin({
bottom: this.bottomAvoidHeight + 16,
left: '4%',
right: '4%'
})
.animation({
duration: 300,
curve: Curve.Spring,
iterations: 1
})
// 长按手势:展开/收起透明度调节
.gesture(
LongPressGesture({ duration: 500 })
.onAction(() => {
this.isExpanded = !this.isExpanded;
})
)
}
.width('100%')
.height('100%')
}
/**
* 构建单个页签项(带徽章和光效)
*/
@Builder
buildTabBarItem(tab: TabConfig, index: number): void {
Column() {
Stack() {
// 图标
Image(this.currentIndex === index ? tab.iconActive : tab.icon)
.width(24)
.height(24)
.fillColor(this.currentIndex === index ? '#FFFFFF' : '#999999')
// 徽章红点
if (tab.badge && tab.badge > 0) {
Column() {
Text(`${tab.badge}`)
.fontSize(10)
.fontColor('#FFFFFF')
}
.width(16)
.height(16)
.backgroundColor('#FF4444')
.borderRadius(8)
.position({ x: 14, y: -4 })
.shadow({ radius: 4, color: '#FF4444' })
}
// 选中状态光晕
if (this.currentIndex === index) {
Column()
.width(48)
.height(48)
.backgroundColor(this.getThemeColor())
.borderRadius(24)
.opacity(0.2)
.blur(12)
.position({ x: -12, y: -12 })
}
}
.width(48)
.height(48)
Text(tab.label)
.fontSize(11)
.fontColor(this.currentIndex === index ? '#FFFFFF' : '#999999')
.margin({ top: 2 })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
/**
* 透明度调节面板
*/
@Builder
buildTransparencyPanel(): void {
Row() {
Text('光感强度')
.fontSize(12)
.fontColor('rgba(255,255,255,0.7)')
.margin({ right: 12 })
// 三档预设按钮
Row({ space: 8 }) {
Button('强')
.type(ButtonType.Capsule)
.fontSize(11)
.backgroundColor(this.navTransparency === TransparencyLevel.STRONG ? this.getThemeColor() : 'rgba(255,255,255,0.1)')
.onClick(() => this.navTransparency = TransparencyLevel.STRONG)
Button('平衡')
.type(ButtonType.Capsule)
.fontSize(11)
.backgroundColor(this.navTransparency === TransparencyLevel.BALANCED ? this.getThemeColor() : 'rgba(255,255,255,0.1)')
.onClick(() => this.navTransparency = TransparencyLevel.BALANCED)
Button('弱')
.type(ButtonType.Capsule)
.fontSize(11)
.backgroundColor(this.navTransparency === TransparencyLevel.WEAK ? this.getThemeColor() : 'rgba(255,255,255,0.1)')
.onClick(() => this.navTransparency = TransparencyLevel.WEAK)
}
// 滑块微调
Slider({
value: this.navTransparency * 100,
min: 30,
max: 95,
step: 5,
style: SliderStyle.InSet
})
.width(100)
.selectedColor(this.getThemeColor())
.onChange((value: number) => {
this.navTransparency = value / 100;
})
}
.width('100%')
.height(50)
.justifyContent(FlexAlign.Center)
.backgroundColor('rgba(255,255,255,0.05)')
.borderRadius({ topLeft: 16, topRight: 16 })
}
/**
* 获取当前主题色(从全局状态读取)
*/
private getThemeColor(): string {
return AppStorage.get<string>('global_theme_color') || '#4A90E2';
}
/**
* 页签切换反馈(光效脉冲 + 微震动)
*/
private triggerTabSwitchFeedback(index: number): void {
// 光效脉冲
const themeColor = this.workspaces?.[index]?.primaryColor || '#4A90E2';
AppStorage.setOrCreate('global_theme_color', themeColor);
// 微震动反馈
try {
import('@kit.SensorServiceKit').then(sensor => {
sensor.vibrator.startVibration({
type: 'time',
duration: 30
}, { id: 0 });
});
} catch (error) {
console.error('Haptic feedback failed:', error);
}
}
// 内容构建器(由外部传入)
@BuilderParam contentBuilder: () => void = this.defaultContentBuilder;
@Builder
defaultContentBuilder(): void {
Column() {
Text('工作区内容')
.fontSize(16)
.fontColor('#999999')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
4.3 多窗口光效同步管理器
代码亮点 :HarmonyOS PC 支持多自由窗口,通过 WindowManager 实现主窗口与浮动工具窗口间的光效同步。浮动窗口跟随主窗口移动,激活时边缘发光,失活时自动降低光效强度 。
typescript
// utils/WindowManager.ets
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
export interface ToolWindowConfig {
name: string;
title: string;
width: number;
height: number;
x?: number;
y?: number;
followMainWindow?: boolean; // 是否跟随主窗口移动
}
export class WindowManager {
private static instance: WindowManager;
private mainWindow: window.Window | null = null;
private subWindows: Map<string, window.Window> = new Map();
static getInstance(): WindowManager {
if (!WindowManager.instance) {
WindowManager.instance = new WindowManager();
}
return WindowManager.instance;
}
/**
* 初始化主窗口(PC自由窗口模式)
*/
async initializeMainWindow(windowStage: window.WindowStage): Promise<void> {
this.mainWindow = windowStage.getMainWindowSync();
// 配置为PC自由窗口
await this.mainWindow.setWindowSizeType(window.WindowSizeType.FREE);
await this.mainWindow.setWindowMode(window.WindowMode.FULLSCREEN);
await this.mainWindow.setWindowTitleBarEnable(false);
await this.mainWindow.setWindowShadowEnabled(true);
await this.mainWindow.setWindowCornerRadius(12);
await this.mainWindow.setWindowBackgroundColor('#00000000');
console.info('Main window initialized for PC immersive mode');
}
/**
* 创建浮动工具窗口(如计算器、便签)
* 特性:置顶、圆角、阴影、跟随主窗口、光效同步
*/
async createToolWindow(config: ToolWindowConfig): Promise<window.Window | null> {
try {
if (!this.mainWindow) {
throw new Error('Main window not initialized');
}
const subWindow = await this.mainWindow.createSubWindow(config.name);
// 配置浮动窗口属性
await subWindow.setWindowSizeType(window.WindowSizeType.FREE);
await subWindow.moveWindowTo({ x: config.x ?? 100, y: config.y ?? 100 });
await subWindow.resize(config.width, config.height);
await subWindow.setWindowBackgroundColor('#00000000');
await subWindow.setWindowShadowEnabled(true);
await subWindow.setWindowCornerRadius(16);
await subWindow.setWindowTopmost(true); // 置顶
// 存储并加载内容
this.subWindows.set(config.name, subWindow);
await subWindow.setUIContent(`pages/${config.name}`);
await subWindow.showWindow();
// 如果配置跟随,设置窗口联动
if (config.followMainWindow) {
this.setupWindowFollow(subWindow, config);
}
// 监听子窗口焦点,同步光效
this.syncSubWindowLightEffect(subWindow, config.name);
console.info(`Tool window created: ${config.name}`);
return subWindow;
} catch (error) {
console.error(`Failed to create tool window:`, (error as BusinessError).message);
return null;
}
}
/**
* 设置窗口跟随(主窗口移动时子窗口跟随)
*/
private setupWindowFollow(subWindow: window.Window, config: ToolWindowConfig): void {
this.mainWindow?.on('windowRectChange', (data: window.RectChangeOptions) => {
if (data.rectChangeReason === window.RectChangeReason.MOVE) {
const mainRect = this.mainWindow?.getWindowProperties().windowRect;
if (mainRect) {
subWindow.moveWindowTo({
x: mainRect.left + (config.x ?? 100),
y: mainRect.top + (config.y ?? 100)
});
}
}
});
}
/**
* 同步子窗口光效(与主窗口主题色联动)
*/
private syncSubWindowLightEffect(subWindow: window.Window, name: string): void {
subWindow.on('windowFocusChange', (isFocused: boolean) => {
// 通过AppStorage同步光效状态
AppStorage.setOrCreate(`window_${name}_focused`, isFocused);
// 子窗口激活时触发脉冲光效
if (isFocused) {
AppStorage.setOrCreate('global_light_pulse', Date.now());
}
});
}
/**
* 全局光效同步(主题色变化时通知所有窗口)
*/
async syncGlobalLightEffect(color: string): Promise<void> {
AppStorage.setOrCreate('global_theme_color', color);
// 可选:通过窗口间通信直接发送
for (const [name, win] of this.subWindows) {
console.info(`Syncing light effect to window: ${name}`);
}
}
async closeToolWindow(name: string): Promise<void> {
const subWindow = this.subWindows.get(name);
if (subWindow) {
await subWindow.destroyWindow();
this.subWindows.delete(name);
}
}
}
4.4 主页面:光效联动与自适应渲染
代码亮点 :主页面整合沉浸光感标题栏与悬浮页签,实现工作区切换时的全屏光效过渡。通过 expandSafeArea 将背景延伸至非安全区,配合动态光晕营造沉浸式体验 。
typescript
// pages/Index.ets
import { ImmersiveTitleBar } from '../components/ImmersiveTitleBar';
import { FloatTabNavigation, TransparencyLevel } from '../components/FloatTabNavigation';
import { WindowManager } from '../utils/WindowManager';
@Entry
@Component
struct WorkbenchPage {
@State currentWorkspace: number = 0;
@State themeColor: string = '#4A90E2';
@State lightIntensity: number = 0.6;
@State isNightMode: boolean = false;
// 工作区页面映射
private workspacePages: string[] = [
'pages/DocumentWorkspace',
'pages/SheetWorkspace',
'pages/SlideWorkspace',
'pages/BoardWorkspace'
];
aboutToAppear(): void {
// 监听全局主题色变化
AppStorage.watch('global_theme_color', (newColor: string) => {
this.themeColor = newColor;
this.triggerAmbientLightTransition();
});
// 监听夜间模式
AppStorage.watch('night_mode', (isNight: boolean) => {
this.isNightMode = isNight;
this.lightIntensity = isNight ? 0.3 : 0.6;
});
}
/**
* 触发环境光过渡动画
*/
private triggerAmbientLightTransition(): void {
// 通过状态变化触发ArkUI自动动画
this.lightIntensity = 1.0;
setTimeout(() => {
this.lightIntensity = this.isNightMode ? 0.3 : 0.6;
}, 300);
}
build() {
Stack() {
// 第一层:动态环境光背景(全屏沉浸)
this.buildAmbientLightLayer()
// 第二层:内容层
Column() {
// 沉浸光感标题栏
ImmersiveTitleBar({ currentWorkspace: this.currentWorkspace })
// 工作区内容(通过Tabs切换)
Tabs({ index: this.currentWorkspace, barPosition: BarPosition.Start }) {
TabContent() { DocumentWorkspace() }
TabContent() { SheetWorkspace() }
TabContent() { SlideWorkspace() }
TabContent() { BoardWorkspace() }
}
.width('100%')
.layoutWeight(1)
.backgroundColor('transparent')
.onChange((index: number) => {
this.currentWorkspace = index;
})
}
.width('100%')
.height('100%')
// 第三层:悬浮页签导航(叠加在最上层)
FloatTabNavigation({
currentIndex: this.currentWorkspace,
onTabChange: (index: number) => {
this.currentWorkspace = index;
},
contentBuilder: () => {} // 内容已在Tabs中渲染
})
}
.width('100%')
.height('100%')
.backgroundColor('#0a0a0f') // 深色基底增强光效对比
// 关键:扩展至所有安全区边缘,实现全屏沉浸
.expandSafeArea(
[SafeAreaType.SYSTEM],
[SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM, SafeAreaEdge.START, SafeAreaEdge.END]
)
}
/**
* 构建环境光背景层
* 三层光效叠加:主光晕 + 辅助光点 + 呼吸动画
*/
@Builder
buildAmbientLightLayer(): void {
Column() {
// 主光晕(随主题色变化)
Column()
.width(400)
.height(400)
.backgroundColor(this.themeColor)
.blur(150)
.opacity(this.lightIntensity * 0.4)
.position({ x: '50%', y: '20%' })
.anchor('50%')
.animation({
duration: 6000,
curve: Curve.EaseInOut,
iterations: -1,
playMode: PlayMode.Alternate
})
.scale({ x: 1.3, y: 1.3 })
// 辅助光点(对角线分布)
ForEach([1, 2, 3], (item: number) => {
Column()
.width(100 + item * 60)
.height(100 + item * 60)
.backgroundColor(this.adjustHue(this.themeColor, item * 30))
.blur(80)
.opacity(this.lightIntensity * (0.2 - item * 0.05))
.position({
x: `${20 + item * 25}%`,
y: `${50 + item * 15}%`
})
.animation({
duration: 4000 + item * 1000,
curve: Curve.EaseInOut,
iterations: -1,
playMode: PlayMode.AlternateReverse
})
})
// 底部反射光(增强空间感)
Column()
.width('100%')
.height(200)
.backgroundColor(this.themeColor)
.opacity(this.lightIntensity * 0.1)
.blur(100)
.position({ x: 0, y: '80%' })
.linearGradient({
direction: GradientDirection.Top,
colors: [
[this.themeColor, 0.0],
['transparent', 1.0]
]
})
}
.width('100%')
.height('100%')
.backgroundColor(this.isNightMode ? '#050508' : '#0a0a0f')
}
/**
* 简单的色相调整(实际项目使用颜色工具库)
*/
private adjustHue(color: string, degree: number): string {
// 简化处理,实际应使用HSL转换
return color;
}
}
五、关键技术总结
5.1 沉浸光感实现清单
| 技术点 | API/方法 | 应用场景 |
|---|---|---|
| 系统材质效果 | systemMaterialEffect: SystemMaterialEffect.IMMERSIVE |
HdsNavigation/HdsTabs 标题栏与页签 |
| 背景模糊 | backgroundBlurStyle(BlurStyle.REGULAR) |
玻璃拟态容器背景 |
| 背景滤镜 | backdropBlur($r('sys.blur.20')) |
更精细的模糊控制 |
| 安全区扩展 | expandSafeArea([SafeAreaType.SYSTEM], [...]) |
内容延伸至状态栏/导航栏 |
| 窗口沉浸 | setWindowLayoutFullScreen(true) |
全屏布局模式 |
| 光效动画 | animation({ duration, iterations: -1 }) |
呼吸灯/脉冲效果 |
5.2 悬浮导航适配要点
- 安全区避让 :通过
getWindowAvoidArea获取底部导航指示器高度,内容区域动态预留空间 - 悬浮层级设计 :导航栏脱离底边,使用
margin({ left: '4%', right: '4%', bottom: avoidHeight + 16 })实现四周留白 - 透明度动态调节 :支持强/平衡/弱三档,通过
Slider或预设按钮实时调整 - 手势融合:长按展开透明度面板,与系统全面屏手势无缝衔接
5.3 PC 端多窗口光效协同
- 主窗口:全屏沉浸,环境光背景延伸至所有安全区边缘
- 浮动工具窗口:置顶、圆角、阴影,跟随主窗口移动
- 光效同步 :通过
AppStorage全局状态实现跨窗口主题色联动 - 焦点感知:窗口激活时边缘发光增强,失活时自动降低光效强度,减少视觉干扰
六、调试与性能优化
6.1 真机调试建议
- 玻璃拟态效果:在模拟器上可能显示异常,建议在支持 HarmonyOS 6 的真机或 PC 模拟器上测试
- 光效性能 :大范围
blur(150)消耗 GPU 资源,建议:- 夜间模式降低光效刷新率
- 窗口失活时暂停呼吸动画
- 使用
visibility控制不可见区域的光效渲染
6.2 无障碍与降级适配
typescript
// 高对比度模式降级:玻璃拟态效果自动转为纯色背景
if (AppStorage.get<boolean>('high_contrast_mode')) {
this.navTransparency = 1.0; // 不透明
// 移除模糊效果,使用纯色替代
}
七、总结与展望
本文基于 HarmonyOS 6(API 23)的 沉浸光感 与 悬浮导航 特性,完整实战了一款面向 PC 端的"光影工作台"多窗口协作应用。核心要点总结:
- HDS 系统材质 :通过
systemMaterialEffect.IMMERSIVE为标题栏和页签带来物理光照级的光晕与反射效果,告别死板的纯色背景 - 悬浮卡片导航:底部页签脱离边界,以圆角玻璃卡片形态悬浮,支持三档透明度自适应调节,内容区域智能避让
- PC 多窗口光效协同:主窗口与浮动工具窗口通过全局状态同步主题色,激活/失活状态自动调整光效强度,打造专业级多任务协作体验
- 全屏沉浸布局 :利用
expandSafeArea将背景光效延伸至状态栏和导航栏,配合动态环境光层营造"无界"视觉体验
未来扩展方向:
- 结合 HarmonyOS PC 的自由窗口管理,实现工作区的拖拽分屏与光效联动
- 接入 分布式软总线,跨设备同步光效主题(手机、平板、PC 统一视觉风格)
- 探索 Face AR 能力,通过摄像头捕捉用户视线,动态调整界面光效焦点区域
转载自:https://blog.csdn.net/u014727709/article/details/157390973
欢迎 👍点赞✍评论⭐收藏,欢迎指正