【共创季稿事节】鸿蒙原生 ArkTS 布局方式之 Stack 实现渐变背景与文字对比度提升

📅 2026/7/4 3:46:52
【共创季稿事节】鸿蒙原生 ArkTS 布局方式之 Stack 实现渐变背景与文字对比度提升
一、引言1.1 为什么需要渐变遮罩在移动应用中经常需要在图片或彩色背景上放置文字——例如轮播图的标题、卡片的内容描述、个人主页的封面信息。这就引出了一个经典的设计难题背景太亮 → 文字看不清 背景太花 → 文字被淹没直接加纯色遮罩可以解决问题但会让界面显得生硬、失去背景的视觉层次感。而渐变遮罩从透明到半透明的过渡可以在提升文字可读性和保留背景深度之间取得完美平衡。1.2 本文核心内容知识点 说明Stack 三层层叠 背景层 遮罩层 文字层LinearGradient API 线性渐变的配置方向、颜色数组rgba 半透明色 从透明到不透明的无缝过渡对比度提升原理 暗色遮罩 vs 亮色背景的文字可见度交互式调参 实时体验不同透明度/方向的效果二、Stack 层叠布局基础2.1 Stack 的 Z 轴坐标系┌──────────────────────────┐│ 屏幕 (X/Y 平面) ││ ││ ┌──────────────────┐ ││ │ Layer 3 (顶层) │ │ ← Z 轴最大值│ ├──────────────────┤ ││ │ Layer 2 (中层) │ ││ ├──────────────────┤ ││ │ Layer 1 (底层) │ │ ← Z 轴起点先声明的组件在最下层│ └──────────────────┘ │└──────────────────────────┘在 ArkUI 的 Stack 中先声明的子组件在最底层后声明的在上面。后声明的子组件会覆盖先声明的。Stack() {Text(‘底层 - 先声明’) // → 在 Z 轴最下面Text(‘中层’) // → 在中间Text(‘顶层 - 后声明’) // → 在 Z 轴最上面覆盖前面所有}2.2 Stack 与 Column/Row 的对比容器 排列方向 适用场景Column Y 轴从上到下 纵向排列Row X 轴从左到右 横向排列Stack Z 轴从后到前 层叠覆盖、叠加效果渐变遮罩场景必须用 Stack——因为我们需要背景、遮罩、文字三者叠在一起而不是排在一起。2.3 Stack 的常用属性Stack() {// 子组件}.alignContent(Alignment.Center) // 子组件整体在 Stack 中的对齐方式.clip(true) // 裁切超出部分三、LinearGradient 渐变 API 详解3.1 API 签名// 在组件上设置线性渐变背景.linearGradient(value: LinearGradient): T// LinearGradient 接口interface LinearGradient {angle?: number | string; // 角度与 direction 二选一direction?: GradientDirection; // 方向与 angle 二选一colors: Array[ResourceColor, number]; // 颜色数组 [颜色, 位置%]repeating?: boolean; // 是否重复}3.2 GradientDirection 枚举枚举值 方向 示意GradientDirection.Left 从左到右 ←GradientDirection.Right 从右到左 →GradientDirection.Top 从上到下 ↑GradientDirection.Bottom 从下到上 ↓GradientDirection.LeftTop 从左上到右下 ↗GradientDirection.LeftBottom 从左下到右上 ↖GradientDirection.RightTop 从右上到左下 ↘GradientDirection.RightBottom 从右下到左上 ↙GradientDirection.None 无方向 -3.3 颜色数组格式colors: [[color1, position1], // position 范围 0.0 ~ 1.0[color2, position2],[color3, position3], // 可以多个颜色节点]例如// 三色渐变红(0%) → 绿(50%) → 蓝(100%)colors: [[‘#FF0000’, 0.0],[‘#00FF00’, 0.5],[‘#0000FF’, 1.0]]3.4 半透明颜色的两种写法// 写法一8 位 Hex每两位代表一个通道最后两位是 Alpha‘#000000CC’ // 黑色Alpha 0xCC ≈ 80%‘#00000000’ // 黑色完全透明// 写法二rgba() 字符串‘rgba(0, 0, 0, 0.8)’ // 黑色 80% 不透明‘rgba(0, 0, 0, 0.0)’ // 黑色完全透明Demo 中使用的是 rgba() 写法private rgba(r: number, g: number, b: number, a: number): string {const alpha: number Math.max(0, Math.min(1, a));returnrgba(${r}, ${g}, ${b}, ${alpha});}因为我们需要动态计算透明度根据 Slider 的值用 rgba() 拼接字符串比 Hex 更灵活。四、Demo 代码逐层剖析4.1 项目结构与路由{“src”: [“pages/StackGradientDemo”]}StackGradientDemo.ets 共 408 行完整结构StackGradientDemo.ets (408行)├── Component StackGradientDemo│ ├── State 变量6个 ← gradientOpacity / startAlpha / endAlpha / gradientDir / demoCard / showOverlay│ ├── 常量数组4组 ← cardColors / cardTitles / cardDescs / dirLabels│ ├── build()│ │ ├── 标题区│ │ ├── Scroll 内容区│ │ │ ├── 对比演示 Row(左:有遮罩, 右:无遮罩)│ │ │ ├── Divider 分割线│ │ │ ├── 卡片选项卡 (4个)│ │ │ └── 核心 Stack (3层)│ │ │ ├── Layer1: 背景渐变 Column│ │ │ ├── Layer2: 半透明遮罩 Column (if showOverlay)│ │ │ └── Layer3: 文字内容 Column│ │ └── 控制面板│ │ ├── 遮罩开关│ │ ├── 渐变方向 (3个按钮)│ │ ├── 起始透明度 Slider│ │ └── 结束透明度 Slider│ ├── Builder demoCardWithOverlay() ← 对比卡片│ └── rgba() 工具方法4.2 六个 State 变量的设计State private startAlpha: number 0.0; // 渐变起点透明度顶部State private endAlpha: number 0.85; // 渐变终点透明度底部State private gradientDir: number 0; // 渐变方向索引State private demoCard: number 0; // 当前卡片索引State private showOverlay: boolean true; // 是否显示遮罩变量 控制的 UI 交互方式showOverlay 整个遮罩层的显隐 点击开关gradientDir 遮罩的渐变方向 三选一按钮startAlpha 遮罩起点的透明度 Slider 0~100%endAlpha 遮罩终点的透明度 Slider 0~100%demoCard 切换 4 种配色方案 选项卡点击4.3 对比演示有遮罩 vs 无遮罩Row() {this.demoCardWithOverlay(true) // 左侧有遮罩this.demoCardWithOverlay(false) // 右侧无遮罩}.height(150)两个卡片使用完全相同的背景红→橙渐变唯一的区别是有无半透明遮罩有遮罩 ✓ 无遮罩 ✗┌─────────────────┐ ┌─────────────────┐│ │ │ ││ │ │ ││ 有遮罩 ✓ │ │ 无遮罩 ✗ ││ 文字清晰可读 │ │ 文字模糊不清 ││ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │ │ │└─────────────────┘ └─────────────────┘↑ 底部有黑色渐变 ↑ 无遮罩文字与背景混淆对比效果有遮罩的一侧文字清晰可见无遮罩的一侧文字几乎融入背景。4.4 核心Stack 三层层叠Stack() {// 第 1 层背景渐变 Column().width(‘100%’).height(‘100%’).linearGradient({direction: GradientDirection.RightBottom,colors: [[this.cardColors[this.demoCard][0], 0.0], // 起始色[this.cardColors[this.demoCard][1], 1.0] // 结束色]})// 第 2 层半透明渐变遮罩★ 核心if (this.showOverlay) {Column().width(‘100%’).height(‘100%’).linearGradient({direction: this.dirValues[this.gradientDir],colors: [[this.rgba(0, 0, 0, this.startAlpha), 0.0], // 起点透明/半透明[this.rgba(0, 0, 0, this.endAlpha), 1.0] // 终点半透明/不透明]})}// 第 3 层文字内容 Column() {// 标题大字号 纯白色 阴影Text(this.cardTitles[this.demoCard]).fontSize(22).fontColor(Color.White).fontWeight(FontWeight.Bold).shadow({ radius: 4, color: ‘#00000066’, offsetX: 1, offsetY: 1 })// 描述文字阴影辅助可读性 Text(this.cardDescs[this.demoCard]) .fontSize(13).fontColor(Color.White).opacity(0.9) .shadow({ radius: 3, color: #00000044, offsetX: 0, offsetY: 1 }) // 底部操作行 Row() { Text( 摄影作品).fontSize(11).fontColor(Color.White).opacity(0.7) Text(查看详情 →).fontSize(11).fontColor(#00B4D8) }}.width(‘100%’).height(‘100%’).padding(16).justifyContent(FlexAlign.End) // 文字靠底部对齐.alignItems(HorizontalAlign.Start) // 文字靠左对齐}.width(‘100%’).height(220).borderRadius(16).clip(true)文字可读性的三重保障保障 实现 作用Layer 2 遮罩 rgba(0,0,0,0→0.85) 渐变 从上方半透明到底部较暗让底部文字更清晰文字颜色 Color.White 白色在所有深色背景上都有高对比度文字阴影 shadow({ blur, color }) 在文字边缘添加暗边与背景产生视觉分离4.5 文字阴影的辅助作用.shadow({radius: 4, // 模糊半径color: ‘#00000066’, // 黑色半透明offsetX: 1, // X 偏移offsetY: 1 // Y 偏移})文字阴影即使在渐变遮罩不够暗的区域也能通过文字边缘的暗色晕边提升可读性。这是一种双重保险——遮罩负责大面积的背景变暗阴影负责文字边缘的精细对比。4.6 四个配色方案private readonly cardColors: string[][] [[‘#4A90D9’, ‘#2ECC71’], // 蓝→绿 — 清新自然[‘#FF6B35’, ‘#E74C3C’], // 橙→红 — 热情活力[‘#9B59B6’, ‘#3498DB’], // 紫→蓝 — 深邃星空[‘#1ABC9C’, ‘#F1C40F’] // 青→黄 — 日落暖阳];四种方案覆盖了不同的色域展示渐变遮罩在各种色彩背景上的通用效果。4.7 控制面板的交互设计控制面板包含四个维度的参数调节① 渐变遮罩开关Row() {Text(‘渐变遮罩’)Text(this.showOverlay ? ‘ 已开启’ : ‘ 已关闭’)}.gesture(TapGesture().onAction(() {this.showOverlay !this.showOverlay;}))点击切换立即看到卡片上的遮罩层出现/消失。② 渐变方向3 种Button(‘从上到下 (Bottom)’) // 默认文字在底部渐变底部最暗Button(‘从下到上 (Top)’) // 文字在顶部时使用Button(‘对角线 (RightBottom)’) // 从右上到左下③ 起始透明度 Slider控制渐变起点的透明度。0% 完全透明保留背景100% 完全不透明纯色。④ 结束透明度 Slider控制渐变终点的透明度。通常设置较高的值如 85%让文字区域的背景足够暗。这些控制组合起来用户可以实时回答以下问题遮罩要还是不遮罩对比测试遮罩从哪个方向渐变最好遮罩要多暗才够这个透明度在不同的背景色上效果一样吗五、文字对比度提升的原理5.1 WCAG 对比度标准Web Content Accessibility Guidelines (WCAG) 建议普通文字对比度 ≥ 4.5:1大文字≥18px对比度 ≥ 3:1对比度计算公式(L1 0.05) / (L2 0.05)其中 L1 是较亮颜色的相对亮度L2 是较暗颜色的相对亮度。5.2 渐变遮罩如何提升对比度无遮罩时背景亮度 0.7浅色渐变背景 → 文字对比度不足文字亮度 1.0白色有遮罩时底部区域背景亮度 0.7 × (1 - 0.85) 0.105大幅降低文字亮度 1.0白色对比度 (1.0 0.05) / (0.105 0.05) ≈ 6.8 : 1 ✅ 达标遮罩通过降低背景区域的亮度间接提升了文字与背景的对比度。5.3 渐变 vs 纯色的对比纯色遮罩 渐变遮罩▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ← 顶部保留原始背景▓▓▓▓▓▓▓▓▓▓ ░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░░▓▓▓ ← 底部最深视觉感受 视觉感受生硬、扁平 自然、有深度文字区域暗但 文字区域暗上方损失了 但上方保留了背景层次感 背景的层次感渐变遮罩的核心优势既保证了底部文字区域的对比度又保留了顶部背景的视觉效果。六、常见问题与坑点6.1 .clip(true) 缺失导致圆角失效// ❌ borderRadius 不生效Stack() {// …}.borderRadius(16)// ✅ 需要 clip(true) 让 borderRadius 裁剪子组件Stack() {// …}.borderRadius(16).clip(true)borderRadius 只作用于 Stack 容器本身如果不设置 .clip(true)内部的 Column 背景渐变会超出圆角边界。6.2 渐变色数组的格式// ✅ 正确格式colors: [[‘#FF0000’, 0.0], // [颜色, 位置][‘#0000FF’, 1.0]]// ❌ 错误格式colors: [‘#FF0000’, ‘#0000FF’] // 数组元素必须是 [color, position] 对6.3 rgba 字符串中的透明度// ✅ 正确rgba 的 alpha 范围 0.0 ~ 1.0‘rgba(0, 0, 0, 0.85)’// ❌ 常见错误使用 0~255 的 alpha‘rgba(0, 0, 0, 85)’ // 85 会被当作 0.85 吗不会rgba 规范中 alpha 是 0~16.4 渐变方向与文字位置不匹配// 如果文字在底部 → 遮罩应从上到下渐变底部最暗.gradient({ direction: Bottom, colors: [[透明], [半透明]] })// ↑ 0% 在顶部100% 在底部// 如果文字在顶部 → 遮罩应从下到上渐变顶部最暗.gradient({ direction: Top, colors: [[透明], [半透明]] })6.5 多个渐变叠加的顺序Stack 中叠加多个 linearGradient 时只有最上层的渐变可见不透明区域会覆盖下层。Stack() {Column().linearGradient({ /* 底层渐变/ })Column().linearGradient({ /上层渐变 - 半透明 */ })// 上层半透明区域 → 可以看到下层// 上层不透明区域 → 完全覆盖下层}6.6 if 条件在 Stack 中的使用Stack() {// … 背景层if (this.showOverlay) {// 遮罩层只有条件为 true 时才渲染Column().linearGradient({ … })}// … 文字层始终渲染}在 Stack 中使用 if 控制某层的显隐是完全合法的——if 在 ArkTS 中本质上是条件渲染。七、实战不同场景的文字对比度方案7.1 轮播图标题Stack() {Image($r(‘app.media.banner’)).width(‘100%’).height(200)// 底部渐变遮罩Column().width(‘100%’).height(‘100%’).linearGradient({direction: GradientDirection.Bottom,colors: [[‘#00000000’, 0.5], [‘#000000CC’, 1.0]]})// 标题文字Column() {Text(‘轮播标题’).fontSize(20).fontColor(Color.White)Text(‘副标题描述’).fontSize(13).fontColor(Color.White).opacity(0.8)}.width(‘100%’).padding(16).justifyContent(FlexAlign.End).alignItems(HorizontalAlign.Start)}.clip(true).borderRadius(12)7.2 个人主页封面Stack() {Image($r(‘app.media.cover’)).width(‘100%’).height(250)// 顶部 底部双渐变Column().width(‘100%’).height(‘100%’).linearGradient({direction: GradientDirection.Bottom,colors: [[‘#00000088’, 0.0], [‘#00000000’, 0.3], [‘#00000000’, 0.7], [‘#000000CC’, 1.0]]})// 用户信息Column() {Text(‘用户名’).fontSize(24)Text(‘简介’).fontSize(14)}.width(‘100%’).padding(16).justifyContent(FlexAlign.End).alignItems(HorizontalAlign.Center)}7.3 暗色主题卡片Stack() {// 深色背景Column().backgroundColor(‘#1a1a2e’).width(‘100%’).height(‘100%’)// 高光渐变左上到右下营造立体感Column().width(‘100%’).height(‘100%’).linearGradient({direction: GradientDirection.RightBottom,colors: [[‘#ffffff11’, 0.0], [‘#00000000’, 1.0]]})// 白色文字在深色背景上天然高对比度Text(‘暗色卡片’).fontSize(18).fontColor(Color.White)}.borderRadius(12).clip(true)八、最佳实践清单8.1 渐变遮罩参数建议场景 起始透明度 结束透明度 方向轮播图底部标题 0% 70~85% Bottom卡片顶部标题 70~85% 0% Top全屏背景文字 0% 80% Bottom头像/图标底部 0% 60% Bottom8.2 文字对比度自检清单文字是否为白色推荐或深灰色在浅色遮罩上是否添加了 shadow() 作为辅助保障遮罩终点透明度是否 ≥ 70%是否在模拟器和真机上分别测试了不同背景色渐变方向是否与文字位置匹配8.3 Stack 层叠的通用模板Stack() {// Layer 1背景Image() / Column().linearGradient() / Any// Layer 2遮罩可选if (needOverlay) {Column().linearGradient({ /* 渐变遮罩 */ })}// Layer 3内容Column() {/* 文字、按钮等 */}.padding(16).justifyContent(FlexAlign.End) // 内容靠底部.alignItems(HorizontalAlign.Start) // 内容靠左}.width(‘100%’).height(desiredHeight).borderRadius(radius).clip(true)8.4 性能注意事项.linearGradient() 是 GPU 加速的性能开销很小避免在滚动列表中过多使用 Stack 渐变可能导致重绘对于静态卡片使用 Builder 拆分可以提升复用效率九、结语9.1 核心回顾Stack 渐变遮罩 背景深度 文字可读性三行代码解决背景太花、文字不清的设计难题第 1 行.linearGradient({ direction, colors }) → 背景第 2 行.linearGradient({ colors: [[透明],[半透明]] }) → 遮罩第 3 行Text().fontColor(White).shadow(…) → 文字9.2 从本 Demo 学到的技术技术 要点Stack 层叠 先声明底层后声明顶层LinearGradient direction colors: [[color, pos]]rgba 透明度 rgba(r,g,b,a) 中 a 的范围 0~1文字阴影 shadow() 作为对比度辅助clip 属性 让 borderRadius 裁切内部子组件9.3 下一步探索RadialGradient 径向渐变中心向外发散的渐变效果SweepGradient 扫描渐变围绕中心旋转的渐变类似色相环BackgroundBlurStyle 背景模糊用毛玻璃效果替代渐变遮罩多渐变叠加同时使用线性渐变 径向渐变创造复杂视觉效果动画渐变使用 animateTo 让渐变参数随时间变化附录 A完整 Demo 代码/*StackGradientDemo.ets —— Stack 渐变背景提升文字可读性 核心技术 Stack() —— 层叠布局将背景/渐变/文字叠加在不同层.linearGradient() —— 线性渐变色背景半透明渐变色遮罩 —— 背景图上方叠加渐变层提升文字对比度 Stack 三层层叠原理 Stack()Layer1: 背景渐变 ColumnLayer2: 半透明遮罩 Column (if showOverlay)Layer3: 文字内容 Column (底部对齐)*/EntryComponentstruct StackGradientDemo {State private startAlpha: number 0.0;State private endAlpha: number 0.85;State private gradientDir: number 0;State private demoCard: number 0;State private showOverlay: boolean true;private readonly cardColors: string[][] [[‘#4A90D9’, ‘#2ECC71’], [‘#FF6B35’, ‘#E74C3C’],[‘#9B59B6’, ‘#3498DB’], [‘#1ABC9C’, ‘#F1C40F’]];private readonly cardTitles: string[] [‘自然风光’, ‘城市夜景’, ‘星空宇宙’, ‘日落余晖’];private readonly dirValues: GradientDirection[] [GradientDirection.Bottom, GradientDirection.Top, GradientDirection.RightBottom];build() {Column() {Text(‘Stack 渐变提升文字对比度’).fontSize(20).fontWeight(FontWeight.Bold).fontColor(Color.White).textAlign(TextAlign.Center).width(‘100%’).padding({ top: 14, bottom: 2 })Text(‘用 Stack 叠加半透明渐变遮罩提升文字可读性’).fontSize(12).fontColor(Color.Gray).textAlign(TextAlign.Center).width(‘100%’).padding({ bottom: 4 })Scroll() { Column() { // 对比演示 Text(对比有渐变遮罩 → 文字清晰 | 无遮罩 → 模糊) .fontSize(11).fontColor(Color.Gray).width(100%).textAlign(TextAlign.Center) Row() { this.demoCardWithOverlay(true) this.demoCardWithOverlay(false) }.width(100%).height(150).padding({ left: 4, right: 4 }) Divider().height(1).color(#ffffff22).margin({ top: 10, bottom: 8 }) // 核心 Stack Stack() { // Layer 1: 背景 Column().width(100%).height(100%) .linearGradient({ direction: GradientDirection.RightBottom, colors: [[this.cardColors[this.demoCard][0], 0.0], [this.cardColors[this.demoCard][1], 1.0]] }) // Layer 2: 遮罩 if (this.showOverlay) { Column().width(100%).height(100%) .linearGradient({ direction: this.dirValues[this.gradientDir], colors: [[this.rgba(0,0,0,this.startAlpha), 0.0], [this.rgba(0,0,0,this.endAlpha), 1.0]] }) } // Layer 3: 文字 Column() { Text(this.cardTitles[this.demoCard]).fontSize(22) .fontColor(Color.White).fontWeight(FontWeight.Bold) .shadow({ radius:4, color:#00000066, offsetX:1, offsetY:1 }) Text(this.cardDescs[this.demoCard]).fontSize(13) .fontColor(Color.White).opacity(0.9).lineHeight(20).margin({ top:6 }) .shadow({ radius:3, color:#00000044, offsetX:0, offsetY:1 }) }.width(100%).height(100%).padding(16) .justifyContent(FlexAlign.End).alignItems(HorizontalAlign.Start) }.width(100%).height(220).borderRadius(16).clip(true).margin({ left:12, right:12 }) }.width(100%).padding({ top: 8, bottom: 16 }) }.layoutWeight(1).width(100%) // 控制面板 Column() { Row() { Text(渐变遮罩).fontSize(12).fontColor(Color.White) Text(this.showOverlay ? 已开启 : 已关闭).fontSize(11) .fontColor(this.showOverlay ? #2ECC71 : #E74C3C).margin({ left: 6 }) }.width(100%).gesture(TapGesture().onAction(() { this.showOverlay !this.showOverlay; })) Row() { this.dirButton(从上到下, 0) this.dirButton(从下到上, 1) this.dirButton(对角线, 2) }.width(100%).margin({ top: 4 }) Row() { Text(起始透明度 ${(this.startAlpha*100).toFixed(0)}%).fontSize(11).fontColor(Color.Gray) Slider({ value: this.startAlpha*100, min:0, max:100, step:5 }) .layoutWeight(1).margin({ left:8, right:8 }) .onChange((v) { this.startAlpha v/100; }) }.width(100%).margin({ top: 4 }) Row() { Text(结束透明度 ${(this.endAlpha*100).toFixed(0)}%).fontSize(11).fontColor(Color.Gray) Slider({ value: this.endAlpha*100, min:0, max:100, step:5 }) .layoutWeight(1).margin({ left:8, right:8 }) .onChange((v) { this.endAlpha v/100; }) }.width(100%).margin({ top: 4 }) }.width(100%).backgroundColor(#1a1a3e).borderRadius(12) .padding(12).margin({ left:12, right:12, bottom:10 }) }.width(100%).height(100%).backgroundColor(#0f3460)}BuilderdemoCardWithOverlay(hasOverlay: boolean) {Stack() {Column().width(‘100%’).height(‘100%’).linearGradient({direction: GradientDirection.RightBottom,colors: [[‘#E74C3C’, 0.0], [‘#F39C12’, 1.0]]})if (hasOverlay) {Column().width(‘100%’).height(‘100%’).linearGradient({direction: GradientDirection.Bottom,colors: [[‘#00000000’, 0.0], [‘#000000CC’, 1.0]]})}Column() {Text(hasOverlay ? ‘有遮罩 ✓’ : ‘无遮罩 ✗’).fontSize(13).fontColor(Color.White).fontWeight(FontWeight.Bold)Text(hasOverlay ? ‘文字清晰可读’ : ‘文字模糊不清’).fontSize(11).fontColor(Color.White).opacity(hasOverlay ? 0.9 : 0.4)}.width(‘100%’).padding(10).justifyContent(FlexAlign.End).alignItems(HorizontalAlign.Start)}.width(‘100%’).height(‘100%’).borderRadius(10).clip(true).margin({ left:4, right:4 })}BuilderdirButton(label: string, idx: number) {Button(label).height(28).fontSize(10).backgroundColor(this.gradientDir idx ? ‘#9B59B6’ : ‘#333’).fontColor(Color.White).borderRadius(6).layoutWeight(1).margin({ left:2, right:2 }).gesture(TapGesture().onAction(() { this.gradientDir idx; }))}private rgba(r: number, g: number, b: number, a: number): string {returnrgba(${r}, ${g}, ${b}, ${Math.max(0, Math.min(1, a))});}}附录 B参考资料HarmonyOS NEXT 开发者文档 — Stack 容器HarmonyOS NEXT 开发者文档 — LinearGradientHarmonyOS NEXT 开发者文档 — GradientDirectionWCAG 对比度规范版权声明本文为 HarmonyOS NEXT 技术分享系列的第八篇遵循 CC BY-NC 4.0 协议。欢迎转载但请注明出处。系列文章第一篇TapGesture 点击手势布局第二篇PanGesture 拖拽手势布局第三篇GestureGroup 组合手势布局第四篇Column 垂直排列入门第五篇Column Scroll 可滚动列表第六篇Column Flex 弹性混合布局第七篇Column 垂直时间轴组件第八篇Stack 渐变文字对比度本文