鸿蒙 HarmonyOS 6 | 20 SymbolGlyph阴影与动效增强开发实战
图标组件在界面里看起来很小,实际承担的任务并不少。状态切换要清楚,禁用态要直观,重点入口要有层次,品牌图标还得有足够的辨识度。鸿蒙 6 在 API 20 这条线上,把 `SymbolGlyph` 的表现力往前推了一步。`symbolShadow` 让图标可以直接带阴影,`ReplaceEffectType.CROSS_FADE` 让状态替换更顺,斜杠遮罩效果可以更自然地表达禁用态,`shaderS
前言
图标组件在界面里看起来很小,实际承担的任务并不少。状态切换要清楚,禁用态要直观,重点入口要有层次,品牌图标还得有足够的辨识度。鸿蒙 6 在 API 20 这条线上,把 SymbolGlyph 的表现力往前推了一步。symbolShadow 让图标可以直接带阴影,ReplaceEffectType.CROSS_FADE 让状态替换更顺,斜杠遮罩效果可以更自然地表达禁用态,shaderStyle 则把渐变填充接进了图标渲染链路。
这几项能力放在一起,解决的是四类高频问题。图标切换不够顺,重点按钮层次不够,禁用态表达太弱,品牌化图标只能靠纯色硬顶。API 20 之后,这些能力都可以直接落在 SymbolGlyph 本身,不需要继续靠外层容器、自定义绘制或手写动画去拼。

一、先把这四项能力分清楚
先看快速替换动效。ReplaceEffectType.CROSS_FADE 的作用很直接,旧图标淡出的同时,新图标淡入,切换过程连续,不会出现生硬跳变。点赞、收藏、播放模式切换、勾选状态切换,这类场景最适合先接这个能力。CROSS_FADE 已经进入 ArkUI 6.0.0(20) 的 SymbolGlyph API 变更范围。
再看阴影。symbolShadow 从 API 20 开始支持,图标本身就能直接挂阴影参数。半径、颜色、偏移这些属性都交给 ShadowOptions,不需要再包一层 Stack 或自己画影子。重点入口、悬浮按钮、深色背景上的主图标,都很适合用它来做层次感。
第三项是禁用态。斜杠遮罩效果已经进了 SymbolGlyph 的替换效果体系里,作用很明确,就是在图标上叠一层对角斜杠,用来表达当前功能不可用或暂时不可操作。这类表达比单纯把图标变灰更直接,用户理解成本也更低。
第四项是渐变填充。shaderStyle 已经接进 SymbolGlyph,可以直接给图标挂 LinearGradientStyle、RadialGradientStyle 或纯色 shader。这样一来,品牌图标、活动页图标、会员页入口图标终于不用继续卡在单色表达里。

二、快速替换和阴影
这两项能力最适合先放进高频交互。点赞、收藏、勾选、模式切换,这些地方的图标变化最频繁,用户也最容易感知到细节差异。状态切换一旦顺下来,页面质感会立刻提升。
下面这个例子适合直接做收藏按钮的基础实现。状态切换时,通过 triggerValue 触发 CROSS_FADE,逻辑清楚,也方便后面接业务状态。
@Entry
@Component
struct FavoriteSymbolDemo {
@State active: boolean = false
@State triggerValue: number = 0
build() {
Column({ space: 20 }) {
SymbolGlyph(this.active ? $r('sys.symbol.heart_fill') : $r('sys.symbol.heart'))
.fontSize(32)
.fontColor(this.active ? '#FF4D4F' : '#222222')
.symbolEffect(
new ReplaceSymbolEffect(EffectScope.WHOLE, ReplaceEffectType.CROSS_FADE),
this.triggerValue
)
Button('切换收藏状态')
.onClick(() => {
this.active = !this.active
this.triggerValue++
})
}
.width('100%')
.padding(24)
}
}
阴影更适合处理层次感。普通状态下影子略深一点,按下时影子收一点,用户会更容易感知到按钮的空间关系。symbolShadow 进了组件本身之后,这件事就不需要额外容器参与了。
@Entry
@Component
struct ShadowSymbolDemo {
@State pressed: boolean = false
private normalShadow: ShadowOptions = {
radius: 8,
color: '#33000000',
offsetX: 2,
offsetY: 2
}
private pressedShadow: ShadowOptions = {
radius: 4,
color: '#22000000',
offsetX: 1,
offsetY: 1
}
build() {
Column({ space: 20 }) {
SymbolGlyph($r('sys.symbol.plus_circle'))
.fontSize(56)
.symbolShadow(this.pressed ? this.pressedShadow : this.normalShadow)
.gesture(
LongPressGesture({ repeat: false })
.onAction(() => {
this.pressed = true
})
.onActionEnd(() => {
this.pressed = false
})
)
Text('长按图标观察阴影变化')
.fontSize(14)
.fontColor('#666666')
}
.width('100%')
.padding(24)
}
}
这类用法很适合首页核心入口、工具栏主按钮和带主操作含义的图标。影子轻一点,焦点会更集中,页面层次也更容易立住。
三、禁用态和渐变
禁用态最怕两件事,一是看不出为什么点不了,二是做得太粗糙,整套交互都显得发虚。斜杠遮罩这种表达方式,优点就在于状态语义很直接。它不是单纯把图标压灰,而是明确告诉用户,这个入口目前不允许用。
下面这个写法,适合做权限受限、功能未解锁、会员未开通这类场景。状态一切,图标直接走斜杠遮罩替换。
@Entry
@Component
struct DisabledStateDemo {
@State enabled: boolean = true
@State triggerValue: number = 0
build() {
Column({ space: 20 }) {
SymbolGlyph($r('sys.symbol.eye'))
.fontSize(40)
.fontColor(this.enabled ? '#007DFF' : '#666666')
.symbolEffect(
new ReplaceSymbolEffect(EffectScope.WHOLE, ReplaceEffectType.SLASH_OVERLAY),
this.triggerValue
)
Button(this.enabled ? '切换为禁用态' : '恢复可用态')
.onClick(() => {
this.enabled = !this.enabled
this.triggerValue++
})
}
.width('100%')
.padding(24)
}
}
渐变填充更适合放在品牌入口、活动页图标、会员权益页和视觉强调区。普通设置页和工具页未必需要它,大量使用反而会让焦点变乱。shaderStyle 现在已经支持接进 SymbolGlyph,直接给图标挂 LinearGradientStyle 或 RadialGradientStyle 就行。
@Entry
@Component
struct GradientSymbolDemo {
private brandGradient: LinearGradientOptions = {
angle: 135,
colors: [
['#FF4D4F', 0.0],
['#FF7A45', 0.5],
['#FFC53D', 1.0]
]
}
private glowGradient: RadialGradientOptions = {
center: ['50%', '50%'],
radius: '50%',
colors: [
['#FFFFFF', 0.0],
['#6A5ACD', 1.0]
],
repeating: false
}
build() {
Column({ space: 30 }) {
SymbolGlyph($r('sys.symbol.star'))
.fontSize(72)
.shaderStyle([new LinearGradientStyle(this.brandGradient)])
SymbolGlyph($r('sys.symbol.sparkles'))
.fontSize(72)
.shaderStyle([new RadialGradientStyle(this.glowGradient)])
}
.width('100%')
.padding(24)
}
}
线性渐变适合有方向感的图标,径向渐变更适合做光感和聚焦。品牌页和活动页这类视觉强场景,线性渐变通常更稳;强调光晕、奖章、能量感时,径向渐变会更合适。(华为开发者官网)
四、最佳实践
这四项能力最适合按场景拆开用。高频交互先接 CROSS_FADE,主操作入口优先接 symbolShadow,禁用态单独走斜杠遮罩,品牌和活动图标再用 shaderStyle 做强调。这样分层处理之后,图标系统会很快从能用变成好用。
项目里更稳的做法,是先把几类常用图标组件封出来。收藏按钮一套写法,主操作按钮一套写法,禁用态一套写法,品牌图标再单独一套。状态和视觉规则一旦收进组件层,业务页面就不会到处散落不同写法,后面统一调整风格也会轻很多。
@Component
export struct AppFavoriteSymbol {
@Prop active: boolean
@Prop triggerValue: number
build() {
SymbolGlyph(this.active ? $r('sys.symbol.heart_fill') : $r('sys.symbol.heart'))
.fontSize(28)
.fontColor(this.active ? '#FF4D4F' : '#222222')
.symbolEffect(
new ReplaceSymbolEffect(EffectScope.WHOLE, ReplaceEffectType.CROSS_FADE),
this.triggerValue
)
}
}
@Component
export struct AppPrimarySymbol {
@Prop pressed: boolean
private normalShadow: ShadowOptions = {
radius: 6,
color: '#26000000',
offsetX: 2,
offsetY: 2
}
private pressedShadow: ShadowOptions = {
radius: 3,
color: '#1A000000',
offsetX: 1,
offsetY: 1
}
build() {
SymbolGlyph($r('sys.symbol.plus_circle'))
.fontSize(36)
.symbolShadow(this.pressed ? this.pressedShadow : this.normalShadow)
}
}
这类封法的价值很现实。图标的动态效果一旦散在业务页面里,后面统一改风格会非常痛苦。组件层先收住,后面的维护成本会低很多。
总结
鸿蒙 6 API 20 给 SymbolGlyph 补进来的这几项能力,真正解决的是图标状态表达和视觉层次的落地问题。CROSS_FADE 让状态切换更顺,斜杠遮罩让禁用态更直观,symbolShadow 把层次感放回了组件本身,shaderStyle 则把渐变填充正式接进了图标系统。
更多推荐





所有评论(0)