44岁被裁后用AI写鸿蒙App(4):UIDesignKit UI——光感玻璃 + 流光交互实战
上一篇 44岁被裁后用AI写鸿蒙App(3):AGC 认证 + 云函数 + 端到端加密实战一个人搞定全栈后端:AGC 手机号登录 - 掘金
给 App 加一点"光",视觉质感从工具级升到产品级。 而且代码量比你想象得少得多。
前言:为什么要在 UI 上下这个功夫
my-parents-helper 是一个给老人用的 App。你可能会觉得:老人又不看颜值,UI 做得好看有什么用?
我的答案是:好用和好看不冲突。
鸿蒙老的视觉风格比较沉闷——按钮扁平、颜色单一、缺少层次感。这不只是好不好看的问题,它还影响使用体验:
- 按钮跟背景没有区分度 → 老人看不清"哪里可以点"
- 页面缺乏层次感 → 看不清"我现在在哪个页面"
- 界面没有反馈感 → 点了不知道点没点中
鸿蒙 @kit.UIDesignKit 提供的视觉特效能力,可以很好地解决这些问题。而且——它真的不难写。
一、核心概念:ShaderEffect + HdsEffectBuilder
鸿蒙 UIDesignKit 的核心是一个叫 HdsEffectBuilder 的工厂类,你通过链式调用配置各种特效,最后调用 .buildEffect() 生成一个 VisualEffect 对象,挂到任何 UI 组件的 .visualEffect() 上。
import { hdsEffect } from '@kit.UIDesignKit';
Button('发送')
.visualEffect(
new hdsEffect.HdsEffectBuilder()
.shaderEffect({ ... }) // 着色器效果
.pointLight({ ... }) // 点光源效果
.buildEffect() // 生成 VisualEffect
)
整个过程就是搭积木。你要哪种效果,就在 builder 上加什么。
二、流光效果:让按钮"活"起来
DUAL_EDGE_FLOW_LIGHT
这是 UIDesignKit 最实用的效果——双边流光。按钮的两条边缘会有颜色流动,像光在表面滑动。
我的 GlowEffect 封装(约 100 行)就是这个效果的一个通用工厂:
// commons/uicomponents → effects/GlowEffect.ets
import { hdsEffect } from '@kit.UIDesignKit';
export class GlowEffect {
static glowFlow(
controller: hdsEffect.ShaderEffectController,
isActive: boolean,
params?: { firstColor, secondColor, duration }
): Object {
const builder = new hdsEffect.HdsEffectBuilder()
.shaderEffect({
effectType: hdsEffect.EffectType.DUAL_EDGE_FLOW_LIGHT,
animation: {
duration: isActive ? 800 : 3000, // 活跃时快闪,常驻时慢呼吸
iterations: -1, // 无限循环
autoPlay: true,
},
controller: controller,
params: {
firstEdgeFlowLight: { startPos: 0, endPos: 1.0, color: '#FF8C5A' },
secondEdgeFlowLight: { startPos: 0.5, endPos: 1.5, color: '#FFE0D0' },
},
});
// 可以附加点光源,让边缘更有立体感
builder.pointLight({
illuminatedType: hdsEffect.PointLightIlluminatedType.BORDER,
});
return builder.buildEffect();
}
}
使用方式极简。每个需要流光的控件建一个独立的 controller:
@ComponentV2
export struct LoginPage {
@Local loginBtnCtrl: hdsEffect.ShaderEffectController = new hdsEffect.ShaderEffectController();
@Local codeBtnCtrl: hdsEffect.ShaderEffectController = new hdsEffect.ShaderEffectController();
build() {
Column() {
// 获取验证码按钮 — 常驻流光
Button('获取验证码')
.visualEffect(GlowEffect.ambientGlow(this.codeBtnCtrl))
// 登录按钮 — 可条件切换脉冲/常驻
Button('登录')
.visualEffect(GlowEffect.glowFlow(this.loginBtnCtrl, this.isLoading))
}
}
}
常驻流光 vs 脉冲流光
| 模式 | 适用场景 | 动画时长 | 视觉效果 |
|---|---|---|---|
| ambientGlow | 按钮常规态、背景装饰 | 3000ms | 慢速呼吸流动,不抢眼 |
| pulseGlow | 点击反馈、提醒、通知 | 800ms | 快速亮色一闪,吸引注意 |
| glowFlow(isActive) | 条件切换 | 根据状态 | active 时快闪,平时常驻 |
使用场景示例:
| 组件 | 效果 | 原因 |
|---|---|---|
| ⚙️ 设置按钮 | ambientGlow | 右上角齿轮按钮,常驻流光提升质感 |
| 📋 复制日志按钮 | ambientGlow | 调试面板里的操作按钮 |
| 🔵 登录按钮 | glowFlow(isActive) | 点击时触发脉冲,反馈用户"正在处理" |
| 🔐 生物识别按钮 | glowFlow(isActive) | 认证过程中闪烁提示 |
为什么不直接用组件封装,而是工厂函数
你可能会想:为什么不用一个 @ComponentV2 struct GlowButton 来封装?
因为 .visualEffect() 是直接挂在不同类型的基础组件上(Button、Row、Text),用组件封装会多一层 DOM 嵌套,影响布局。工厂函数直接返回 VisualEffect 对象,挂上去就行,不影响现有的 UI 结构。
三、点光源效果:给组件加"立体感"
流光是在边缘流动的,但要让组件看起来有"微凸起"的立体感,还需要点光源。
// 单独使用点光源
new hdsEffect.HdsEffectBuilder()
.pointLight({
illuminatedType: hdsEffect.PointLightIlluminatedType.BORDER,
})
.buildEffect()
PointLightIlluminatedType.BORDER 会在组件边缘生成一个微弱的亮光,模拟光线从侧面照射的效果。
流光和点光源通常是组合使用的——光在边缘流动 + 边缘被照亮 = 组件像玻璃一样通透。
四、玻璃拟态:通透的卡片背景
除了流光,我还用到了鸿蒙的背景模糊能力来实现"玻璃"效果。
鸿蒙的 BlurStyle 组件可以直接给任何容器加毛玻璃效果:
Column() {
// 卡片内容
}
.backgroundBlurStyle(BlurStyle.Thin)
.backgroundColor('#80FFFFFF') // 半透明白
.borderRadius(16)
配合渐变背景,可以做出很通透的层级感:
// 主页面渐变背景
Column()
.width('100%')
.height('100%')
.linearGradient({
angle: '180deg',
colors: [['#F0ECFF', 0], ['#EEFFF5', 1]]
})
顶层放毛玻璃卡片,底层是渐变背景——两层叠加,效果不输那些大厂的 App。
五、实际效果:登录页面的完整流光按钮
这是 LoginPage 里一个典型的流光按钮——获取验证码按钮:
Button(this.emailCodeCountdown > 0 ?
`${this.emailCodeCountdown}s` : // 倒计时
(this.emailCodeSending ? '发送中' : '获取验证码')) // 状态文字
.fontSize(14)
.fontColor(Color.White)
.padding({ left: 10, right: 10 })
.height(48)
.borderRadius(8)
.backgroundColor('#E0E0E0') // 按钮底色
.visualEffect(new hdsEffect.HdsEffectBuilder()
.shaderEffect({
effectType: hdsEffect.EffectType.DUAL_EDGE_FLOW_LIGHT,
animation: { duration: 3000, iterations: -1, autoPlay: true },
controller: this.codeBtnCtrl,
params: {
firstEdgeFlowLight: { startPos: 0, endPos: 1.0, color: '#FF8C5A' },
secondEdgeFlowLight: { startPos: 0.5, endPos: 1.5, color: '#FFE0D0' },
},
})
.pointLight({ illuminatedType: hdsEffect.PointLightIlluminatedType.BORDER })
.buildEffect()
)
.enabled(this.emailCodeCountdown === 0)
.onClick(() => this.onEmailCodeSend())
这段代码的效果:按钮四边有暖橙色光晕缓缓流动,倒计时 60 秒内按钮禁用,流光暂停——60 秒后流光恢复,提示用户可以再次点击。
六、设计原则:不要过度设计
最后说一个重要的原则:UI 特效是"调味料"不是"主菜"。
这个 App 的用户是老人。我遵循几条规则:
| 规则 | 原因 |
|---|---|
| 只在操作按钮上用流光 | 告诉老人"这里可以点" |
| 只在设置页用毛玻璃 | 区分内容层级,不干扰阅读 |
| 减速动画 | 老人反应速度慢,动画太快会晕 |
| 大按钮 + 高对比度 | 视觉特效是点缀,大字大按钮才是核心 |
| 不引入外部动效库 | 鸿蒙系统能力完全够用,不增加包体积 |
小结
鸿蒙 @kit.UIDesignKit 的能力总结:
| 效果 | 核心 API | 使用位置 |
|---|---|---|
| 流光(边缘流动) | EffectType.DUAL_EDGE_FLOW_LIGHT | 按钮、图标 |
| 点光源(边缘照亮) | PointLightIlluminatedType.BORDER | 按钮、卡片 |
| 玻璃拟态(毛玻璃) | backgroundBlurStyle(BlurStyle) | 设置页卡片 |
| 渐变背景 | .linearGradient() | 主页面背景 |
| 组合特效 | HdsEffectBuilder 链式调用 | 任意组件 |
全部代码在 GlowEffect.ets(约 100 行),每个控件只需 3 行就能挂上流光效果。投入产出比极高。
下篇预告
第5篇:踩坑合集——@ObservedV2 + Repeat、编译报错、组件化重构
我会把开发过程中遇到的最有价值的坑整理出来——那些你查文档也查不到的"鸿蒙专属"问题。
关注我 seal_jing,一个正在鸿蒙生态里写 App 的独立开发者。
发布时间:2026年6月 作者:seal_jing,44岁、被裁、血压已恢复正常、正在写代码