Files
2026-02-05 16:06:49 +08:00

9.0 KiB
Raw Permalink Blame History

「随心」主题Suixin Theme技术计划plan

0. 目标回顾

在 Home 现有「风景 / 纯色」主题基础上新增第三种主题「随心」:

  • 输入:问卷生成的用户画像 U(本地存储)
  • 输出Home 背景推荐颜色(以纯色为主)
  • 规则:复用「个性化背景颜色推荐算法」的 Base Theme/Neutral Theme 与 Hard Rules
  • 计算时机
    • 冷启动App 进程级):计算一次并锁定 Base Theme
    • 切换文案Home 上滑切下一条):在同一 Base Theme 内更新一次“当前颜色”
  • 持久化:主题选择与「随心」计算状态均持久化(避免回到 Home/重进页面时丢失)
  • 多语言TCzh-TW+ EN

1. 现状梳理(与改动点)

1.1 现有主题切换

  • ThemeMode 当前为 'scenery' | 'color'
  • ThemeModal 弹窗提供 2 个卡片切换
  • Home 根据 themeMode
    • scenery:背景图 + 默认底色
    • color:从 THEME_COLORSindex 轮换纯色
  • ui.theme.mode 已在 AsyncStorage 持久化

1.2 用户画像输入已就绪

客户端已将问卷映射为 UserProfileV1_2(_Extended) 并持久化(user.profileScoring),关键字段:

  • stage.unknownHard Ruleunknown → Neutral
  • need(稀疏 one-hot{ [needTag]: 1 }{}
  • emotion_score: number | null
  • profile_confidence: number
  • profile_answered

2. 技术方案总览

2.1 「随心」算法在 Home 的落地形态

Home 不具备“长文案阅读页的 scroll”因此采用**“渐变单点采样”**来复用算法的连续插值模型:

  • Base Theme 仍按 need / stage 选定并锁定Theme Lock
  • 每次切换文案时,生成一个 (t \in [0, 1]),并计算:

[ Color(t) = lerp(Color_top, Color_bottom, t) ]

  • 输出为单一 hex 纯色,作为 Home backgroundColor
  • 全程 不跨 need、不跨主题色系,仅在同主题内移动

2.2 持久化与“只在冷启动/切换文案时计算”

为同时满足“持久化”与“冷启动时计算”:

  • 持久化内容:锁定的 base_theme_id + 用于生成 (t) 的 seed + 当前 step_index + last_color
  • 冷启动计算:当检测到“新一轮 App 启动会话”时,重新选择并锁定 base_theme_id,并重置/更新 seedstep_index
  • 切换文案计算:仅递增 step_index,在同一 base_theme_id 下更新 last_color

说明:冷启动检测以“进程级首次进入 Home”为准工程实现阶段会在 _layout 或全局单例中生成 boot 标记)。

3. 数据结构与存储设计

3.1 扩展主题枚举

  • ThemeMode 扩展为:'scenery' | 'color' | 'suixin'
  • 存储 key沿用 ui.theme.mode

3.2 新增「随心」状态存储

新增本地存储 key建议

  • ui.theme.suixin.state

数据结构(建议):

type SuixinThemeStateV1 = {
  schema_version: 1;
  saved_at: string;            // ISO8601
  base_theme_id: 'neutral' | 'emotional_support' | 'parenting_pressure' | 'self_worth' | 'anxiety_relief' | 'rest_balance';
  seed: string;                // 用于生成 t 的稳定种子(可由 profile + 日期等派生)
  step_index: number;          // 每切换一条文案 +1
  last_color: string;          // "#RRGGBB"
};

3.3 冷启动会话标记Boot ID

为实现“仅冷启动时重置 base theme/seed”新增一个进程级 boot 标记(实现二选一):

  • 方案 A推荐:在 app/_layout.tsx 首次挂载时生成 boot_id 并写入内存单例(不落盘)
  • 方案 B:写入 AsyncStorage(例如 app.boot.lastSeenAt)并结合“本次运行内存标记”判定首次进入 Home

计划优先采用方案 A逻辑清晰且不污染存储。

4. 颜色算法实现细节Home 版本)

4.1 主题色盘常量

在客户端新增一个颜色模块(例如 client/src/features/suixinTheme/),内置:

  • Base Theme5 套)+ Neutral1 套)
  • 与设计文档保持一致的 hex

4.2 Base Theme 选择(锁定)

输入:UserProfileScoring

输出:base_theme_id

规则:

  • stage.unknown === 1neutral
  • need 为空 {}neutral
  • 否则取 Object.keys(need)[0]
    • 若 key 在枚举内 → 对应 Base Theme
    • 否则 → neutral

4.3 t 的生成与“低感知变化”

为了让“切换文案”带来“流动感”但不跳变,采用小步进策略:

  • 定义 N = 12(可调):表示从 (0 \to 1) 的分段数
  • 每次切换文案:step_index += 1
  • 计算:t = (step_index % N) / (N - 1)

该策略保证 (t) 在 ([0, 1]) 内缓慢移动;到达 1 后回到 0 会有一次跳变。为进一步降低跳变,可改为往返波形:

  • phase = step_index % (2*(N-1))
  • t = phase <= (N-1) ? phase/(N-1) : (2*(N-1)-phase)/(N-1)

实现阶段默认采用往返波形,避免回卷突跳。

4.4 lerp 计算(严格线性)

  • lerp 仅允许线性插值
  • 颜色空间:先使用 sRGB 的逐通道线性插值(实现简单、可控);若后续需要更自然,可升级到线性空间插值,但仍保持线性模型

4.5 emotion/confidence 的约束接入

本期按“安全优先”策略落地:

  • emotion_score === nullemotion_score <= 0.3:输出不做任何微扰(纯 lerp 结果)
  • 亮度微扰((\Delta L \le \pm 2%))与饱和度上限为可选增强;若落地,将以 profile_confidence 作为开关条件,并确保不改变色系

5. UI 与交互实现计划

5.1 ThemeModal新增第三个主题卡片

  • client/components/home/ThemeModal.tsx
    • ThemeMode 扩展为包含 'suixin'
    • 新增 ThemeCard:标题使用 i18nt('theme.suixin')
    • 布局改造:由 2 卡横排改为 3 卡自适应flexWrap 或减小 gap/宽度),确保小屏不溢出
    • 预览图:一期可复用 theme_color.png 作为占位;若有设计资源再替换为 theme_suixin.png

5.2 Home新增主题分支与颜色计算时机

client/app/(app)/home.tsx

  • themeMode === 'suixin' 作为第三分支:
    • 背景为纯色(backgroundColor = suixinColor
    • 不显示风景图
  • 冷启动Home 首次进入时,读取用户画像与 suixin.state
    • 若检测到新 boot 会话:重算并写入 suixin.state
    • 否则:直接使用持久化的 last_color
  • 切换文案:在现有 triggerNextContent 成功切换索引后:
    • 若当前主题为 suixin:递增 step_index,计算新的 last_color,并持久化

5.3 收藏Favorites背景记录兼容

FavoriteItem.background 当前对 colorhex,对 scenery 存图片索引。

  • suixin 同样存 hex,与 color 分支一致即可

6. i18n 计划TC / EN

client/src/i18n/locales/all.json 增加:

  • theme.suixin
  • (可选)theme.suixinDesc(若 UI 后续展示描述)

英文命名采用语义化方案(本计划建议):

  • ENtheme.suixin = "Ease"
  • TCtheme.suixin = "隨心"

若后续品牌希望保留音译,也可改为 EN=Suixin,不影响技术实现。

7. 兼容性与迁移

  • ThemeMode 的存储值新增 'suixin'
    • 旧版本只会存 'scenery'|'color',升级后兼容
    • 若读取到未知值,继续回退 'scenery'
  • 新增 ui.theme.suixin.state
    • 若不存在,首次进入随心主题时初始化

8. 测试计划(最小可回归)

8.1 单元测试(推荐)

为颜色算法模块增加用例(可放在 client/src/features/suixinTheme/__tests__/

  • stage.unknown=1 → 必选 neutral
  • need={} → 必选 neutral
  • need={rest_balance:1} → 选 rest_balance Base Theme
  • step_index 递增 → t 按往返波形变化且始终在 ([0,1])
  • emotion_score=null / <=0.3 → 不触发微扰逻辑

8.2 手动验收(与 spec 对齐)

  • ThemeModal 能看到第三个主题并可切换
  • 冷启动进入 Home随心背景根据画像选定主题色系
  • 上滑切换文案:背景色在同主题内缓慢变化(无跨主题跳色)
  • stage.unknown=1need 跳过:背景为 Neutral Theme
  • 切换语言:主题名称在 TC/EN 下正确显示

9. 风险与对策

  • 三卡布局拥挤:采用 flexWrap/缩小卡片尺寸,必要时改为横向滚动
  • “持久化”与“冷启动重算”矛盾:以“状态落盘 + 冷启动重置 base theme/seed”方式兼容两者
  • 颜色可读性风险:一期先用主题中间色/插值结果,避免过饱和;必要时增加对比度检查(后续迭代)

10. 里程碑拆分(实现顺序)

  • M1基础接入
    • 扩展 ThemeModeThemeModal 增加第三项与 i18n
    • Home 增加 suixin 分支,背景可显示(先用 neutral 兜底)
  • M2算法落地 + 持久化
    • 新增 suixin 颜色模块Base/Neutral、pickTheme、lerp、t 生成)
    • 新增 suixin.state 存取与冷启动/切换文案更新
  • M3回归与体验优化
    • 收藏背景记录兼容
    • 测试补齐与边界修正unknown/跳过/缺画像)