01 鸿蒙原生 ArkTS 毛玻璃效果 Column + Blur 层叠布局实战


一、引言
毛玻璃效果(Glassmorphism / Frosted Glass)是近年来移动端和桌面端 UI 设计中非常流行的视觉风格。它通过半透明背景配合高斯模糊,营造出"透过磨砂玻璃观看后方内容"的质感,兼具层次感与高级感。
在 HarmonyOS NEXT 6.1.1(API 24)中,ArkUI 框架提供了原生的 .blur() 高斯模糊 API 和 backgroundBlurStyle 系统级模糊样式,使得在鸿蒙应用中实现毛玻璃效果变得简单高效。本文将以一个完整的示例应用为线索,深入讲解如何使用 Column + blur + backgroundColor 这三大核心技术,构建纵向层叠的毛玻璃卡片布局。
我们将从最基础的布局原理开始,逐步深入到每个 API 的细节用法、参数调优技巧、性能优化方案以及多种实际场景的扩展实践。无论你是刚接触鸿蒙开发的初学者,还是希望提升 UI 质感的中级开发者,这篇文章都能为你提供全面、可落地的技术指导。
二、项目背景与效果预览
2.1 什么是毛玻璃效果
毛玻璃效果的核心视觉特征是:
| 特征 | 描述 | 实现方式 |
|---|---|---|
| 半透明背景 | 内容透过半透明层显示,产生层次感 | backgroundColor + alpha 通道 |
| 背景模糊 | 透过半透明层看到的内容被模糊处理 | blur() API |
| 玻璃边缘 | 半透明亮边框营造玻璃厚度感 | border + rgba 高光色 |
| 悬浮阴影 | 卡片与背景之间产生空间距离 | shadow 属性 |
2.2 示例应用的视觉效果
本文构建的示例应用呈现以下视觉效果:
- 深色渐变背景:从深蓝黑(#0f0c29)到深紫蓝(#302b63)再到深紫(#24243e)的纵向渐变,为毛玻璃提供丰富的视觉底层
- 装饰光晕:使用粉色和蓝色圆形元素配合大模糊值(blur(80)、blur(60))模拟发光效果,增强背景层次
- 四张毛玻璃卡片:每张卡片以 Column 布局承载内容,使用半透明白色底色 + blur(20) + 半透明边框,呈现典型的毛玻璃质感
- 阶梯层叠:卡片之间通过 position 偏移(每张向下 16vp)形成纵向层叠,类似卡片堆叠的视觉效果
- 入场动画:卡片从下方滑入并渐显,提升用户体验
2.3 适用场景
这种布局非常适合以下场景:
- 音乐播放器的播放列表 / 专辑封面展示
- 个人中心页面的信息卡片
- 系统设置面板的选项分组
- 数据看板的指标卡片
- 社交应用的用户资料卡片
- 任何需要突出层次感和高级感的 UI 界面
三、开发环境准备
3.1 环境要求
在开始编码之前,请确保开发环境满足以下要求:
| 项目 | 要求 |
|---|---|
| 操作系统 | Windows 10/11 64 位 |
| IDE | DevEco Studio 5.0 及以上版本 |
| SDK | HarmonyOS NEXT 6.1.1(API 24) |
| 构建工具 | hvigor(内置在 DevEco Studio 中) |
| 目标设备 | 鸿蒙手机 / 平板 / 模拟器 |
3.2 创建项目
在 DevEco Studio 中创建一个新的 Empty Ability 项目:
- 打开 DevEco Studio,点击 “Create Project”
- 选择 “Empty Ability” 模板
- 配置项目名称(如 MyApplication)
- 确保 Compile SDK 选择 API 24(HarmonyOS NEXT 6.1.1)
- 点击 Finish 完成创建
3.3 项目结构概览
创建完成后,项目的核心结构如下:
MyApplication/
├── entry/
│ └── src/
│ └── main/
│ ├── ets/
│ │ ├── entryability/
│ │ │ └── EntryAbility.ets # 应用入口 Ability
│ │ └── pages/
│ │ └── BlurColumnPage.ets # 毛玻璃布局页面(我们即将创建)
│ ├── resources/
│ │ └── base/
│ │ ├── element/ # 颜色、字符串等资源
│ │ ├── media/ # 图片资源
│ │ └── profile/
│ │ └── main_pages.json # 页面路由配置
│ └── module.json5 # 模块配置
├── build-profile.json5 # 项目构建配置
└── oh-package.json5 # 依赖管理
3.4 配置页面路由
在 entry/src/main/resources/base/profile/main_pages.json 中注册页面路由:
{
"src": [
"pages/BlurColumnPage"
]
}
3.5 设置应用入口
在 EntryAbility.ets 中,将 windowStage.loadContent() 的路径指向我们的新页面:
// entryability/EntryAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
// ... 其他生命周期方法省略
onWindowStageCreate(windowStage: window.WindowStage): void {
hilog.info(0x0000, 'MyApp', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/BlurColumnPage', (err) => {
if (err.code) {
hilog.error(0x0000, 'MyApp',
'Failed to load content: %{public}s', JSON.stringify(err));
return;
}
hilog.info(0x0000, 'MyApp', 'Succeeded in loading content.');
});
}
}
至此,环境准备完成。下面我们进入核心技术的学习。
四、核心技术深度解析
4.1 Column 容器布局原理
Column 是 ArkUI 中最基础的布局容器之一,它的主轴(Main Axis)方向为垂直方向,交叉轴(Cross Axis)方向为水平方向。
4.1.1 Column 的坐标系统
理解 Column 的坐标系统是掌握布局的基础:
┌─────────────────────────┐
│ 交叉轴 │
│ ←──────────────→ │
│ ┌───────┐ ┌───────┐ │ ↑
│ │ 子组件 │ │ 子组件 │ │ │
│ │ A │ │ B │ │ 主
│ └───────┘ └───────┘ │ 轴
│ ┌──────────────────┐ │ │
│ │ 子组件 C │ │ │
│ └──────────────────┘ │ ↓
└─────────────────────────┘
4.1.2 主轴控制:justifyContent
justifyContent 控制子组件在主轴(垂直方向)上的排列方式:
Column() {
// 子组件...
}
.justifyContent(FlexAlign.Start) // 顶部起始排列
// .justifyContent(FlexAlign.Center) // 居中对齐
// .justifyContent(FlexAlign.End) // 底部对齐
// .justifyContent(FlexAlign.SpaceBetween) // 均匀分布,首尾靠边
4.1.3 交叉轴控制:alignItems
alignItems 控制子组件在交叉轴(水平方向)上的对齐方式:
Column() {
// 子组件...
}
.alignItems(HorizontalAlign.Start) // 左对齐
// .alignItems(HorizontalAlign.Center) // 居中对齐
// .alignItems(HorizontalAlign.End) // 右对齐
4.1.4 子组件在 Column 中的占位
在 Column 中,如果子组件没有显式设置宽度,默认会尝试撑满父容器的宽度(视子组件类型而定)。Text、Button 等组件默认自适应内容宽度,Column 自身默认撑满父容器宽度。
这个行为在毛玻璃卡片布局中很重要——我们通常希望卡片撑满可用宽度,所以会给 Column 显式设置 .width('100%')。
4.2 blur() 高斯模糊 API 详解
.blur() 是 ArkUI 中用于对组件及其内容应用高斯模糊效果的 API。它是实现毛玻璃效果最核心的 API。
4.2.1 基本语法
Component()
.blur(radius: number)
其中 radius 参数为模糊半径,单位是 vp(虚拟像素),取值范围通常为 0~100。数值越大,模糊效果越强烈。
4.2.2 模糊半径的视觉效果
| 模糊半径 | 视觉效果 | 适用场景 |
|---|---|---|
| 1~4 | 极轻微模糊,消除锯齿感 | 标签、小元素背景 |
| 5~15 | 中等模糊,可见纹理但内容模糊 | 毛玻璃卡片主体 |
| 16~30 | 强模糊,仅能感知颜色和光影 | 主要毛玻璃面板 |
| 31~60 | 极强模糊,仅剩色彩区域 | 光晕、装饰效果 |
| 60+ | 几乎完全虚化,只剩颜色 | 背景光晕、氛围光 |
4.2.3 blur 的工作机制
理解 blur 的工作机制对于用好它至关重要:
-
blur 是对组件自身内容及其背景的模糊:当对一个 Column 应用
.blur(20)时,Column 内部所有子组件以及 Column 的 backgroundColor 都会被模糊处理 -
blur 与 backgroundColor 的协同:如果 Column 的 backgroundColor 是纯色(alpha=1),模糊效果只能模糊内部的子组件;如果 backgroundColor 是半透明的(alpha<1),模糊效果会同时模糊透过半透明区域看到的父容器背景内容——这正是毛玻璃效果的实现原理
-
blur 不影响子组件的交互:模糊是纯粹的视觉效果,不影响子组件的触摸事件和点击响应
4.2.4 backgroundBlurStyle 系统级模糊
除了 .blur() API,ArkUI 还提供了 backgroundBlurStyle 属性,用于快速应用系统级的背景模糊样式:
Component()
.backgroundBlurStyle(BlurStyle.Thin) // 轻薄模糊
// .backgroundBlurStyle(BlurStyle.Regular) // 常规模糊
// .backgroundBlurStyle(BlurStyle.Thick) // 厚重模糊
backgroundBlurStyle 与 .blur() 的区别在于:
| 对比维度 | .blur() | backgroundBlurStyle |
|---|---|---|
| 控制粒度 | 精确到数值 | 系统预设的枚举值 |
| 灵活性 | 高,可精确调参 | 低,只有几种固定样式 |
| 使用简便性 | 需手动配半透明底色 | 自动处理背景样式 |
| 兼容性 | API 6+ 通用 | API 12+ 新增 |
在本文的示例中,我们使用 .blur() 以获得更精确的控制。
4.3 backgroundColor 与 alpha 通道
backgroundColor 配合 alpha 通道(透明度)是实现毛玻璃效果的另一关键。
4.3.1 rgba 颜色表示法
在 ArkTS 中,颜色可以使用多种表示法:
// 十六进制(不支持 alpha)
.backgroundColor('#ffffff')
// rgba 格式(支持 alpha,取值范围 0~1)
.backgroundColor('rgba(255, 255, 255, 0.12)')
// 十六进制 + alpha(8 位十六进制,前 6 位是 RGB,后 2 位是 alpha)
.backgroundColor('#1affffff') // alpha = 0.10
其中 rgba 格式是最直观的,四个参数分别代表红、绿、蓝、透明度。透明度 alpha 取值范围 0(完全透明)到 1(完全不透明)。
4.3.2 毛玻璃中的 alpha 取值策略
alpha 值的选择直接影响毛玻璃的视觉效果:
alpha = 0.05 ~ 0.10 → 极淡,背景几乎完全透出,模糊感弱
alpha = 0.10 ~ 0.20 → 适中,背景清晰可见,模糊感明显 ✓ 推荐
alpha = 0.20 ~ 0.35 → 较浓,背景隐约可见,更像彩色玻璃
alpha = 0.35 ~ 0.50 → 浓厚,背景仅剩光影,倾向于纯色面板
alpha = 0.50+ → 不再有毛玻璃感,接近纯色不透明
对于大多数毛玻璃效果场景,alpha 值在 0.10~0.20 之间效果最佳。太低了玻璃感不足,太高了又失去了"透过玻璃看背景"的核心特征。
4.4 Stack 层叠容器
Stack 是 ArkUI 中用于实现层叠布局的容器,它的子组件按照添加顺序从下往上堆叠。
4.4.1 Stack 的基本使用
Stack() {
// 先添加的组件在底层
Column() { /* 背景层 */ }
.width('100%')
.height('100%')
// 后添加的组件在上层
Column() { /* 前景层 */ }
.width('100%')
.height('100%')
}
在毛玻璃布局中,Stack 的层叠特性是实现"背景透过前景显示"效果的基础架构。
4.4.2 Stack 的对齐方式
Stack 通过 alignContent 属性控制子组件在容器内的对齐方式:
Stack()
.alignContent(Alignment.Center) // 默认居中
.alignContent(Alignment.TopStart) // 左上角
.alignContent(Alignment.BottomEnd) // 右下角
4.5 综合:毛玻璃效果的技术原理
结合以上三个核心技术,毛玻璃效果的实现原理可以概括为:
┌── Stack 层叠容器 ──────────────────────────┐
│ │
│ [底层] 背景层 │
│ ├── 渐变色 / 图片 │
│ └── 装饰光晕 │
│ │
│ [上层] 毛玻璃卡片 │
│ ├── backgroundColor: rgba(白, 0.12) │
│ │ ← 半透明底色, 让下方背景透过来 │
│ ├── blur(20) │
│ │ ← 模糊透过来的背景内容 │
│ └── border: rgba(白, 0.25) │
│ ← 玻璃边缘高光 │
│ │
│ 视觉结果:透过磨砂玻璃看到模糊后的背景 │
│ │
└──────────────────────────────────────────────┘
核心公式:毛玻璃效果 = Stack(背景) + Column(半透明底色 + blur + 边框)
五、完整代码实现
下面我们逐段构建完整的毛玻璃布局页面。建议在 DevEco Studio 中创建 BlurColumnPage.ets 文件并跟随编写。
5.1 数据模型定义
首先定义毛玻璃卡片的数据结构:
/**
* 数据模型:毛玻璃卡片数据
* icon : 卡片图标(使用 Emoji 作为图标,无需图片资源)
* title : 卡片标题
* desc : 卡片描述文字
* tag : 右上角标签
*/
interface GlassCardItem {
icon: string; // 图标(emoji)
title: string; // 标题
desc: string; // 描述内容
tag: string; // 标签
}
使用 interface 定义数据结构是 ArkTS 中的标准做法。这里我们使用 Emoji 作为图标,避免了对图片资源的外部依赖,使示例更加轻量。
5.2 毛玻璃卡片子组件(GlassCard)
这是整个布局的核心组件,实现了毛玻璃效果的三个关键技术点:
@Component
struct GlassCard {
private item: GlassCardItem = { icon: '', title: '', desc: '', tag: '' };
/** 层叠偏移量:越靠后的卡片偏移越大,营造纵深感 */
private offsetY: number = 0;
/** 卡片索引(用于渐变动画延迟等) */
private index: number = 0;
build() {
// ── 最外层 Column:承载整张毛玻璃卡片 ──
Column() {
// ======== 第 1 行:图标 + 标题 + 标签(横向排列) ========
Row() {
// 图标区域:使用大号 Emoji 作为装饰
Text(this.item.icon)
.fontSize(28)
.lineHeight(34)
// 标题文字:白色粗体
Text(this.item.title)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#ffffff')
.lineHeight(26)
.margin({ left: 10 })
// 弹性空间:将标签推到行尾
Blank()
// 标签气泡:也带了毛玻璃效果(半透明背景 + 弱模糊)
Text(this.item.tag)
.fontSize(11)
.fontColor('#ffffff')
.fontWeight(FontWeight.Medium)
.padding({ left: 10, right: 10, top: 3, bottom: 3 })
.backgroundColor('#33ffffff') // 半透明白色
.borderRadius(20) // 圆角气泡
.blur(2) // 微弱模糊
}
.alignItems(VerticalAlign.Center)
.width('100%')
// ======== 第 2 行:描述文字 ========
Text(this.item.desc)
.fontSize(14)
.fontColor('#e0e6ff') // 浅蓝白色,与深色背景协调
.lineHeight(22)
.margin({ top: 10 })
.width('100%')
.textAlign(TextAlign.Start)
}
// ==========================================================
// ★★★ 核心:毛玻璃(Frosted Glass)三要素 ★★★
// ① backgroundColor('rgba(255,255,255,0.12)') — 半透明底色
// ② .blur(20) — 高斯模糊,模拟玻璃透光效果
// ③ .border + rgba 亮边框 — 模拟玻璃边缘高光
// ==========================================================
.alignItems(HorizontalAlign.Start)
.width('100%')
.padding(18)
// ── ① 半透明白色底色:透过背景显示下方内容 ──
.backgroundColor('rgba(255, 255, 255, 0.12)')
// ── ② 高斯模糊:使卡片背景呈现"毛玻璃"质感 ──
.blur(20)
// ── ③ 玻璃边缘高光边框 ──
.border({
width: 1,
color: 'rgba(255, 255, 255, 0.25)', // 半透明白色边框
style: BorderStyle.Solid
})
// ── 其他视觉属性 ──
.borderRadius(20) // 大圆角增强玻璃质感
.shadow({
radius: 20, // 大阴影半径,柔和扩散
color: 'rgba(0, 0, 0, 0.3)', // 黑色半透明阴影
offsetX: 0, // 无水平偏移
offsetY: 8 // 垂直向下偏移,产生悬浮感
})
// ── 层叠偏移:每张卡片相对前一张偏移一定距离 ──
.position({ top: this.offsetY })
// ── 动画:页面加载时卡片从下方滑入 ──
.transition({
type: TransitionType.Insert,
translate: { x: 0, y: 60 }, // 从下方 60vp 处开始
opacity: 0, // 初始透明度为 0
duration: 400, // 动画时长 400ms
curve: Curve.Friction // 衰减曲线,自然减速
})
}
}
关键代码详解
第 96 行 — 半透明底色:
.backgroundColor('rgba(255, 255, 255, 0.12)')
这里的 alpha 值 0.12 是经过调试选择的"黄金比例"。如果改为 0.06,卡片会太透明,背景几乎完全透出,毛玻璃感不足;如果改为 0.25,卡片又会太厚重,失去了轻盈感。
第 98 行 — 高斯模糊:
.blur(20)
blur(20) 是中等偏强的模糊程度。在这个值下,透过半透明区域看到的背景图文会明显模糊,但又保留了大致的色彩和明暗分布,呈现出"透过磨砂玻璃"的视觉效果。
第 100~104 行 — 玻璃边缘高光:
.border({
width: 1,
color: 'rgba(255, 255, 255, 0.25)',
style: BorderStyle.Solid
})
厚度仅为 1vp 的半透明边框,模拟了真实玻璃的边缘高光。想象一下真实世界中的玻璃杯——边缘处的玻璃更厚,光线透过时会呈现出亮边。这个 1px 的边框就是在模拟这个物理现象。
第 115 行 — 层叠偏移:
.position({ top: this.offsetY })
每个卡片实例通过 offsetY 参数在垂直方向产生不同的偏移量,从而实现阶梯式层叠效果。这个偏移量由父组件在 ForEach 循环中计算传入。
5.3 配置数据
在主页面组件中,我们定义四张卡片的数据:
private readonly cardList: GlassCardItem[] = [
{
icon: '🎵',
title: '正在播放',
desc: 'Blur — 毛玻璃效果让背景音乐封面透过卡片若隐若现',
tag: 'NOW'
},
{
icon: '📂',
title: '最近项目',
desc: '鸿蒙 ArkTS 示例:Column + Blur 层叠布局 · v1.0',
tag: '3 个'
},
{
icon: '⚙️',
title: '系统设置',
desc: '显示 · 声音 · 通知 · 隐私 · 所有设置项均采用毛玻璃面板',
tag: '设置'
},
{
icon: '📊',
title: '数据看板',
desc: 'CPU 45% · 内存 62% · 存储 128G/256G · 网络 正常',
tag: '实时'
}
];
四个数据项覆盖了音乐、文件、设置、数据仪表盘四种常见场景,代码复用性好,读者可以轻松替换为自己的业务数据。
5.4 主页面组件(BlurColumnPage)
主页面是整个布局的"导演",它利用 Stack 层叠容器将背景层和前景层组合在一起:
@Entry
@Component
struct BlurColumnPage {
// ... 数据定义(同上节)
build() {
// ══════════════════════════════════════════════════════════
// 最外层 Stack:承载背景 + 前景毛玻璃卡片层叠
// Stack 允许背景和前景内容相互重叠,毛玻璃才能透过背景
// ══════════════════════════════════════════════════════════
Stack() {
// ======== (1) 背景层:渐变色 + 装饰元素 ========
this.BackgroundLayer()
// ======== (2) 前景层:毛玻璃卡片 + Column 层叠 ========
this.ForegroundLayer()
}
.width('100%')
.height('100%')
}
/**
* 背景层构建:线性渐变 + 装饰光晕
*/
@Builder
BackgroundLayer() {
Column() {
// 占位容器
Column()
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
.linearGradient({
direction: GradientDirection.Bottom,
colors: [
['#0f0c29', 0], // 顶部:深蓝黑
['#302b63', 0.5], // 中部:深紫蓝
['#24243e', 1] // 底部:深紫
]
})
// 粉色装饰光晕(右上角)
Column()
.width(280)
.aspectRatio(1)
.borderRadius(140)
.backgroundColor('#ff6fd8')
.blur(80) // 大模糊 → 光晕感
.position({ top: 60, right: -40 })
.opacity(0.35)
// 蓝色装饰光晕(左下角)
Column()
.width(200)
.aspectRatio(1)
.borderRadius(100)
.backgroundColor('#00d4ff')
.blur(60)
.position({ bottom: 100, left: -30 })
.opacity(0.25)
}
/**
* 前景层构建:Scroll + Column + GlassCard 层叠列表
*/
@Builder
ForegroundLayer() {
Scroll() {
Column() {
// 页面标题区
this.TitleSection()
// ★ 核心区域:毛玻璃卡片层叠 ★
this.CardStackSection()
// 技术说明面板
this.TechNotesSection()
}
.width('100%')
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.height('100%')
}
@Builder
TitleSection() {
Column() {
Text('✨ 毛玻璃 · 层叠布局')
.fontSize(26)
.fontWeight(FontWeight.Bold)
.fontColor('#ffffff')
.lineHeight(34)
Text('Column + blur + backgroundColor')
.fontSize(13)
.fontColor('rgba(255,255,255,0.6)')
.margin({ top: 6 })
Text('鸿蒙原生 ArkTS 毛玻璃效果(Frosted Glass / Glassmorphism)')
.fontSize(12)
.fontColor('rgba(255,255,255,0.4)')
.margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.width('100%')
.margin({ top: 60, bottom: 20 })
}
@Builder
CardStackSection() {
Column() {
ForEach(this.cardList, (item: GlassCardItem, idx: number) => {
GlassCard({
item: item,
offsetY: idx * 16, // ★ 每张卡片向下偏移 16vp,形成层叠 ★
index: idx
})
}, (item: GlassCardItem) => item.title)
// 底部占位空间
Column()
.width('100%')
.height(40)
}
.alignItems(HorizontalAlign.Start)
.width('100%')
.padding({ left: 20, right: 20 })
}
@Builder
TechNotesSection() {
// 技术说明面板,也使用毛玻璃风格
Column() {
// ... 详细内容见完整代码
}
.alignItems(HorizontalAlign.Start)
.width('100%')
.padding(18)
.backgroundColor('rgba(255, 255, 255, 0.08)')
.blur(10)
.border({ width: 1, color: 'rgba(255,255,255,0.15)' })
.borderRadius(20)
.margin({ left: 20, right: 20, top: 24, bottom: 32 })
}
}
完整代码文件
最终完整的 BlurColumnPage.ets 文件约 375 行,由以下组件构成:
| 组件 | 行数 | 职责 |
|---|---|---|
| GlassCardItem (interface) | 6 | 卡片数据模型 |
| GlassCard (@Component) | ~90 | 毛玻璃卡片 UI 组件 |
| BlurColumnPage (@Entry @Component) | ~170 | 主页容器,组合背景+前景 |
| GlassTipRow (@Component) | ~27 | 说明面板中的要点行组件 |
所有组件在同一个 .ets 文件中定义,方便阅读和调试。在生产项目中,可以根据需要将子组件抽取到单独的文件中。
5.5 技术说明面板(GlassTipRow)
为了帮助读者理解布局原理,我们在页面底部增加了技术说明面板,其中使用了一个辅助子组件 GlassTipRow:
@Component
struct GlassTipRow {
private dotColor: string = '#ffffff';
private text: string = '';
build() {
Row() {
// 圆点指示器(带微弱发光)
Column()
.width(8)
.height(8)
.borderRadius(4)
.backgroundColor(this.dotColor)
.blur(2) // 圆点也带微弱模糊
.margin({ right: 10 })
// 说明文字
Text(this.text)
.fontSize(13)
.fontColor('rgba(255,255,255,0.8)')
.lineHeight(20)
.layoutWeight(1) // 撑满剩余宽度
}
.alignItems(VerticalAlign.Top)
.width('100%')
.margin({ bottom: 10 })
}
}
这个组件展示了毛玻璃风格的另一种应用——即使是小小的指示圆点,配合 blur(2) 和半透明色也能呈现出精致的光泽感。
六、布局原理深度分析
6.1 层叠偏移的计算逻辑
在 CardStackSection 中,每张卡片的偏移量是这样计算的:
offsetY: idx * 16 // idx 从 0 开始,所以偏移量分别为 0, 16, 32, 48
这个偏移量通过 position({ top: offsetY }) 应用到每张卡片上。由于卡片都在同一个 Column 中顺序排列,position 的 top 值使得每张卡片相对于其原本位置向下移动。偏移量递增的规律带来了视觉上的"阶梯层叠"。
为什么要使用 position 而不是 marginTop?
| 方式 | 效果 | 区别 |
|---|---|---|
| marginTop | 增加上方间距 | 卡片间的间距增大,但不会重叠 |
| position({ top }) | 相对于原位置偏移 | 卡片可以重叠,形成层叠效果 |
层叠效果的实现正是利用了 position 偏移可以让元素重叠的特性。配合毛玻璃的半透明背景,上层卡片"漏出"下层卡片的一角,层次感油然而生。
6.2 光晕装饰的实现技巧
示例中的粉色和蓝色光晕使用了一种"欺骗性"技巧——本质上是一个圆形 Column + 极高的 blur 值:
Column()
.width(280)
.aspectRatio(1) // 宽高比 1:1,确保是正圆
.borderRadius(140) // 圆角为宽的一半 → 圆形
.backgroundColor('#ff6fd8')
.blur(80) // ★ 极高的模糊值,使圆形边缘完全虚化
.position({ top: 60, right: -40 })
.opacity(0.35) // 透明度降低,使其融入背景
blur(80) 的极高模糊值使得圆形的边缘不再锐利,而是像颜料在水中晕开一样扩散开来,形成了柔和的光晕效果。这种技巧不依赖任何图片资源,纯代码实现,非常实用。
6.3 入场动画的时机控制
GlassCard 中的 .transition() 设置了插入动画:
.transition({
type: TransitionType.Insert,
translate: { x: 0, y: 60 }, // 从下方 60vp 处移入
opacity: 0, // 从完全透明开始
duration: 400, // 400ms 完成
curve: Curve.Friction // 摩擦曲线:开始时快,结束时慢
})
由于四张卡片都在同一个 ForEach 循环中创建,它们的动画是同时触发的。如果要实现"逐张滑入"的效果(即每张卡片依次进入),可以通过 index 参数延迟每张卡片的动画启动时间:
// 在 GlassCard 的 build() 中补充
.onAppear(() => {
// 使用 animateTo 实现延迟动画
animateTo({
duration: 400,
delay: this.index * 100, // 每张卡片延迟 100ms
curve: Curve.Friction
}, () => {
// 动画目标:无额外操作,transition 已处理
})
})
6.4 阴影与毛玻璃效果的配合
阴影在毛玻璃效果中起着两个重要作用:
- 营造空间感:阴影使卡片看起来悬浮在背景之上,增强了层叠的立体感
- 模拟玻璃厚度:真实玻璃有厚度,会产生投影
在 GlassCard 中,我们使用了较大的阴影值:
.shadow({
radius: 20, // 大半径 → 柔和扩散的阴影
color: 'rgba(0, 0, 0, 0.3)', // 半透明黑色
offsetX: 0, // 无偏移 → 居中阴影
offsetY: 8 // 向下偏移 → 光源在正上方
})
shadow 的 radius 值越大,阴影越柔和、越扩散;offsetY 控制阴影的垂直偏移,模拟光源的方向。这里我们模拟了来自正上方的光源,所以阴影主要出现在卡片下方。
七、扩展应用场景与实践
掌握了 Column + blur + backgroundColor 的核心技术后,我们可以将其应用于多种实际场景。
7.1 场景一:音乐播放器界面
将示例中的卡片改造为音乐播放器界面:
@Component
struct MusicPlayerCard {
build() {
Column() {
// 专辑封面(圆形,带光晕)
Column()
.width(120)
.aspectRatio(1)
.borderRadius(60)
.backgroundColor('rgba(255,255,255,0.15)')
.blur(5)
.overlay({
shape: Circle(),
width: 110,
height: 110
})
Blank().height(16)
// 歌曲信息
Text('Blur — 毛玻璃')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#ffffff')
Text('HarmonyOS NEXT')
.fontSize(14)
.fontColor('rgba(255,255,255,0.6)')
.margin({ top: 4 })
Blank().height(24)
// 进度条(毛玻璃风格)
Row() {
Column()
.width('60%')
.height(4)
.backgroundColor('rgba(255,255,255,0.3)')
.borderRadius(2)
.overlay({
builder: Column()
.width('40%')
.height('100%')
.backgroundColor('rgba(255,255,255,0.8)')
.borderRadius(2),
align: Alignment.Start
})
}
.width('100%')
}
.width('100%')
.padding(24)
.backgroundColor('rgba(255,255,255,0.1)')
.blur(20)
.borderRadius(24)
.border({ width: 1, color: 'rgba(255,255,255,0.2)' })
}
}
7.2 场景二:登录/注册页面
毛玻璃效果在登录页面中同样出彩:
@Component
struct GlassInputField {
private placeholder: string = '';
private isPassword: boolean = false;
build() {
Column() {
// 输入框
TextInput({ placeholder: this.placeholder })
.width('100%')
.height(48)
.backgroundColor('rgba(255,255,255,0.08)')
.placeholderColor('rgba(255,255,255,0.4)')
.fontColor('#ffffff')
.borderRadius(12)
.padding({ left: 16 })
.border({
width: 1,
color: 'rgba(255,255,255,0.15)',
style: BorderStyle.Solid
})
.type(this.isPassword ? InputType.Password : InputType.Normal)
}
.width('100%')
.margin({ bottom: 14 })
}
}
7.3 场景三:数据仪表盘
多张毛玻璃卡片组合成仪表盘:
@Component
struct MetricCard {
private label: string = '';
private value: string = '';
private unit: string = '';
private trend: 'up' | 'down' | 'stable' = 'stable';
build() {
Column() {
Text(this.label)
.fontSize(12)
.fontColor('rgba(255,255,255,0.6)')
Blank().height(8)
Row() {
Text(this.value)
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#ffffff')
Text(this.unit)
.fontSize(12)
.fontColor('rgba(255,255,255,0.5)')
.margin({ left: 4 })
.alignSelf(ItemAlign.End)
.margin({ bottom: 4 })
}
}
.alignItems(HorizontalAlign.Start)
.width('45%')
.padding(16)
.backgroundColor('rgba(255,255,255,0.1)')
.blur(16)
.borderRadius(16)
.border({ width: 1, color: 'rgba(255,255,255,0.15)' })
}
}
八、性能优化与最佳实践
在鸿蒙 NEXT 上使用毛玻璃效果时,以下几点优化建议可以帮助你获得更好的性能。
8.1 模糊半径的选择
blur 的半径越大,GPU 的计算负担越重。建议:
| 场景 | 推荐模糊半径 | 理由 |
|---|---|---|
| 主要毛玻璃面板 | 15~25 | 视觉效果明显,性能适中 |
| 装饰性元素 | 40~80 | 数量少,可以接受 |
| 列表项中的毛玻璃 | 10~15 | 列表项多,需控制性能 |
| 弹窗/对话框 | 20~30 | 临时显示,性能影响有限 |
8.2 半透明层叠层数控制
每叠加一层 blur + 半透明背景,GPU 就需要多一次离屏渲染(offscreen rendering)。建议:
- 层叠深度不超过 5 层
- 对模糊区域不重叠的卡片,使用 Scroll 容器而非多层 Stack 嵌套
- 避免在 List / Grid 的每个 item 中都使用 blur
8.3 使用条件渲染避免不必要的模糊
在不需要毛玻璃效果的场景下,通过条件渲染避免创建模糊组件:
Column() {
if (this.enableGlassEffect) {
Text('毛玻璃内容')
.backgroundColor('rgba(255,255,255,0.12)')
.blur(20)
} else {
Text('普通内容')
.backgroundColor('#ffffff')
}
}
8.4 利用 @Builder 复用毛玻璃样式
为了避免在每个组件中重复编写毛玻璃样式代码,可以使用 @Builder 封装:
@Builder
GlassStyle(content: () => void) {
Column() {
content()
}
.width('100%')
.padding(18)
.backgroundColor('rgba(255, 255, 255, 0.12)')
.blur(20)
.borderRadius(20)
.border({ width: 1, color: 'rgba(255,255,255,0.25)' })
}
然后在需要的地方调用:
this.GlassStyle(() => {
Text('这是毛玻璃包裹的内容')
.fontColor('#ffffff')
})
8.5 适配深色/浅色模式
毛玻璃效果在深色模式下效果最佳,但也可以通过条件样式适配浅色模式:
import { Configuration } from '@kit.AbilityKit';
// 在页面中监听主题变化
@State private isDarkMode: boolean = true;
aboutToAppear() {
const config = this.getUIContext()?.getHostContext()
?.config as Configuration;
this.isDarkMode = config?.colorMode === 1; // 1 = 深色
}
get glassBgColor(): string {
return this.isDarkMode
? 'rgba(255, 255, 255, 0.12)' // 深色模式:白底
: 'rgba(0, 0, 0, 0.06)'; // 浅色模式:黑底
}
九、常见问题与调试技巧
9.1 毛玻璃效果不明显
问题:应用了 blur 和半透明背景后,看起来和普通半透明面板没什么区别。
原因分析:毛玻璃效果需要"背景有丰富的内容"才能体现。如果背景是纯色或内容很少,模糊前后的区别不大。
解决方案:
- 确保背景层有丰富的内容(渐变色、图片、装饰元素)
- 增加 blur 值到 20~30
- 降低 backgroundColor 的 alpha 到 0.08~0.12
- 检查卡片是否被其他不透明组件遮挡
9.2 卡片内容也被模糊了
问题:应用 blur 后,卡片内部的文字和图标也变得模糊不清。
原因分析:blur 作用于整个组件及其子组件。如果对包含文字的卡片整体应用 blur,文字自然也会模糊。
解决方案:
方法一:使用 backgroundBlurStyle(只模糊背景,不模糊内容):
Column() {
Text('清晰的内容')
.fontColor('#ffffff')
}
.backgroundBlurStyle(BlurStyle.Regular)
.backgroundColor('rgba(255,255,255,0.12)')
方法二:将 blur 层和内容层分离:
Stack() {
// blur 层:只有背景,没有内容
Column()
.width('100%')
.height('100%')
.backgroundColor('rgba(255,255,255,0.12)')
.blur(20)
// 内容层:不模糊的文字和图标
Column() {
Text('清晰的内容')
.fontColor('#ffffff')
}
.padding(18)
}
9.3 性能卡顿
问题:页面中包含大量毛玻璃卡片时出现卡顿。
原因分析:过多的 blur 操作消耗 GPU 资源。
解决方案:
- 减少列表中毛玻璃卡片的数量
- 降低模糊半径
- 使用 LazyForEach 代替 ForEach 实现懒加载
- 对不在可视区域的卡片禁用 blur
- 考虑使用静态的模糊背景图片代替实时 blur
十、总结
10.1 技术回顾
本文从零到一构建了一个基于鸿蒙原生 ArkTS 的毛玻璃层叠布局示例,核心知识点包括:
| 技术点 | API / 属性 | 作用 |
|---|---|---|
| 垂直布局 | Column | 主轴垂直排列子组件 |
| 层叠布局 | Stack | 允许背景和前景重叠 |
| 高斯模糊 | .blur(radius) | 对背景内容应用模糊 |
| 半透明色 | backgroundColor rgba | 控制透过程度 |
| 玻璃边框 | .border rgba | 模拟玻璃边缘高光 |
| 悬浮阴影 | .shadow | 营造立体空间感 |
| 位置偏移 | .position | 实现卡片层叠偏移 |
| 入场动画 | .transition | 卡片滑入效果 |
10.2 设计原则
在应用毛玻璃效果时,建议遵循以下设计原则:
- 层次清晰:确保背景、玻璃层、内容层三层关系明确
- 适度使用:毛玻璃适合点缀和强调,不宜占据整个页面
- 深色优先:毛玻璃在深色背景上效果最佳
- 性能意识:控制模糊半径和层叠数量,避免性能问题
- 内容可见:确保模糊后的文字依然可读,避免过度模糊
10.3 在 HarmonyOS NEXT 中的展望
随着 HarmonyOS NEXT 的持续演进,ArkUI 框架中与毛玻璃相关的 API 也在不断丰富:
- backgroundBlurStyle:提供了系统级的标准毛玻璃样式,更加便捷
- foregroundBlur:前景模糊,可实现更丰富的视觉效果
- 动态模糊:结合动画实现实时变化的模糊效果
未来,毛玻璃效果在鸿蒙应用中将成为一种常见且强大的设计语言,帮助开发者构建出更富质感和层次感的用户界面。
10.4 写在最后
本文通过一个完整的示例项目,全面讲解了在 HarmonyOS NEXT 6.1.1(API 24)中使用 Column + blur + backgroundColor 实现毛玻璃层叠布局的方法。从基础原理到代码实现,从性能优化到场景扩展,希望能帮助你掌握这项实用的 UI 技术。
毛玻璃效果虽然精致,但它的实现原理并不复杂——本质上就是"半透明 + 高斯模糊"的巧妙组合。掌握了这个核心公式,你就能在鸿蒙应用中创造出无数富有创意的毛玻璃设计。
欢迎在实际项目中尝试和探索,并结合鸿蒙特有的分布式能力,打造出更具创新性的用户体验。
更多推荐



所有评论(0)