
我们面对的不是一张封面,而是不断变化的输入。
封面的色相、明暗、饱和度都无法预设,但播放页仍要同时满足四个稳定目标。
核心矛盾只有一句话:输入不可控,但体验输出必须稳定。
线上问题
固定参数能做出好案例,
也会制造新问题。
我们先不谈解法,只看线上结果:失败主要集中在四种类型。




从问题转向方法
设计稿是卖家秀,真实封面才是买家秀。
四种失败看似都能靠手动微调缓解,但设计稿可以精选封面,开发必须面对全量输入。修好当前案例,不等于规则已经稳定。
- 难还原:静态设计稿无法完整表达取色、渐变和动效逻辑。
- 难覆盖:修好一张封面,可能又制造一个新的 Bad case。
- 难沟通:“柔和一点”和“不要太脏”不是可执行参数。


AI 的第一步不是选颜色。是把问题拆成一条链路。
后面的每次迭代都沿用同一张地图:先找到失效环节,再只替换一个错误假设。
Stage 01 · 建立基线
固定分类,
在常规封面上确实有效。
先从最小方案开始:用 HSV 亮度(0–1)把封面分成“亮”和“暗”,再套用两组固定文字参数。




先承认它有效:在典型输入上,这套规则简单、快速,也能得到可接受的结果。
Stage 01 · 暴露边界
问题不是不能工作,
而是不能稳定工作。
一旦进入阈值边缘,或者文字真正落下的背景与原始封面不同,两组固定参数就无法继续保证对比度。
- 阈值问题:背景连续变化,文字结果却会在 0.8 附近突然翻转。
- 落点问题:算法判断的是封面,文字面对的却是处理后的最终渐变。
推导下一阶段:不再继续微调两组文字参数,先用更接近人眼感受的方式测准最终背景,再决定如何控制背景和计算文字。


Stage 02 · 测准亮暗
数值一样亮,人眼不一定觉得一样亮。
承接上一阶段:固定两档失效,首先因为 HSV/HSB 的数值亮度不能代表人眼真正看到的明暗;在调整背景之前,要先把判断依据测准。
简单的 HSV/HSB 亮度无法反映人眼对不同颜色的感知差异。绿、红、蓝即使 B 值相同,主观亮度也不同;这里改用 0–1 的 relative luminance 作为判断依据。
- 改用 relative luminance 判断背景的真实视觉明暗。
- 文字色选择不再只依赖一个简单亮度阈值。
- 同样的对比规则,在不同色相上更符合人眼感受。
本轮结果:明暗判断更接近视觉感受;但高饱和、高亮背景本身仍可能过度刺激,下一步先把背景收进安全范围。


Stage 03 · 控制背景
保留封面情绪,但不原样放大刺激。
承接上一阶段:感知亮度已经把明暗测准,但高饱和、高亮背景仍可能抢走内容注意力;因此再把最终背景收进安全范围。
直接取色会让某些高饱和封面把页面推向过曝。这一版先把背景色压回到播放页可控的视觉范围。
这一步解决的不是“颜色不够像封面”,而是“封面颜色不能无损放大到整个界面”。
本轮结果:明暗判断更可靠,背景也进入安全范围;但极低饱和和暗部噪点仍可能被放大,下一步先保护输入边界。


Stage 04 · 保护边界
黑色里的一点蓝,不应该被放大成蓝色。
承接上一阶段:背景进入安全范围,不代表色相输入一定可靠;在建立更灵敏的动态规则前,必须先过滤中性色和暗部噪点。
中性色阈值收紧到 HSV S<2%,极低饱和和暗部噪点不再被判定为可靠色相;没有可靠色相时,直接进入灰阶保护逻辑。
本轮结果:输入边界已经稳定;接下来不再增加孤立参数,而是让 AI 探索两个固定答案之间的连续规则。


Stage 05 · 动态参数优势
固定参数只能覆盖两端,动态参数可以适配整个区间。
动态参数的核心优势:固定参数会让不同背景共享同一个结果,在阈值附近还会突然跳变;动态参数则根据最终背景,连续调整文字的亮度、饱和度和对比度。
背景只变化一点,文字也只变化一点;常规输入保持平滑,极端输入再由参数上下限、中性色保护和对比度检测兜底。
- 覆盖完整区间
从暗部到亮部都有对应值,不再只有深、浅两档。 - 过渡连续平滑
相近背景得到相近结果,切换时不会突然翻转。 - 极端输入可控
曲线设有上下限,对比不足时再反馈调整。
本轮结果:一条动态参数曲线覆盖暗、中暗、中亮、亮四类背景,同时保证视觉连续性和可读性。




验证闭环
不是参数变多了,
而是每类问题都有验证出口。
用同一批封面做优化前后对照:先看四类核心问题是否被对应规则解决,再看全量压测里还剩哪些边界。
演讲时这一页要给真实证据:没有数据就展示同源 A/B 和遗留案例,不用“已经稳定”替代验证结果。
体验层 · 底部魔法色动画
让颜色有呼吸感,但不能抢戏。
顺序很重要:先让静态规则稳定,再增加表现层;动效不是用来掩盖算法问题。
在规则稳定之后,再加入底部缓慢流动的多层色场,丰富播放页颜色层次和质感。
- 层次:暗、中、亮多层叠加,不是单个色块位移。
- 速度:缓慢漂移,动效是氛围,不是主信息。
- 边界:不影响歌词、进度和主操作的阅读。
沉淀规则
最终交付的不是八组参数,
而是一条可反馈的链路。
主演讲只需要记住这六步;具体阈值和函数属于开发附录,答疑时再展开。
开发参数明细(Step 01–08)
封面取色
- 封面缩放:统一缩到 128 × 128,降低计算量并稳定取样。
- 主色:全图取 dominant,先跳过过黑 / 过白像素。
- 顶部色:上半区取色,用于顶部背景和导航区过渡。
- 底部色:下 1/3 区域取色,用于播放器区域和底部流光。
- 透明像素:alpha < 180 直接丢弃。
色相可靠性
- 可靠色相:HSV 饱和度 ≥ 2% 才认为色相可靠。
- 暗噪点保护:亮度 <8 且 chroma <8,也视为色相不可靠。
- 中性色逻辑:色相不可靠时,背景饱和度归零,不强行染色。
主背景调校
- 主色进入 appleMusicTune():先让封面主色进入更适合播放页的视觉范围。
- 低饱和色:可靠色相且饱和度 <10% 时,限制在 0–12%。
- 普通有彩色:s × 1.08,最终限制在 24–82%。
- 三层背景:base 控制主氛围,soft 提亮过渡,deep 保留暗部深度。
背景硬限制
- 处理对象:底部背景、底部混合色、主色都会经过 constrainBackgroundColor()。
- 饱和度上限:背景饱和度最高 72%,避免封面高刺激色原样铺开。
- 亮度上限:背景亮度初始最高 92%。
- 对比反馈:如果 UI 对比不够,亮度上限会从 92 一路降到最低 20。
底部流光
- 三层结构:基于底部混合色生成 dark、mid、light 三层流光,形成底部颜色层次。
- 色相偏移:dark = hue +18°,mid = hue +4°,light = hue -12°,代码中用 hue +348° 表达。
- 共享饱和度:max(bottomS × 0.85, fallbackS × 0.65, 24),最终限制在 24–72%。
8–48%
20–70%
52–88%
浅色背景自适应
- lightFactor:根据 perceivedLuminance 连续计算浅色背景系数。
- 浅色越强:底部流光整体更轻,暗层透明度降低,亮层更柔和。
- 模糊增强:浅背景下 darkBlur 和 lightBlur 都会增加,避免硬边和脏块。
UI 填充色
- 计算输入:播放器 UI 色用底部代表色 bottomGradientRgb 计算,而不是直接看封面。
- 浅背景:相对亮度 ≥0.45 时,UI 饱和度 = backgroundS ×1.24,限制 22–94%。
- 浅背景亮度:UI 亮度 = 55 - backgroundS ×0.08,限制 45–53%。
- 深背景:UI 饱和度固定 10%,亮度 100%,基本是白色系。
- 中性背景:背景和 fallback 都没可靠色相时,UI 饱和度为 0;浅中性背景 UI 亮度为 23%。
最终兜底
- 第一轮:先用 backgroundBrightnessMax = 92 生成整套底部 palette。
- 检测:计算底部代表色与 UI 色对比度。
- 反馈:正常目标是对比 ≥3:1;如果极端输入仍低于 2:1,就让 backgroundBrightnessMax -= 1,并重新生成底部背景、流光和 UI 色。
- 停止条件:优先达到 3:1;极端输入至少守住 2:1,或在亮度上限降到 20 时停止并记录为遗留案例。
规则闭环 · 交互验证
切换封面,验证最终效果。
选择预置封面或上传本地图片,查看背景、文字与控件的联动结果。
从“调好一张图”,到“让每一张图都尽量好看”。
播放页魔法色优化的过程,是从调颜色走向建规则的过程。真正的交付不是某一张最好看的图,而是一条面对未知输入仍能测量、反馈和兜底的链路。