阴影与光照效果:让你的 HarmonyOS 6.0 界面真正“浮“起来
阴影与光照效果:让你的 HarmonyOS 6.0 界面真正"浮"起来
做过 UI 的都知道,一个界面有没有质感,往往不是差在颜色上,而是差在"影"上。纯平的卡片看着像纸片,加了阴影的卡片才像是浮在屏幕上方的东西。HarmonyOS 6.0 的 ArkUI 给我们提供了 shadow 属性,参数不多,但真正用好的开发者没几个——要么阴影太硬像黑框,要么太散像雾气,要么干脆看不出效果。
今天这篇文章,我就把 shadow 的每个参数掰开揉碎讲清楚,再配合实际场景给你讲怎么用出"设计感",而不是"加了个寂寞"。

一、shadow 属性全解析:四个参数各自管什么
ArkUI 的 shadow 接口签名如下:
shadow(value: ShadowOptions | ShadowStyle)
其中 ShadowOptions 是一个对象,包含四个字段:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| radius | number | Resource | 是 | - | 模糊半径 |
| color | Color | string | Resource | 否 | 黑色半透明 | 阴影颜色 |
| offsetX | number | Resource | 否 | 0 | X 轴偏移量 |
| offsetY | number | Resource | 否 | 0 | Y 轴偏移量 |
还有一个 ShadowStyle 枚举类型,提供系统预设的阴影样式,比如 ShadowStyle.OUTER_DEFAULT_SM、ShadowStyle.OUTER_DEFAULT_MD 等,适合快速使用但定制性有限。
先看一个最基础的用法:
Column() {
Text('基础阴影')
.fontSize(16)
.fontColor('#333333')
}
.width(200)
.height(100)
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({
radius: 20,
color: '#66000000',
offsetX: 0,
offsetY: 4
})
这段代码的效果是:一个白底圆角卡片,下方有一道柔和的灰色阴影,视觉上卡片微微"浮起"。接下来逐个参数说清楚。
二、模糊半径 radius:阴影的"软硬"开关
radius 是 shadow 唯一的必填参数,它控制阴影边缘的模糊程度。值越大,阴影越柔和、扩散越远;值越小,阴影越锐利、越像一条硬边。
让我用代码对比一下不同 radius 的效果:
@Entry
@Component
struct ShadowRadiusDemo {
build() {
Column({ space: 20 }) {
// radius = 0:几乎没有模糊,阴影像一条硬边
Column() {
Text('radius: 0')
.fontSize(14)
.fontColor('#333333')
}
.width(180)
.height(80)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({
radius: 0,
color: '#99000000',
offsetX: 0,
offsetY: 6
})
// radius = 10:轻微模糊,边缘开始变得柔和
Column() {
Text('radius: 10')
.fontSize(14)
.fontColor('#333333')
}
.width(180)
.height(80)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({
radius: 10,
color: '#99000000',
offsetX: 0,
offsetY: 6
})
// radius = 30:中等模糊,阴影自然扩散
Column() {
Text('radius: 30')
.fontSize(14)
.fontColor('#333333')
}
.width(180)
.height(80)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({
radius: 30,
color: '#99000000',
offsetX: 0,
offsetY: 6
})
// radius = 60:大范围模糊,阴影变成一层淡淡的雾
Column() {
Text('radius: 60')
.fontSize(14)
.fontColor('#333333')
}
.width(180)
.height(80)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({
radius: 60,
color: '#99000000',
offsetX: 0,
offsetY: 6
})
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
.padding(20)
}
}
实际经验:大多数卡片场景,radius 在 15~30 之间效果最自然。低于 10 容易显得生硬,高于 50 就像一团雾气——除非你刻意要做那种毛玻璃远景的雾化效果,否则别开太大。
另外注意一点:radius 的单位是 px(物理像素),不是 vp。在高分辨率设备上,同样的数值视觉上会更细腻。如果你在不同设备上发现阴影粗细不一致,可能就是这个原因。
三、阴影颜色 color:#AARRGGBB 格式的坑
color 参数支持 Color 枚举、字符串和 Resource 三种写法,但最常用的是十六进制字符串。这里有个关键点:ArkUI 的颜色字符串格式是 #AARRGGBB,alpha 通道在前面,这和 CSS 的 #RRGGBBAA 是反的。
// 正确:AA 在前,RRGGBB 在后
shadow({ radius: 20, color: '#33000000' }) // 约20%透明度的黑色
shadow({ radius: 20, color: '#66000000' }) // 约40%透明度的黑色
shadow({ radius: 20, color: '#99000000' }) // 约60%透明度的黑色
// 错误:如果按CSS习惯写,透明度就跑到最后了
// '#00000033' 在ArkUI里会被解析为AA=00(全透明),阴影完全不可见
不同透明度的阴影效果差异极大:
@Entry
@Component
struct ShadowColorAlphaDemo {
build() {
Column({ space: 20 }) {
Column() {
Text('alpha: 10% (#1A000000)')
.fontSize(14)
.fontColor('#333333')
}
.width(180)
.height(80)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({ radius: 25, color: '#1A000000', offsetX: 0, offsetY: 4 })
Column() {
Text('alpha: 30% (#4D000000)')
.fontSize(14)
.fontColor('#333333')
}
.width(180)
.height(80)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({ radius: 25, color: '#4D000000', offsetX: 0, offsetY: 4 })
Column() {
Text('alpha: 50% (#80000000)')
.fontSize(14)
.fontColor('#333333')
}
.width(180)
.height(80)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({ radius: 25, color: '#80000000', offsetX: 0, offsetY: 4 })
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
.padding(20)
}
}
实际经验:浅色背景上的卡片阴影,alpha 值控制在 0.1~0.3 之间最舒服。超过 0.5 就会显得太重,像卡片底下压了一块黑砖。深色背景上可以适当提高到 0.3~0.5,因为深色背景下浅色阴影不容易显出来,需要更强的对比。
常见 alpha 值速查:
| 透明度 | 十六进制值 | 完整写法 |
|---|---|---|
| 5% | 0D | #0D000000 |
| 10% | 1A | #1A000000 |
| 20% | 33 | #33000000 |
| 30% | 4D | #4D000000 |
| 40% | 66 | #66000000 |
| 50% | 80 | #80000000 |
| 60% | 99 | #99000000 |
| 70% | B3 | #B3000000 |
| 80% | CC | #CC000000 |
| 90% | E6 | #E6000000 |
四、偏移量 offsetX/offsetY:模拟光源方向
offsetX 和 offsetY 控制阴影相对于组件的偏移方向,本质上是模拟光源的位置。如果光从左上方打过来,影子就该往右下方投射。
offsetX > 0:阴影向右偏移offsetX < 0:阴影向左偏移offsetY > 0:阴影向下偏移offsetY < 0:阴影向上偏移
@Entry
@Component
struct ShadowOffsetDemo {
build() {
Column({ space: 20 }) {
// 光源在左上:阴影向右下偏移
Column() {
Text('offsetX: 8, offsetY: 8')
.fontSize(14)
.fontColor('#333333')
}
.width(180)
.height(80)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({ radius: 20, color: '#33000000', offsetX: 8, offsetY: 8 })
// 光源在正上方:阴影只向下偏移
Column() {
Text('offsetX: 0, offsetY: 6')
.fontSize(14)
.fontColor('#333333')
}
.width(180)
.height(80)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({ radius: 20, color: '#33000000', offsetX: 0, offsetY: 6 })
// 光源在右上:阴影向左下偏移
Column() {
Text('offsetX: -8, offsetY: 8')
.fontSize(14)
.fontColor('#333333')
}
.width(180)
.height(80)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({ radius: 20, color: '#33000000', offsetX: -8, offsetY: 8 })
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
.padding(20)
}
}
实际经验:大多数 App 的设计规范里,光源默认在左上方或正上方。所以 offsetX 一般取 0 或小正值(2~8),offsetY 取正值(4~12)。偏移量太大(超过 20)会让阴影和组件脱节,看着像影子被风吹走了。
还有一点要注意:偏移量的单位也是 px,不是 vp。不同设备像素密度不一样,同样的偏移值在 2x 屏幕上视觉偏移只有 1x 屏幕的一半。如果你的设计稿标注了 vp 单位的偏移,记得做换算。
五、彩色阴影:用组件同色的半透明阴影做品牌感
默认的黑色阴影大家都在用,看多了难免审美疲劳。一个提升界面质感的小技巧:用组件自身颜色的半透明版本作为阴影色。这在 Material Design 3 和很多现代 UI 设计中被大量使用。
比如你有一个蓝色主题的按钮,不用黑色阴影,而是用蓝色半透明阴影:
@Entry
@Component
struct ColoredShadowDemo {
build() {
Column({ space: 30 }) {
// 黑色阴影:普通但无聊
Row() {
Text('普通阴影')
.fontSize(16)
.fontColor(Color.White)
}
.width(200)
.height(56)
.backgroundColor('#1976D2')
.borderRadius(28)
.justifyContent(FlexAlign.Center)
.shadow({ radius: 20, color: '#33000000', offsetX: 0, offsetY: 4 })
// 蓝色半透明阴影:和按钮同色系,更有整体感
Row() {
Text('品牌色阴影')
.fontSize(16)
.fontColor(Color.White)
}
.width(200)
.height(56)
.backgroundColor('#1976D2')
.borderRadius(28)
.justifyContent(FlexAlign.Center)
.shadow({ radius: 24, color: '#4D1976D2', offsetX: 0, offsetY: 6 })
// 绿色按钮 + 绿色阴影
Row() {
Text('成功状态')
.fontSize(16)
.fontColor(Color.White)
}
.width(200)
.height(56)
.backgroundColor('#2E7D32')
.borderRadius(28)
.justifyContent(FlexAlign.Center)
.shadow({ radius: 24, color: '#4D2E7D32', offsetX: 0, offsetY: 6 })
// 橙色按钮 + 橙色阴影
Row() {
Text('警告状态')
.fontSize(16)
.fontColor(Color.White)
}
.width(200)
.height(56)
.backgroundColor('#E65100')
.borderRadius(28)
.justifyContent(FlexAlign.Center)
.shadow({ radius: 24, color: '#4DE65100', offsetX: 0, offsetY: 6 })
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
.padding(20)
.justifyContent(FlexAlign.Center)
}
}
彩色阴影的关键点:alpha 值要低(0.2~0.4),否则彩色阴影会显得很脏。阴影的 radius 可以比黑色阴影稍大一点,让颜色扩散得更柔和,看起来像是从按钮本身"散发"出来的光晕效果。
这种手法在品牌色鲜明的 App 里特别好用——比如网易云音乐的红、支付宝的蓝、微信的绿,用同色系阴影会让整个界面更有品牌辨识度。
六、多层阴影叠加:用多个 shadow 制造深度感
单层阴影的问题在于:它要么柔和但不够清晰,要么清晰但不够柔和。专业设计里经常用"多层阴影"来解决这个矛盾——一层小 radius 的深色阴影提供清晰的轮廓感,一层大 radius 的浅色阴影提供柔和的扩散感。
ArkUI 的 shadow 属性支持传入数组,实现多层阴影叠加:
@Entry
@Component
struct MultiShadowDemo {
build() {
Column({ space: 30 }) {
// 单层阴影:要么清晰要么柔和,难以两全
Column() {
Text('单层阴影')
.fontSize(16)
.fontColor('#333333')
}
.width(240)
.height(120)
.backgroundColor(Color.White)
.borderRadius(16)
.justifyContent(FlexAlign.Center)
.shadow({ radius: 20, color: '#1A000000', offsetX: 0, offsetY: 4 })
// 双层阴影:小radius深色 + 大radius浅色,轮廓和扩散兼顾
Column() {
Text('双层阴影')
.fontSize(16)
.fontColor('#333333')
}
.width(240)
.height(120)
.backgroundColor(Color.White)
.borderRadius(16)
.justifyContent(FlexAlign.Center)
.shadow({
radius: 8,
color: '#1A000000',
offsetX: 0,
offsetY: 2
})
.shadow({
radius: 30,
color: '#0D000000',
offsetX: 0,
offsetY: 8
})
// 三层阴影:极致深度感
Column() {
Text('三层阴影')
.fontSize(16)
.fontColor('#333333')
}
.width(240)
.height(120)
.backgroundColor(Color.White)
.borderRadius(16)
.justifyContent(FlexAlign.Center)
.shadow({
radius: 4,
color: '#26000000',
offsetX: 0,
offsetY: 1
})
.shadow({
radius: 12,
color: '#1A000000',
offsetX: 0,
offsetY: 4
})
.shadow({
radius: 40,
color: '#0D000000',
offsetX: 0,
offsetY: 12
})
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
.padding(20)
.justifyContent(FlexAlign.Center)
}
}
多层阴影的搭配逻辑:
- 近层阴影:小 radius(4~8),小 offsetY(1~3),较高透明度,模拟卡片边缘紧贴的阴影
- 中层阴影:中 radius(12~20),中 offsetY(4~8),中等透明度,提供主要的浮起感
- 远层阴影:大 radius(30~60),大 offsetY(8~16),低透明度,营造大范围的深度扩散
三层叠加的效果远比单层自然,很多设计系统(包括 Material Design)在 Sketch/Figma 里的阴影预设就是多层叠加出来的。
七、卡片浮动效果:shadow + borderRadius 黄金组合
阴影最经典的用途就是卡片浮动效果。一个圆角卡片加上恰到好处的阴影,是几乎所有现代 App 的标配。但这里有个细节:borderRadius 的圆角和阴影的扩散方向要协调。如果 borderRadius 很大但阴影是方形的,视觉上会非常违和。
@Entry
@Component
struct FloatingCardDemo {
build() {
Scroll() {
Column({ space: 24 }) {
// 小圆角卡片:干净利落
Column() {
Text('信息卡片')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#1A1A1A')
.margin({ bottom: 8 })
Text('这是一段卡片内容,展示小圆角配合紧凑阴影的效果。')
.fontSize(14)
.fontColor('#666666')
.lineHeight(22)
}
.width('100%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({ radius: 16, color: '#1A000000', offsetX: 0, offsetY: 2 })
// 大圆角卡片:柔和亲切
Column() {
Text('推荐卡片')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#1A1A1A')
.margin({ bottom: 8 })
Text('更大的圆角需要更柔和的阴影,radius也要相应增大。')
.fontSize(14)
.fontColor('#666666')
.lineHeight(22)
}
.width('100%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(20)
.shadow({ radius: 28, color: '#1A000000', offsetX: 0, offsetY: 4 })
// 超大圆角胶囊卡片
Column() {
Text('胶囊卡片')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#1A1A1A')
.margin({ bottom: 8 })
Text('borderRadius接近高度一半时,阴影需要大radius才协调。')
.fontSize(14)
.fontColor('#666666')
.lineHeight(22)
}
.width('100%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(40)
.shadow({ radius: 36, color: '#1A000000', offsetX: 0, offsetY: 6 })
// 带交互反馈的浮动卡片:按下时阴影收缩
Column() {
Text('可点击卡片')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#1A1A1A')
.margin({ bottom: 8 })
Text('点击时阴影收缩模拟按下效果,松开恢复。')
.fontSize(14)
.fontColor('#666666')
.lineHeight(22)
}
.width('100%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(16)
.shadow({ radius: 24, color: '#1A000000', offsetX: 0, offsetY: 6 })
.gesture(
GestureGroup(GestureMode.Exclusive,
LongPressGesture()
.onAction(() => {})
)
)
}
.padding(20)
}
.width('100%')
.height('100%')
.backgroundColor('#F0F0F0')
}
}
一条经验法则:radius 的值大约设为 borderRadius 的 1.2~1.8 倍比较协调。圆角越大,阴影越需要"铺开"才自然。
八、Material Design 阴影层级与 shadow 参数对应
Material Design 定义了一套阴影层级系统,用 dp(density-independent pixels)来划分组件的"海拔"。虽然 HarmonyOS 不直接使用 dp,但设计规范是相通的。下面给出各层级的 shadow 参数参考:
| 层级 | 典型组件 | elevation | shadow参数参考 |
|---|---|---|---|
| 0dp | Flat button | 0 | 无阴影 |
| 1dp | Card (rest) | 1 | radius:8, color:#1A000000, offsetX:0, offsetY:1 |
| 2dp | Card (raised) | 2 | radius:10, color:#1A000000, offsetX:0, offsetY:2 |
| 3dp | Refresh indicator | 3 | radius:14, color:#1A000000, offsetX:0, offsetY:3 |
| 4dp | Floating button | 4 | radius:16, color:#26000000, offsetX:0, offsetY:4 |
| 6dp | SnackBar | 6 | radius:20, color:‘#26000000’, offsetX:0, offsetY:6 |
| 8dp | Menu | 8 | radius:24, color:‘#33000000’, offsetX:0, offsetY:8 |
| 12dp | Dialog | 12 | radius:32, color:‘#33000000’, offsetX:0, offsetY:12 |
| 16dp | Bottom sheet | 16 | radius:40, color:‘#33000000’, offsetX:0, offsetY:16 |
| 24dp | Modal bottom sheet | 24 | radius:50, color:‘#33000000’, offsetX:0, offsetY:24 |
实现一个 Material 风格的对话框阴影:
@Entry
@Component
struct MaterialDialogShadow {
build() {
Column() {
Column() {
Text('对话框标题')
.fontSize(20)
.fontWeight(FontWeight.Medium)
.fontColor('#1A1A1A')
.margin({ bottom: 12 })
Text('这是一个 Material Design 风格的对话框,使用了 12dp 级别的阴影参数。')
.fontSize(14)
.fontColor('#666666')
.lineHeight(22)
.margin({ bottom: 24 })
Row({ space: 12 }) {
Row() {
Text('取消')
.fontSize(14)
.fontColor('#1976D2')
}
.padding({ left: 20, right: 20, top: 10, bottom: 10 })
.borderRadius(8)
.backgroundColor('#E3F2FD')
Row() {
Text('确认')
.fontSize(14)
.fontColor(Color.White)
}
.padding({ left: 20, right: 20, top: 10, bottom: 10 })
.borderRadius(8)
.backgroundColor('#1976D2')
}
.justifyContent(FlexAlign.End)
}
.width(280)
.padding(24)
.backgroundColor(Color.White)
.borderRadius(16)
.shadow({ radius: 32, color: '#33000000', offsetX: 0, offsetY: 12 })
}
.width('100%')
.height('100%')
.backgroundColor('#B3000000')
.justifyContent(FlexAlign.Center)
}
}
当然你也可以用 ShadowStyle 枚举快速设置系统预设阴影,写法更简洁:
Column() {
Text('系统预设阴影')
}
.shadow(ShadowStyle.OUTER_DEFAULT_SM)
ShadowStyle 提供了 OUTER_DEFAULT_SM、OUTER_DEFAULT_MD、OUTER_DEFAULT_LG 等预设,对应小/中/大三级阴影。优点是跟着系统主题走,深色模式下自动适配;缺点是无法微调参数,定制性不足。
九、性能注意:大量阴影组件的渲染开销
阴影不是免费的。每个带阴影的组件都需要额外的离屏渲染(offscreen rendering)来完成模糊计算。渲染流程大致是:先绘制组件的轮廓到离屏缓冲区,然后对缓冲区做高斯模糊,再与原图合成。当组件数量多、阴影参数大时,GPU 负载会显著增加。
性能影响排序(从大到小):
radius值——模糊半径是计算量最大的因素,radius 翻倍,计算量大约翻四倍- 组件数量——列表里100个卡片各带阴影,开销远超单个卡片
- 多层阴影——每个
.shadow()调用都会触发一次离屏渲染 - 阴影区域大小——组件越大,模糊区域也越大
实际建议:
// 反面教材:List里每个item都带复杂阴影
@Entry
@Component
struct BadListShadowDemo {
private dataArr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
build() {
List({ space: 12 }) {
ForEach(this.dataArr, (item: number) => {
ListItem() {
Column() {
Text('Item ' + item.toString())
.fontSize(16)
.fontColor('#333333')
}
.width('100%')
.height(80)
.backgroundColor(Color.White)
.borderRadius(12)
// 三层阴影在列表里 = 性能灾难
.shadow({ radius: 4, color: '#26000000', offsetX: 0, offsetY: 1 })
.shadow({ radius: 12, color: '#1A000000', offsetX: 0, offsetY: 4 })
.shadow({ radius: 40, color: '#0D000000', offsetX: 0, offsetY: 12 })
.padding(16)
}
}, (item: number) => item.toString())
}
.width('100%')
.height('100%')
.padding(16)
}
}
// 正面教材:列表item用简单阴影,只在关键卡片用多层阴影
@Entry
@Component
struct GoodListShadowDemo {
private dataArr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
build() {
List({ space: 12 }) {
ForEach(this.dataArr, (item: number) => {
ListItem() {
Column() {
Text('Item ' + item.toString())
.fontSize(16)
.fontColor('#333333')
}
.width('100%')
.height(80)
.backgroundColor(Color.White)
.borderRadius(12)
// 列表item只用单层轻量阴影
.shadow({ radius: 8, color: '#1A000000', offsetX: 0, offsetY: 2 })
.padding(16)
}
}, (item: number) => item.toString())
}
.width('100%')
.height('100%')
.padding(16)
}
}
如果你确实需要长列表带阴影,还有几个优化思路:
- 用
renderGroup(true)——将组件及其子组件先整体离屏渲染再合成,减少重复计算 - 复用阴影参数对象——把 shadow 参数定义为常量,避免每次渲染都创建新对象
- 考虑用图片替代——对于固定大小的卡片,可以预渲染带阴影的 9-patch 图片
- LazyForEach 替代 ForEach——懒加载减少同时渲染的阴影组件数
// renderGroup 优化示例
Column() {
Text('优化后的卡片')
}
.width(200)
.height(100)
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({ radius: 20, color: '#1A000000', offsetX: 0, offsetY: 4 })
.renderGroup(true)
十、阴影与模糊的区别:shadow 是外部投影,blur 是内部模糊
这两个效果经常被新手混淆,但它们本质完全不同:
| 维度 | shadow | blur |
|---|---|---|
| 作用位置 | 组件外部,投射到背景上 | 组件内部,模糊自身内容 |
| 视觉效果 | 组件"浮"起来,下方有投影 | 组件"虚"掉,内容看不清 |
| 参数 | radius + color + offsetX + offsetY | radius(一个参数) |
| 常见用途 | 卡片浮动、按钮层级、对话框深度 | 隐藏敏感信息、毛玻璃遮罩、景深效果 |
| 对组件内容影响 | 不影响组件内容的清晰度 | 组件内容变模糊 |
@Entry
@Component
struct ShadowVsBlurDemo {
build() {
Column({ space: 30 }) {
// shadow:卡片内容清晰,外部有投影
Column() {
Text('Shadow 效果')
.fontSize(16)
.fontColor('#333333')
Text('内容依然清晰')
.fontSize(14)
.fontColor('#666666')
.margin({ top: 4 })
}
.width(240)
.height(120)
.backgroundColor(Color.White)
.borderRadius(12)
.justifyContent(FlexAlign.Center)
.shadow({ radius: 24, color: '#33000000', offsetX: 0, offsetY: 8 })
// blur:卡片内容变模糊,没有外部投影
Column() {
Text('Blur 效果')
.fontSize(16)
.fontColor('#333333')
Text('内容被模糊了')
.fontSize(14)
.fontColor('#666666')
.margin({ top: 4 })
}
.width(240)
.height(120)
.backgroundColor(Color.White)
.borderRadius(12)
.justifyContent(FlexAlign.Center)
.blur(10)
// backdropBlur:模糊的是组件背后的内容,不是自身
Column() {
Text('BackdropBlur 效果')
.fontSize(16)
.fontColor(Color.White)
Text('模糊背景内容')
.fontSize(14)
.fontColor('#FFFFFFCC')
.margin({ top: 4 })
}
.width(240)
.height(120)
.backgroundColor('#80FFFFFF')
.borderRadius(12)
.justifyContent(FlexAlign.Center)
.backdropBlur(20)
}
.width('100%')
.height('100%')
.backgroundColor('#F0F0F0')
.padding(20)
.justifyContent(FlexAlign.Center)
}
}
还有一个容易混淆的概念是 backdropBlur——它模糊的是组件背后的内容,相当于毛玻璃效果,和 shadow 完全不是一回事。backdropBlur 最经典的场景就是半透明遮罩层下面看到模糊的背景。
三者的使用场景总结:
- shadow:需要"浮起来"的感觉——卡片、按钮、弹窗
- blur:需要"看不清"的效果——敏感信息遮挡、失焦状态
- backdropBlur:需要"毛玻璃"效果——半透明遮罩、底部弹窗背景
实战:一个完整的卡片列表页面
把前面讲的内容综合起来,写一个实际可用的卡片列表页面:
interface CardItem {
title: string
subtitle: string
bgColor: string
shadowColor: string
}
@Entry
@Component
struct CardListPage {
private cardDataArr: CardItem[] = [
{ title: '今日推荐', subtitle: '为你精选的热门内容', bgColor: '#FFFFFF', shadowColor: '#1A000000' },
{ title: '限时优惠', subtitle: '距离结束还有2小时', bgColor: '#FFFFFF', shadowColor: '#4D1976D2' },
{ title: '新功能上线', subtitle: '探索最新版本更新', bgColor: '#FFFFFF', shadowColor: '#4D2E7D32' },
{ title: '热门话题', subtitle: '当前最火的讨论', bgColor: '#FFFFFF', shadowColor: '#1A000000' },
{ title: '精选课程', subtitle: '高质量学习资源', bgColor: '#FFFFFF', shadowColor: '#4DE65100' },
]
build() {
Column() {
Text('阴影效果实战')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#1A1A1A')
.width('100%')
.padding({ left: 20, right: 20, top: 20, bottom: 16 })
List({ space: 16 }) {
ForEach(this.cardDataArr, (item: CardItem, index: number) => {
ListItem() {
Row() {
Column() {
Text(item.title)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#1A1A1A')
Text(item.subtitle)
.fontSize(14)
.fontColor('#888888')
.margin({ top: 6 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
Text('>')
.fontSize(20)
.fontColor('#CCCCCC')
}
.width('100%')
.padding(20)
.backgroundColor(item.bgColor)
.borderRadius(16)
.shadow({
radius: 20,
color: item.shadowColor,
offsetX: 0,
offsetY: 4
})
}
.padding({ left: 20, right: 20 })
}, (item: CardItem, index: number) => index.toString())
}
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
}

常见问题与避坑指南
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 阴影完全看不到 | radius 为 0,或 color 的 alpha 为 00 | 确保 radius > 0,color 格式为 #AARRGGBB 且 AA 不为 00 |
| 阴影颜色和预期不一致 | 把 #AARRGGBB 写成了 CSS 的 #RRGGBBAA | ArkUI 颜色格式 alpha 在前,如 #33000000 是 20%透明黑色 |
| 阴影被截断,右侧或底部消失 | 父容器 overflow 裁剪了超出边界的阴影 | 给父容器加 padding 留出阴影空间,或确认 clip 属性未开启 |
| 阴影方向不对 | offsetX/offsetY 正负方向理解反了 | offsetX 正值向右,offsetY 正值向下 |
| 列表滚动卡顿 | 每个 ListItem 都有多层大 radius 阴影 | 列表项用单层小 radius 阴影,多层阴影只用于重点卡片 |
| borderRadius 和阴影不协调 | 大圆角配小 radius 阴影,阴影看起来是方形的 | radius 值设为 borderRadius 的 1.2~1.8 倍 |
| 同样的 shadow 参数在不同设备上粗细不一样 | shadow 参数单位是 px 不是 vp | 高密度屏上视觉更细,需要根据设备 density 做适配 |
| shadow 和 blur 分不清 | shadow 是外部投影,blur 是内部模糊 | 需要"浮起"用 shadow,需要"模糊"用 blur,需要"毛玻璃"用 backdropBlur |
| 阴影在深色背景上几乎不可见 | 深色背景 + 黑色阴影 = 对比度不足 | 深色背景上提高阴影 alpha 或改用浅色阴影 |
| ShadowStyle 预设不够灵活 | 枚举值只有几档,无法微调 | 需要精细控制时用 ShadowOptions 对象替代 ShadowStyle 枚举 |
更多推荐
所有评论(0)