鸿蒙应用开发实战【03】— ArkUI 设计令牌体系构建(颜色/字体/动画)

前言

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
在这里插入图片描述

在多人协作的项目中,如果每个开发者随意使用颜色字面量('#4F7CFF')、字号(14)、动画时长(300),项目很快就会陷入"颜色一致性噩梦"。**设计令牌(Design Token)**就是解决这个问题的最佳实践——把设计决策集中存储为命名常量,让整个项目通过名字引用而非直接使用原始值。

本篇介绍号码助手项目中的设计令牌体系:AppColorsAppFontsAppAnimations,以及 ArkUI 中一个极易踩坑的颜色格式问题

本篇重点#AARRGGBB 颜色格式、设计令牌封装规范、颜色语义化命名、ArkUI 字体常量、动画曲线封装。


一、为什么需要设计令牌

1.1 没有设计令牌时的问题

// ❌ 没有设计令牌时,颜色散落在各处
Text('号码助手').fontColor('#14141E')          // 主文字色
Text('副标题').fontColor('#14141E80')           // 副文字色,但哪个是哪个?
.backgroundColor('#B8FFFFFF')                    // 卡片背景色,什么含义?
.border({ color: '#14141E14' })                  // 边框色,语义不明

维护困难:设计稿改了主色调,要逐文件搜索替换

1.2 使用设计令牌后

// ✅ 使用设计令牌后,语义清晰
Text('号码助手').fontColor(AppColors.TEXT)
Text('副标题').fontColor(AppColors.TEXT_2)
.backgroundColor(AppColors.CARD)
.border({ color: AppColors.LINE })

维护简单:改一处 AppColors.PRIMARY = '#3A5FD4',全项目生效


二、AppColors — 颜色设计令牌

2.1 完整颜色系统

号码助手使用的完整颜色令牌体系如下:

// AppColors.ets
export class AppColors {
  // ── 主色调 ──────────────────────────────────
  static readonly PRIMARY: string = '#4F7CFF'         // 主色:蓝紫
  static readonly PRIMARY_2: string = '#7C5CFF'        // 辅色:紫
  static readonly PRIMARY_BG: string = '#F0F3FF'       // 主色浅背景

  // ── 功能色 ──────────────────────────────────
  static readonly DANGER: string = '#FF2E4D'           // 危险/删除
  static readonly DANGER_BG: string = '#FFF0F2'        // 危险背景
  static readonly WARN: string = '#FF7A1E'             // 警告/待换绑
  static readonly WARN_BG: string = '#FFF5EE'          // 警告背景
  static readonly CYAN: string = '#20C0CE'             // 青色/强调

  // ── 文字色 ──────────────────────────────────
  static readonly TEXT: string = '#14141E'             // 主文字(近黑)
  static readonly TEXT_2: string = '#14141E80'         // 副文字(半透明)
  static readonly TEXT_3: string = '#14141E4D'         // 占位文字
  static readonly MUTED: string = '#999999'            // 禁用文字

  // ── 背景色 ──────────────────────────────────
  static readonly BG: string = '#F5F7FF'               // 页面背景
  static readonly MUTED_BG: string = '#F2F2F7'         // 灰色背景

  // ── 卡片与边框(关键!使用 #AARRGGBB 格式)──
  static readonly CARD: string = '#B8FFFFFF'           // 毛玻璃卡片(72% 透明度白色)
  static readonly CARD_B: string = '#E6FFFFFF'         // 浅色卡片(90% 透明度白色)
  static readonly LINE: string = '#14141E14'           // 分割线/边框(8% 透明度黑色)
}

2.2 颜色分类对照表

类别 令牌名 十六进制值 用途
主色 PRIMARY #4F7CFF 按钮、强调、主要链接
辅色 PRIMARY_2 #7C5CFF 渐变终点、次要强调
危险 DANGER #FF2E4D 删除按钮、错误提示
警告 WARN #FF7A1E 待换绑状态、橙色提示
主文字 TEXT #14141E 标题、正文
副文字 TEXT_2 #14141E80 说明文字(带 50% 透明度)
页面背景 BG #F5F7FF 所有页面的背景色
卡片 CARD #B8FFFFFF 列表卡片(毛玻璃效果)

三、ArkUI 颜色格式深度解析(重要踩坑)

3.1 问题现象

号码助手在开发初期,所有卡片都显示为淡黄色,而设计稿要求是半透明白色

颜色格式对比截图

图1:左图为颜色格式错误时的黄色卡片,右图为修复后的正确半透明白色卡片

3.2 根本原因

ArkUI 的颜色格式与 CSS/Android 不同!

平台 颜色格式 Alpha 位置
CSS / Web #RRGGBBAA 最后
Android XML #AARRGGBB 最前
ArkUI #AARRGGBB 最前

以 72% 透明度的白色为例:

// ❌ 错误写法(CSS 格式,AA 在最后)
// #FFFFFF 后面加 B8 表示 72% 不透明度
static readonly CARD: string = '#FFFFFFB8'
// ArkUI 解析为:AA=0xFF(完全不透明), R=0xFF, G=0xFF, B=0xB8
// 结果:RGB(255, 255, 184) = 淡黄色!

// ✅ 正确写法(ArkUI 格式,AA 在最前)
// B8 在最前面表示 72% 不透明度
static readonly CARD: string = '#B8FFFFFF'
// ArkUI 解析为:AA=0xB8(72% 不透明), R=0xFF, G=0xFF, B=0xFF
// 结果:带透明度的白色(正确!)

3.3 Alpha 值换算表

不透明度 十六进制 AA 值 用途示例
100% FF 完全不透明
90% E6 CARD_B 卡片
80% CC 轻微透明
72% B8 CARD 毛玻璃卡片
50% 80 TEXT_2 副文字
30% 4D TEXT_3 占位文字
8% 14 LINE 分割线
0% 00 完全透明

3.4 实际修复代码

// 修复前(错误):颜色格式错误,导致卡片变黄
// static readonly CARD: string = '#FFFFFFB8'   // 错误!
// static readonly CARD_B: string = '#FFFFFFE6'  // 错误!

// 修复后(正确):#AARRGGBB 格式
static readonly CARD: string = '#B8FFFFFF'   // ✅ 72% 不透明白色
static readonly CARD_B: string = '#E6FFFFFF' // ✅ 90% 不透明白色
static readonly LINE: string = '#14141E14'   // ✅  8% 不透明深色(分割线)
static readonly TEXT_2: string = '#14141E80' // ✅ 50% 不透明深色(副文字)

记忆口诀:ArkUI 颜色格式 = A(透明度)在前,RGB 在后。与 CSS 的习惯正好相反!


四、AppFonts — 字体设计令牌

4.1 字体令牌定义

// AppFonts.ets
export class AppFonts {
  // ── 字号(vp 单位)──────────────────────────
  static readonly SIZE_DISPLAY: number = 28       // 大标题(欢迎语)
  static readonly SIZE_TITLE: number = 20         // 页面标题
  static readonly SIZE_SECTION: number = 16       // 分区标题
  static readonly SIZE_BODY: number = 14          // 正文
  static readonly SIZE_META: number = 11          // 元数据/说明文字
  static readonly SIZE_MINI: number = 10          // 极小标签

  // ── 字重 ──────────────────────────────────
  static readonly WEIGHT_BOLD: number = 700       // 粗体(标题)
  static readonly WEIGHT_SEMIBOLD: number = 600   // 半粗(强调)
  static readonly WEIGHT_MEDIUM: number = 500     // 中等(副标题)
  static readonly WEIGHT_REGULAR: number = 400    // 常规(正文)
}

4.2 字体令牌对照表

令牌名 字号值 字重值 典型用途
SIZE_DISPLAY 28vp BOLD (700) 首页欢迎语、数字大标
SIZE_TITLE 20vp BOLD (700) 页面顶部标题
SIZE_SECTION 16vp SEMIBOLD (600) 列表分区标题
SIZE_BODY 14vp MEDIUM (500) 卡片主要文字
SIZE_META 11vp REGULAR (400) 时间戳、卡号掩码
SIZE_MINI 10vp REGULAR (400) 分类标签、徽标文字

4.3 在 UI 中使用字体令牌

import { AppFonts } from '../common/theme/AppFonts'
import { AppColors } from '../common/theme/AppColors'

// 卡号管理页 — 卡号数字显示
Text('138****8888')
  .fontSize(AppFonts.SIZE_SECTION)     // 16vp
  .fontWeight(AppFonts.WEIGHT_BOLD)    // 700
  .fontColor(AppColors.TEXT)

// 应用列表行 — 应用名
Text(appName)
  .fontSize(AppFonts.SIZE_BODY)        // 14vp
  .fontWeight(AppFonts.WEIGHT_MEDIUM)  // 500
  .fontColor(AppColors.TEXT)

// 说明文字
Text('使用该号码注册的应用')
  .fontSize(AppFonts.SIZE_META)        // 11vp
  .fontColor(AppColors.TEXT_2)         // 50% 透明度

五、AppAnimations — 动画设计令牌

5.1 动画令牌定义

// AppAnimations.ets
import { curves } from '@kit.ArkUI'

export class AppAnimations {
  // 标准过渡动画(按钮点击、状态切换)
  static readonly STANDARD = curves.cubicBezierCurve(0.4, 0, 0.2, 1)

  // 弹出动画(底部弹窗、卡片展开)
  static readonly SPRING = curves.springMotion(0.35, 0.75)

  // 时长(毫秒)
  static readonly DURATION_FAST: number = 150    // 快速切换(图标/颜色)
  static readonly DURATION_NORMAL: number = 300  // 标准过渡
  static readonly DURATION_SLOW: number = 500    // 复杂动画
}

5.2 在组件中使用动画令牌

import { AppAnimations } from '../common/theme/AppAnimations'

// 按下效果
.onClick(() => {
  animateTo({
    duration: AppAnimations.DURATION_FAST,
    curve: AppAnimations.STANDARD
  }, () => {
    this.selectedIndex = newIndex
  })
})

// 弹出卡片动画(例如:添加卡号时的预览头像弹出)
.animation({
  duration: AppAnimations.DURATION_NORMAL,
  curve: AppAnimations.SPRING
})

六、设计令牌最佳实践

6.1 命名规范

  1. 语义化:用 TEXT_2(副文字)而不是 GRAY_50(颜色本身)
  2. 层级清晰:主色 → 变体(PRIMARYPRIMARY_2PRIMARY_BG
  3. 功能分组:文字色、背景色、状态色、边框色分组命名

6.2 扩展方法

对于颜色枚举到 hex 的映射,使用独立方法而不是直接在令牌中硬编码:

// 在 AddAppPage.ets 中定义 CardColor → hex 的映射方法
private cardColorHex(c: CardColor): string {
  switch (c) {
    case 'blue':   return AppColors.PRIMARY      // '#4F7CFF'
    case 'purple': return AppColors.PRIMARY_2    // '#7C5CFF'
    case 'green':  return '#3CC463'
    case 'orange': return AppColors.WARN         // '#FF7A1E'
    case 'pink':   return AppColors.DANGER       // '#FF2E4D'
    default:       return AppColors.PRIMARY
  }
}

6.3 避免魔法数字

// ❌ 魔法数字:14 是什么?为什么是 14?
Text(appName).fontSize(14)

// ✅ 使用令牌:一看就知道这是"正文"
Text(appName).fontSize(AppFonts.SIZE_BODY)

七、设计令牌与设计稿的对应关系

7.1 设计稿颜色变量映射

根据 design/index.html 设计稿,颜色变量与代码令牌的对应关系:

设计稿变量 代码令牌 十六进制值
--color-primary AppColors.PRIMARY #4F7CFF
--color-text AppColors.TEXT #14141E
--color-text-2 AppColors.TEXT_2 #14141E80
--card-bg AppColors.CARD #B8FFFFFF
--card-bg-2 AppColors.CARD_B #E6FFFFFF
--color-line AppColors.LINE #14141E14

7.2 字体规格对应

设计稿规格 代码令牌 典型应用
.card-num:15px Bold SIZE_SECTION + WEIGHT_BOLD 卡号管理页卡号数字
.body:14px Medium SIZE_BODY + WEIGHT_MEDIUM 应用列表 app 名
.meta:11px Regular SIZE_META + WEIGHT_REGULAR 运营商、时间戳

八、本篇小结

通过本篇,你掌握了号码助手设计令牌体系的全貌:

  1. 颜色令牌(AppColors):语义化命名,避免魔法颜色值散落全项目
  2. ArkUI 颜色格式#AARRGGBB(Alpha 在前!)这是开发者极易踩坑的地方
  3. 字体令牌(AppFonts):统一字号和字重,保证全应用排版一致性
  4. 动画令牌(AppAnimations):封装 cubicBezierCurvespringMotion,统一动画风格

最重要的记忆点:ArkUI 颜色是 #AARRGGBB,不是 CSS 的 #RRGGBBAA

下一篇将介绍 应用配置详解app.json5module.json5 和路由表 main_pages.json 的每个字段含义。


九、参考资料

本系列相关文章:

官方文档:

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐