下拉框与网页端对齐(照 FeedLite 那套观感)(#86)

用户 2026-11-05:「好了,鸿蒙端也用类似的方式调,效果让它保持一致。」

端上**不需要像网页那样手写浮层** —— 网页端必须自绘是因为
「原生 select 的展开列表由浏览器绘制,CSS 改不动」;
ArkUI 的 Select 本身就是自绘的、选项列表也归框架管。
⇒ 把对应属性都设上就等价了:

  网页端                          ArkUI Select
  ──────────────────────────────────────────────────────
  按钮 40 高 / 圆角 / 细边框       height(40) + borderRadius + border
  箭头打开转 180°                 框架内置(自带动画)
  悬停 tint-1                     不支持(触屏没有 hover)→ 用打开态代替
  选中项底 tint-2                 selectedOptionBgColor(fbMixWhite(accent,0.80))
  选中项文字 主题色                selectedOptionFontColor(accent)
  选中项右侧对勾                   showDefaultSelectedIcon(true)
  浮层白底 + 细边框               menuBackgroundColor(White) + border
  浮层与按钮同宽                   optionWidth(OptionWidthMode.FIT_TRIGGER)
  未选值次要色                     fontColor(有值 ? accent : Faint)

① Theme 新增 fbMixWhite(hex, ratio)
   与网页端 fb_color_tint() **同算法同比例**(混 91/80/72% 白)。
   ⚠️ 为什么不用现成的 fbAlpha():那是「原色 + 透明度」,
      会透出父容器底色 —— 下拉浮层背景不确定,那样会时深时浅;
      这里是实色混合,颜色确定。
   ⚠️ 实测 16 组(4 个主题色 × 4 档比例)与 PHP 输出逐个比对,全部一致。

② 边框单独用一档
   按钮边框 fbMixWhite(accent, 0.62) —— 比 tint-3 再深一点,
   否则在 tint-1 的浅底上几乎看不见边界。

③ 修一个类型坑
   本来写了个 pickFontColor(): string 返回 accent / FbColors.muted,
   但 FbColors.muted 是 Resource($r(...)),返回 string | Resource
   会让 fontColor() 报「Type 'string | Resource' is not assignable to 'string'」。
   ⇒ 改成内联三元,且未选用 FbColors.faint(那个是 string 常量)。

编译通过,已装真机。
This commit is contained in:
2026-11-05 20:25:59 +08:00
parent 389cb31df4
commit d9ca9dbe31
2 changed files with 97 additions and 18 deletions
+55
View File
@@ -386,6 +386,61 @@ function fbIconOf(name: string): Resource {
* @param hex 六位十六进制(`#RRGGBB`;也容忍八位,会先截成前六位)
* @param alpha 0~1(0.4 = 40% 不透明)
*/
/**
* 把主题色往白色方向混,得到一档浅色。
*
* 🔴 #86(用户 2026-11-05):「鸿蒙端也用类似的方式调,效果让它保持一致。」
*
* 与网页端 `fb_color_tint()`(app/helpers.php)**必须是同一个算法**,
* 否则同一条任务在两端的下拉项颜色不一样 —— 那就白"保持一致"了。
*
* 网页端的三档:
* tint-1(混 91% 白)→ 悬停 / 打开态底
* tint-2(混 80% 白)→ 选中项底
* tint-3(混 72% 白)→ 选中项悬停
*
* ⚠️ 与 `fbAlpha()` 的区别:那个是"原色 + 透明度"(透出背景色,会受
* 父容器底色影响);这个是"实色混合"(不透明,颜色确定)。
* 下拉项底色必须用实色 —— 用透明度的话叠在白色面板上还行,
* 但它自己是浮层,背景不确定,会时深时浅。
*
* @param hex 六位十六进制(`#RRGGBB`)
* @param ratio 往白色混多少(0.91 = 91% 白 + 9% 原色)
*/
export function fbMixWhite(hex: string, ratio: number): string {
let base: string = hex.trim();
if (base.startsWith('#')) {
base = base.substring(1);
}
if (base.length > 6) {
base = base.substring(base.length - 6);
}
if (base.length !== 6) {
return hex; // 不是 6 位 hex(如 rgba())→ 原样返回,调用方自己兜底
}
const r: number = parseInt(base.substring(0, 2), 16);
const g: number = parseInt(base.substring(2, 4), 16);
const b: number = parseInt(base.substring(4, 6), 16);
if (isNaN(r) || isNaN(g) || isNaN(b)) {
return hex;
}
let q: number = ratio;
if (q < 0) {
q = 0;
}
if (q > 1) {
q = 1;
}
const nr: number = Math.round(r + (255 - r) * q);
const ng: number = Math.round(g + (255 - g) * q);
const nb: number = Math.round(b + (255 - b) * q);
const h2 = (v: number): string => {
const s2: string = v.toString(16).toUpperCase();
return s2.length < 2 ? '0' + s2 : s2;
};
return '#' + h2(nr) + h2(ng) + h2(nb);
}
export function fbAlpha(hex: string, alpha: number): string {
let base: string = hex.trim();
if (base.startsWith('#')) {