前言

之前的 3D 拆解分析 App 全站只有紫色一个主色——按钮是紫色的,选中态是紫色的,部件编号也是紫色的。好看但单调,所有元素看起来都是"同一层级"的。

这次迭代引入了多色系统:红色代表手机、紫色代表手表、蓝色代表相机、绿色代表大件设备。每个场景有自己的颜色,部件清单里的每个部件也有自己的颜色。颜色不再是装饰,而是信息的一部分。
完整效果
在这里插入图片描述

首页:从单色到多色

调色板定义

const A: string = '#8A5DFA'   // 品牌紫
const A2: string = '#FF6B6B'  // 红色 — 手机
const A3: string = '#00B894'  // 绿色 — 大件
const A4: string = '#5DADE2'  // 蓝色 — 相机
const A5: string = '#FECA57'  // 黄色 — 装饰

在这里插入图片描述

五个颜色各有明确的用途:

  • A(紫色):品牌主色,用于按钮、选中态、通用元素
  • A2(红色):手机场景的专属色
  • A3(绿色):大件设备场景的专属色
  • A4(蓝色):相机场景的专属色
  • A5(黄色):装饰性用途

HarmonyOS NEXT 标识

Row() {
  Row() {}.width(6).height(6).borderRadius(3).backgroundColor(A3)
  Text(' HarmonyOS NEXT').fontSize(12).fontColor(T3)
}.margin({ top: 4 })

标题下方加了一个绿色小圆点 + “HarmonyOS NEXT” 文字。绿色圆点是 HarmonyOS 品牌色,一行文字就交代了技术栈。比单独放一个 logo 更轻量。
在这里插入图片描述

统计卡片:信息密度的新维度

Row({ space: 10 }) {
  this.Stat('5 种', '基底选择', A)
  this.Stat('4 档', '拆解精度', A3)
  this.Stat('2~16', '部件数量', A4)
  this.Stat('3D', '爆炸展示', A5)
}
.width('100%').margin({ bottom: 26 })

在这里插入图片描述

四个统计卡片用不同颜色标记:

@Builder Stat(value: string, label: string, color: string) {
  Column() {
    Text(value).fontSize(16).fontWeight(FontWeight.Bold).fontColor(color)
    Text(label).fontSize(10).fontColor(T3).margin({ top: 2 })
  }
  .layoutWeight(1).padding({ top: 12, bottom: 12 })
  .backgroundColor(CW).borderRadius(14)
  .shadow({ radius: 4, color: 'rgba(0,0,0,0.03)', offsetY: 1 })
}

每个卡片只有两个数字——值和标签。颜色区分了四个不同的维度:选择多样性、精度档位、部件范围、展示方式。

为什么不用统一颜色?

如果四个卡片都是紫色,用户需要读文字才能区分。用四种颜色后,用户扫一眼就知道"这是四个不同的东西"——颜色本身就在传递信息。

场景卡片:每个场景一个颜色

this.SCard(0, '手机拆解', '3×3 · 9部件', '#FF6B6B', '#FFF5F5')
this.SCard(1, '手表拆解', '3×3 · 9部件', A, AL)
this.SCard(2, '相机拆解', '4×3 · 12部件', '#5DADE2', '#EBF5FB')
this.SCard(3, '大件设备', '4×4 · 16部件', '#00B894', '#E8F8F5')

在这里插入图片描述

每个卡片传入两个颜色参数:color(文字/边框/选中态)和 bg(选中时的背景色)。背景色是主色的 10% 透明度版本:

场景 主色 背景色
手机 #FF6B6B(红) #FFF5F5(浅红)
手表 #8A5DFA(紫) #F4F1FC(浅紫)
相机 #5DADE2(蓝) #EBF5FB(浅蓝)
大件 #00B894(绿) #E8F8F5(浅绿)

选中态的变化:

.backgroundColor(this.preset === idx ? bg : CW)
.border({ width: this.preset === idx ? 1.5 : 1,
  color: this.preset === idx ? color + '30' : '#F0EEF6' })

未选中时是白色背景 + 淡灰边框。选中时变成彩色背景 + 彩色半透明边框。用户一眼就能看出当前选的是哪个场景。

部件数量标签的颜色联动

Text(this.parts() + ' 部件').fontSize(11).fontWeight(FontWeight.Bold)
  .fontColor(this.preset === 0 ? A2 : (this.preset === 1 ? A : (this.preset === 2 ? A4 : A3)))
  .padding({ left: 8, right: 8, top: 3, bottom: 3 })
  .backgroundColor(this.preset === 0 ? '#FFF5F5' : (this.preset === 1 ? AL : (this.preset === 2 ? '#EBF5FB' : '#E8F8F5')))

部件数量标签的颜色跟随当前选中的场景——选手机时显示红色,选手表时显示紫色。这种"颜色联动"让用户在切换场景时能直观感受到变化。

Step 步骤卡片:编号角标

@Builder Step(num: string, icon: string, title: string, color: string) {
  Column() {
    Stack({ alignContent: Alignment.TopEnd }) {
      Column() {
        Text(icon).fontSize(24).margin({ bottom: 8 })
        Text(title).fontSize(13).fontWeight(FontWeight.Bold).fontColor(T1)
      }
      .padding({ top: 14, bottom: 14, left: 12, right: 12 })
      .alignItems(HorizontalAlign.Center)
      .backgroundColor(CW).borderRadius(16).border({ width: 1, color: '#F0EEF6' })

      Row() {
        Text(num).fontSize(9).fontWeight(FontWeight.Bold).fontColor(Color.White)
      }
      .width(18).height(18).borderRadius(9).backgroundColor(color)
      .justifyContent(FlexAlign.Center).alignItems(VerticalAlign.Center)
      .position({ x: 0, y: 0 })
    }
  }.width(85)
}

每个步骤卡片右上角有一个彩色编号角标:

  • 步骤 1:紫色(品牌色)
  • 步骤 2:绿色(代表"智能")
  • 步骤 3:红色(代表"爆炸")

Stack({ alignContent: Alignment.TopEnd }) 让角标定位在卡片右上角。角标本身用 position({ x: 0, y: 0 }) 微调位置,确保不会被卡片边框裁剪。

Hero Card 渐变升级

旧版渐变:

.linearGradient({ angle: 150, colors: [[A,0],['#9B7CF4',0.4],['#C4B5FD',1]] })

新版渐变:

.linearGradient({ angle: 135, colors: [[A, 0], ['#6C3CE0', 0.5], ['#A78BFA', 1]] })

三个变化:

  1. 角度从 150° 改为 135°(更标准的对角线渐变)
  2. 中间色从 #9B7CF4(浅紫)变成 #6C3CE0(深紫)
  3. 渐变更深,和白色文字的对比度更高

装饰性圆也从 2 个增加到 3 个:

Circle({ width: 160, height: 160 }).fill('rgba(255,255,255,0.06)').position({ x: '60%', y: -40 })
Circle({ width: 100, height: 100 }).fill('rgba(255,255,255,0.08)').position({ x: '5%', y: 90 })
Circle({ width: 60, height: 60 }).fill('rgba(255,255,255,0.10)').position({ x: '80%', y: 120 })

三个圆大小递减(160 → 100 → 60),透明度递增(0.06 → 0.08 → 0.10),形成视觉层次。

分析工作台:部件的个性化着色

调色板数组

const PC: string[] = ['#8A5DFA','#FF6B6B','#00B894','#5DADE2','#FECA57','#E8734A','#6C5CE7','#48DBFB']

八个颜色,循环分配给部件。如果部件超过 8 个,用 i % PC.length 取模循环。

部件卡片的颜色分配

cards.push({
  n: ns[i], idx: i + 1,
  sub: '网格(' + col + ',' + row + ') · 深度' + Math.round(d * 100) + '%',
  d: d, c: PC[i % PC.length]
})

每个部件的颜色在 rebuild() 时就确定了,存在 PCardc 字段里。

部件清单:彩色编号 + 彩色深度条

ForEach(this.pc, (c: PCard) => {
  Row() {
    // 彩色编号圆圈
    Column() {
      Text(c.idx.toString()).fontSize(11).fontWeight(FontWeight.Bold).fontColor(Color.White)
    }
    .width(24).height(24).borderRadius(12).backgroundColor(c.c)
    .justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)

    // 彩色深度条
    Row() {}.width(c.d * 44).height(5).borderRadius(3)
      .backgroundColor(c.c + '55').margin({ left: 10, right: 10 })

    // 名称和位置
    Column() {
      Text(c.n).fontSize(13).fontWeight(FontWeight.Medium).fontColor(T1)
      Text(c.sub).fontSize(10).fontColor(T3)
    }.alignItems(HorizontalAlign.Start).layoutWeight(1)

    // 右箭头
    SymbolGlyph($r('sys.symbol.chevron_right')).fontSize(12).fontColor(['#DDD'])
  }
})

每个部件卡片有三个彩色元素:

  1. 编号圆圈:白色数字 + 彩色背景
  2. 深度条:彩色(55% 透明度)
  3. 右箭头:淡灰色(不参与颜色编码)

头部信息:彩色圆点

Row({ space: 8 }) {
  this.InfoDot(A, this.count + ' 部件')
  this.InfoDot(A3, this.cols + '×' + this.rows + ' 网格')
}
@Builder InfoDot(color: string, text: string) {
  Row() {
    Row() {}.width(5).height(5).borderRadius(3).backgroundColor(color)
    Text(' ' + text).fontSize(10).fontColor(T2)
  }
}

部件数量用紫色圆点,网格尺寸用绿色圆点。两个信息用不同颜色标记,一眼就能区分。

工作区背景:渐变 + 点阵

// 渐变背景
Column() {}
  .width('100%').height('100%').borderRadius(18)
  .linearGradient({ angle: 180,
    colors: [['#F0EEFA', 0], ['#F8F7FC', 0.5], ['#F5F5F9', 1]] })

// 点阵装饰
Circle({ width: 3, height: 3 }).fill(A + '08').position({ x: '10%', y: '10%' })
Circle({ width: 3, height: 3 }).fill(A + '06').position({ x: '30%', y: '20%' })
// ... 更多圆点

工作区背景从纯色变成了渐变 + 点阵。点阵用 A + '08'(8% 透明度紫色)的微小圆点,间距不规则,形成类似"网格纸"的质感。

这种装饰不影响功能,但让空白区域不那么单调。

Slider 的状态联动

Slider({ value: this.val * 100, min: 0, max: 100, step: 5, style: SliderStyle.OutSet })
  .blockColor(this.on ? A : '#D0CED8')
  .trackColor('#F0ECF8')
  .selectedColor('#A78BFA')

未爆炸时滑块是灰色(#D0CED8),爆炸时变成紫色(A)。颜色变化暗示"现在可以操作了"。

颜色系统设计的原则

1. 一个颜色一个含义

紫色(A)  → 品牌色、通用操作、手表场景
红色(A2) → 手机场景、爆炸操作、步骤 3
绿色(A3) → 大件场景、网格信息、步骤 2
蓝色(A4) → 相机场景、部件数量
黄色(A5) → 装饰性用途

每个颜色都有明确的语义。用户一旦建立了"红色=手机"的心理映射,后续看到红色就能快速关联到手机相关的信息。

2. 背景色是主色的淡化版

主色 #FF6B6B → 背景色 #FFF5F5
主色 #8A5DFA → 背景色 #F4F1FC
主色 #5DADE2 → 背景色 #EBF5FB
主色 #00B894 → 背景色 #E8F8F5

背景色是主色加了约 90% 白色的结果。这种"同色系淡化"保证了视觉一致性——选中卡片的背景和它的文字颜色属于同一个色系。

3. 部件颜色循环但不重复

const PC: string[] = ['#8A5DFA','#FF6B6B','#00B894','#5DADE2','#FECA57','#E8734A','#6C5CE7','#48DBFB']
c: PC[i % PC.length]

八个颜色循环使用。在 4×4 网格(16 个部件)中,每个颜色会出现两次。但相邻的部件颜色不同,视觉上不会混淆。

4. 不是所有元素都需要颜色

右箭头 SymbolGlyph 用淡灰色(#DDD),不参与颜色编码。因为它只是装饰性的引导符号,不需要传递信息。如果右箭头也用彩色,会增加视觉噪音。

踩坑记录

1. 半透明背景色的计算

// 主色 #FF6B6B → 浅红背景 #FFF5F5

计算方式:把主色的 RGB 值和白色(#FFFFFF)按 10:90 的比例混合。

R: 255 * 0.1 + 255 * 0.9 = 255 → FF
G: 107 * 0.1 + 255 * 0.9 = 240 → F0
B: 107 * 0.1 + 255 * 0.9 = 240 → F0

实际结果是 #FFF0F0,和代码中的 #FFF5F5 有微小差异——手动微调过,让视觉效果更好。

2. 颜色对比度

选中态的彩色背景上需要放白色文字(✓ 符号)。确保对比度足够:

#FF6B6B(红)上的白色 → 对比度 3.5:1 ✓
#8A5DFA(紫)上的白色 → 对比度 4.2:1 ✓
#00B894(绿)上的白色 → 对比度 2.8:1 ⚠️ 偏低
#5DADE2(蓝)上的白色 → 对比度 2.5:1 ⚠️ 偏低

绿色和蓝色上的白色对比度偏低,小字号(12px)可能看不清。实际使用中可以通过加深主色或加粗文字来补偿。

3. 颜色过多导致的混乱

五种颜色已经接近用户短期记忆的上限(心理学上的 “7±2” 法则)。如果后续再加颜色,建议:

  • 新颜色只用于新功能区块,不复用已有颜色
  • 同一屏幕内同时出现的颜色不超过 5 种
  • 用灰色系(T1/T2/T3)作为中性色平衡视觉

4. 渐变背景上的颜色叠加

Column() {}
  .linearGradient({ angle: 180,
    colors: [['#F0EEFA', 0], ['#F8F7FC', 0.5], ['#F5F5F9', 1]] })

工作区的渐变背景会影响上面叠加元素的颜色感知。浅色元素在浅紫渐变上会显得更紫,在浅灰渐变上会显得更灰。点阵圆点用 A + '08'(极低透明度)就是为了减少这种干扰。

写在最后

多色系统的核心价值是"用颜色传递信息"——红色告诉你是手机,紫色告诉你是手表,绿色告诉你是大件。用户不需要读文字,颜色本身就在做分类。

但颜色不是越多越好。五种颜色是目前的平衡点:足够区分不同场景,又不至于让用户眼花。如果后续功能增加,可能需要重新评估颜色系统的扩展性。

部件的个性化着色是这次最实用的改进——在部件清单里,彩色编号 + 彩色深度条让用户能快速定位"第 3 个部件(绿色)在网格的左下角"。比单一颜色的编号圆圈高效很多。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐