跨端: 玻璃走基础组件(透明度/磨砂/投影)+ 判据去芜存菁(修 4 个判据自身缺陷)

用户两条:「玻璃不透明/不够磨砂炫酷,要更基础组件化」+「判据去掉无意义的、保留有用的」。

★ 玻璃的透明度与质感(逐层调出来的,不是拍的)
  · 透明度:`BlurStyle` 是固定档、没有 saturate 旋钮 ⇒ 观感偏灰。
    改用参数化 `backgroundEffect({ radius, saturation })`,字段与 CSS `backdrop-filter`
    一一对应,于是可以**照抄 WebUI 的数**:`blur(18px) saturate(1.5)`
    (WebUI `.narrow-nav`,那里最"玻璃"的一档)。**saturate 才是玻璃感的主要来源**。
  · 质感:只模糊+饱和仍然"像一块平的白方块" —— 玻璃板之所以像板靠的是**边缘**。
    WebUI 的完整配方是 `box-shadow: 0 8px 28px rgb(0 0 0 / 0.16)` + 发丝描边。
    只有模糊没有投影 ⇒ 一片雾;加上投影 ⇒ 一块板。**这是"不够炫酷"的主因**
    (我先前两个方向都调了模糊与饱和,方向就偏了)。
  · 投影用**系统预置档** `ShadowStyle.OUTER_FLOATING_SM`("浮起的小面板"),
    不是手写 `{radius, color, offsetY}` —— 手写色要么是 `#AARRGGBB`、
    要么是 `rgba()`,两者都被判据明令禁止,而那条禁令**是对的**:
    手写阴影色在深色下看不见(深底叠深影 = 无变化),WebUI 在 `.dark` 里
    要把同一处从 0.16 加重到 0.55 —— 那正是"替系统猜深浅"。

★ 基础组件(用户:「定义一个基础玻璃容器给各个组件引用」)
  · `GlassCardModifier` / `PaneModifier`(`AttributeModifier<CommonAttribute>`):
    **一处定义、处处引用**。页面里的玻璃从 15 处内联三连收敛成
    `.attributeModifier(...of(this.bgActive))`。
    ★ 为什么不是 `@Styles`:全局 `@Styles` 读不到 `this`(而玻璃要知道 `bgActive`);
      成员级 `@Styles` 能读 `this` 但**每个 struct 各一份**,MainPage 有 7 个 struct
      ⇒ 只是把"每处抄 3 行"换成"每 struct 抄 1 份",没解决问题。
  · `Motion.dur()`:接系统「减弱动态效果」(`isAnimationReduceEnabledSync()`,API 23)。
    WebUI 有 `prefers-reduced-motion` 硬约束,鸿蒙这边**改造前一次都没调过**。

★ 途中撞出并修掉的**真 bug**(都是我自己的)
  · 把 `@Styles glassCard()` 里也替换成 `attributeModifier` ⇒
    「我的」页 ProfileSection/AppearanceSection/PushSection **整块不渲染**
    (设备实测:Scroll 直接跳到「客户端连接密钥」)。删掉那个死 `@Styles` 即恢复。
  · `Theme.shadowColor/hairline` 用了 `rgba()` + `#AARRGGBB` ⇒ 违反本仓两条既有禁令
    ("鸿蒙侧不写 CSS 颜色函数"、"半透明属于系统材质/职责")。改用系统阴影档。

★ 判据去芜存菁:修了 4 个**判据自身的缺陷**(都是它声称在管、实际漏判/误判)
  · 材质来源只扫 `backgroundBlurStyle` —— 玻璃改成 `backgroundEffect` 后**整类漏判**。
    实测:往 Modifier 里塞 `backgroundEffect({radius:99,saturation:9.9})` 照样全绿。
    ⇒ 补上这一类,并加"参数必须引 Theme 令牌"(否则只是换个旋钮写死)。
  · 参数抽取用 `[^)]*` —— 对 `this.materialOf()` 与 `{...}` 对象字面量都提前截断,
    **误伤合法写法**(本次被它误红两次)。改成配对括号 + 去外层花括号。
  · 嵌套判定**没比文件**:拿 A 文件的字符偏移去比 B 文件 ⇒ 判出
    "MainPage 内含 SettingsPage 的玻璃"这种物理上不可能的红。
    假红比漏报更坏 —— 会让人去改本来对的代码(本次差一点)。
  · `harmony-appearance` 数的是**内联字面量**("三元出现 ≥5 次"),
    收敛成组件后写法变了、行为没变却变红 ⇒ 改成判不变式
    (让位只有一条路径 + 每个窗格都收到开关)。
  · 顺带:设备判据找「背景」时只在首屏可见节点里找(而 dumpLayout 只给可见节点),
    且我的第一版只朝一个方向滑(越滑越远)⇒ 改成"先回顶、再逐屏向下扫"。

判据:files=32 ran=32 checks=518 pass=518 fail=0 red=0;baseline 7/7✓(第 7 次,逐个取证)。
This commit is contained in:
2026-09-20 08:16:10 +08:00
parent 5103e0aee3
commit eb4e413c67
8 changed files with 331 additions and 61 deletions

View File

@ -194,6 +194,59 @@ export class Theme {
*/
static readonly cardMaterial: BlurStyle = BlurStyle.BACKGROUND_THIN;
/*
* ── 玻璃的**参数化**配方(比材质档更接近 WebUI)──
*
* `BlurStyle` 是**固定档**(系统给的那几组),而 WebUI 的玻璃是**显式参数**:
*
* .narrow-nav backdrop-filter: blur(18px) saturate(1.5) ← 浮条
* --bg-blur-panel: 10px ← 面板
*
* 关键差别是 **saturate(饱和度增强)**:它让透过玻璃的颜色更鲜艳,
* 正是"玻璃感"的主要来源。`BlurStyle` 没有这个旋钮,
* 所以纯用材质档会偏灰 —— 用户说"不够炫酷"就是这个观感。
*
* ArkUI 的参数化 API 是 `backgroundEffect({ radius, saturation, brightness })`,
* 字段与 CSS 的 `backdrop-filter` 一一对应,于是可以**照抄 WebUI 的数**。
* 取值规则(不是拍的):
* · `CARD_RADIUS = 18` / `CARD_SATURATION = 1.5` —— 与 WebUI `.narrow-nav`
* 逐字同源(那是 WebUI 里最"玻璃"的一档)。卡片面积小,用最有质感的档;
* · 面板面积大、透太多会伤正文可读性 ⇒ 用 `--bg-blur-panel: 10px`,
* 饱和度取 1.3(比 1.5 收一点 —— 大面积高饱和会让整页发艳)。
*
* ★ 为什么不直接抄 1.5 给面板:WebUI 那边这两个值本来就是**分开的**
* (10px 面板 / 18px 浮条),抄就得抄全套,不能只挑一个数。
*/
static readonly glassCardRadius: number = 18;
static readonly glassCardSaturation: number = 1.5;
/**
* 「悬浮玻璃板」的另外两半:**投影 + 发丝描边**。
*
* 只做模糊+饱和仍然"像一块平的白方块" —— 玻璃板之所以像板,靠的是**边缘**:
* 它要看起来**浮在壁纸之上**。WebUI 的配方(`.app-shell > *`):
*
* box-shadow: 0 8px 28px rgb(0 0 0 / 0.16);
*
* 只有模糊没有投影 ⇒ 一片雾;加上投影 ⇒ 一块板。
* **这是本次"不够炫酷"的主因**(我先前只调了模糊与饱和度,方向就偏了)。
*
* ★ 用**系统预置阴影档** `ShadowStyle.OUTER_FLOATING_SM`("浮起的小面板"),
* 而不是手写 `{radius, color, offsetY}`。三个理由:
* ① 手写色是 `#AARRGGBB` 或 `rgba(...)` —— 两者都被判据明令禁止
* (B 条:"半透明属于系统材质/语义色的职责"、"鸿蒙侧不写 CSS 颜色函数"),
* 而这条禁令**是对的**:见 ②;
* ② 手写色在**深色下看不见**(深底叠深影 = 无变化),而 WebUI 在 `.dark` 里
* 要把同一处阴影从 0.16 加重到 0.55 才有效 —— 那是"替系统猜深浅"的典型;
* ③ 系统档自带深/浅两套取值,与 `radiusCard` 走 `sys.float.*` 是同一个道理。
*
* 发丝描边同理不再手写:`Theme.border`(系统分隔线色)已经在用,
* 深浅两套由系统给。
*/
static readonly glassShadow: ShadowStyle = ShadowStyle.OUTER_FLOATING_SM;
// ─────────────── 圆角:系统尺寸 ───────────────
/** 卡片圆角:系统"卡片"圆角(不再与 WebUI 的 14vp 绑死 —— 允许各自跟随系统) */