【open harmony/harmonyos】ArkUI 玻璃拟态弹层实战:打造轻量级节点详情面板
【open harmony/harmonyos】ArkUI 玻璃拟态弹层实战:打造轻量级节点详情面板
前言 ✨
在 HarmonyOS / OpenHarmony 应用中,弹层是非常常见的交互形式。
比如:
- 新增内容
- 查看详情
- 编辑信息
- 二次确认
- 快捷操作
如果弹层只是普通白底卡片,当然能用,但在沉浸式界面中会显得比较割裂。我的项目 星图 Xingtu 是一个全屏 3D 知识星图应用,背景是深色空间和发光节点,所以弹层也不能做得太“传统”。
这篇文章会结合项目中的节点详情弹层、创建节点弹层、词图生成弹层,分享如何使用 ArkUI 实现玻璃拟态风格的轻量级浮层。🧊
一、为什么选择玻璃拟态弹层
星图应用的主界面是一个全屏空间场景:
- 背景是深色渐变
- 节点有发光效果
- 底部是悬浮导航栏
- 顶部有悬浮操作区
如果这时候弹出一个完全不透明的大卡片,会遮挡场景,也会破坏沉浸感。
所以项目里的弹层采用了这种设计:
- 半透明背景
- 背景模糊
- 细描边
- 大圆角
- 轻阴影
- 内容简洁
它不是跳转页面,而是覆盖在星图上方,让用户始终感觉自己还在同一个空间里。🌌
二、弹层组件拆分
项目中主要有三个弹层:
XingtuNodeSheet:节点详情弹层XingtuCreateNodeSheet:新增节点弹层XingtuGenerateWordMapSheet:词图生成弹层
它们的职责不同,但视觉风格保持一致:
- 节点详情:展示标题、备注、标签、连接、删除、关闭
- 新增节点:输入名称、备注、标签
- 词图生成:输入主题和关键词,生成星图
这种拆分方式比较清晰。每个弹层只关注自己的业务,不把所有逻辑塞进一个巨大组件里。
三、节点详情弹层结构
先看节点详情弹层 XingtuNodeSheet。
@Component
export struct XingtuNodeSheet {
node: XingtuNode | null = null;
linking: boolean = false;
onClose: () => void = () => {};
onLink: (nodeId: string) => void = () => {};
onDelete: (nodeId: string) => void = () => {};
build() {
if (!this.node) {
Column() {}
} else {
Column({ space: 10 }) {
Text(this.node.title)
.fontSize(24)
.fontWeight(FontWeight.Bold)
Text(this.node.note.length > 0 ? this.node.note : '暂无备注')
Text(this.node.tags.length > 0 ? this.node.tags.join(' / ') : '暂无标签')
Row({ space: 12 }) {
Button(this.linking ? '连线中' : '连接')
Button('删除')
Button('关闭')
}
}
}
}
}
这里有一个小细节:当 node 为空时,组件返回一个空 Column,不展示内容。
这样外层可以始终挂载这个组件,只通过选中节点来控制它是否显示。
四、玻璃拟态样式核心 🧊
节点详情弹层的高级感主要来自下面这组样式:
.padding({ left: 18, right: 18, top: 18, bottom: 18 })
.borderRadius(28)
.backgroundColor(XingtuTheme.materialGlass)
.backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THIN)
.border({ width: 1, color: XingtuTheme.materialStroke })
.shadow({
radius: 28,
color: XingtuTheme.softShadow,
offsetX: 0,
offsetY: 12
})
几个关键点:
backgroundColor使用半透明颜色backgroundBlurStyle增加背景模糊border用低透明描边强化边界shadow让浮层从背景中浮起来borderRadius(28)让弹层更柔和
这几个属性组合起来,就能做出比较自然的玻璃拟态效果。
五、统一主题色管理 🎨
为了避免样式散落在各个组件里,项目把颜色统一放进 XingtuTheme。
export class XingtuTheme {
static readonly materialGlass: string = '#29FFFFFF';
static readonly materialStroke: string = '#24FFFFFF';
static readonly surfaceElevated: string = '#2EFFFFFF';
static readonly textPrimary: string = '#F8FAFC';
static readonly textSecondary: string = '#A7B3C7';
static readonly primaryAction: string = '#F8FAFC';
static readonly primaryActionText: string = '#0B1220';
static readonly destructive: string = '#FF453A';
static readonly softShadow: string = '#24000000';
}
这样有几个好处:
- 多个弹层风格统一
- 后续调整主题更方便
- 颜色含义更清楚
- 不容易出现“同一个白色写了五种透明度”的问题
做 UI 文章时,这类主题色封装也很值得展示,因为它能体现项目不是随手堆样式,而是有设计规范的。
六、新增节点弹层
新增节点弹层 XingtuCreateNodeSheet 负责收集用户输入。
@State title: string = '';
@State note: string = '';
@State tagsText: string = '';
private createNode(): void {
const parsedTags: string[] = this.tagsText
.split(',')
.map((item: string) => item.trim())
.filter((item: string) => item.length > 0);
this.onCreate(this.title.trim(), this.note.trim(), parsedTags);
this.title = '';
this.note = '';
this.tagsText = '';
}
这里把标签输入设计成逗号分隔,提交时再转换成数组。
表单提交后会清空本地状态,这样下次打开弹层时不会残留旧内容。
七、输入框样式处理
为了和整体视觉一致,输入框也使用了半透明背景和圆角:
TextInput({ text: this.title, placeholder: '名称' })
.height(46)
.borderRadius(14)
.backgroundColor(XingtuTheme.surfaceElevated)
.fontColor(XingtuTheme.textPrimary)
.onChange((value: string) => this.title = value)
这里的重点不是复杂,而是统一:
- 输入框高度一致
- 圆角一致
- 背景色一致
- 字体颜色一致
当一个弹层里有多个输入项时,统一的规格会明显提升页面质感。
八、操作按钮分层
弹层中通常会有多个按钮,比如:
- 取消
- 创建
- 删除
- 连接
- 关闭
项目中使用不同颜色区分按钮优先级:
Button('创建')
.layoutWeight(1)
.height(44)
.backgroundColor(XingtuTheme.primaryAction)
.fontColor(XingtuTheme.primaryActionText)
Button('删除')
.layoutWeight(1)
.height(44)
.backgroundColor(XingtuTheme.surfaceElevated)
.fontColor(XingtuTheme.destructive)
主操作使用亮色背景,危险操作使用红色文字,普通操作使用半透明背景。
这样用户不用仔细阅读,也能快速判断哪个操作最重要、哪个操作需要谨慎。
九、弹层在主界面中的挂载
这些弹层最终都挂载在 XingtuAppShell 的 Stack 中。
Stack({ alignContent: Alignment.Bottom }) {
this.buildHdsTabs()
this.buildPrimaryAddButton()
XingtuNodeSheet({
node: this.selectedNode(),
linking: this.store.linkingSourceId !== null,
onClose: () => {
this.store.selectNode(null);
this.store.linkingSourceId = null;
this.revision += 1;
}
})
.margin({ left: 16, right: 16, bottom: this.overlayBottomInset() })
XingtuCreateNodeSheet({ ... })
XingtuGenerateWordMapSheet({ ... })
}
这里用 overlayBottomInset() 控制弹层距离底部导航的间距,避免弹层和悬浮 Tab 重叠。
private overlayBottomInset(): number {
return this.isImmersiveGraphSurface() ? 96 : 112;
}
这个细节在沉浸式界面里很重要:浮层多了以后,最容易出问题的不是样式,而是层级和避让。
十、体验总结 🌟
通过这套设计,项目中的弹层具备了几个特点:
- 不跳转页面,保持沉浸感
- 玻璃拟态风格,和星图背景融合
- 输入、详情、生成弹层风格统一
- 主按钮、普通按钮、危险按钮层级清晰
- 弹层和底部悬浮导航保持安全距离
对于 HarmonyOS / OpenHarmony 应用来说,弹层不仅是功能容器,也可以成为提升视觉质感的重要部分。
如果你的应用本身是沉浸式、深色、高级感路线,可以尝试用 backgroundBlurStyle、半透明背景、细描边和轻阴影组合出这种玻璃拟态浮层。✨

更多推荐


所有评论(0)