跨端: 抽出基础面/玻璃组件(Motion + Surface)+ 三处硬弹的弹层补过渡
用户两条要求,各自都指向"决策被复制到各处"这个病根:
① 「应该定义一个基础玻璃容器给各个组件引用」
② 「很多页面还是不够精致……封装为基础组件供所有页面使用。基础组件包含动画」
★ 玻璃不透明的真根因(前几轮都没找到,这次是像素证据定的)
实测模拟器 3184×2232、壁纸 aurora + 压暗 37:
Navigation [229,140,3156,2204] ← 255,255,255(整块内容区)
y=2210(Navigation 之外那条缝) ← 226,225,235(壁纸清楚可见)
那条缝是决定性的:**壁纸层本身是好的**,白是因为上面盖了不透明的壳。
链路上一共三层,逐层修:
· `Navigation` 外壳:不设背景 ⇒ 系统默认不透明白,把里面已透明的窗格整片盖住;
· navBar 内容层(列表栏根容器):同样没有背景;
· **卡片**:铺的是 `Theme.surface`(实体系统色)。
修后:缝隙 203,213,228 / 卡片 191,204,220 —— 壁纸透得出来。
★ 新增基础组件(common/)
· `Surface.ets` —— `GlassPane`(正文面)/ `GlassCard`(嵌套面)/ `PageHeader` / `Pressable`
· `Motion.ets` —— `Motion.dur()`:接系统「减弱动态效果」
(`accessibility.isAnimationReduceEnabledSync()`,API 23 正好够用)。
WebUI 侧有 `prefers-reduced-motion` 硬约束(index.css:1266),
鸿蒙这边**改造前一次都没调过** —— 系统里关了动画,我们照样动。这是无障碍义务。
· `Theme.cardMaterial` —— 卡片材质令牌。不是拍脑袋选的:
`COMPONENT_THIN` 实测卡片 252,254,254(吃掉 94% 壁纸,就是用户看到的"白卡");
`BACKGROUND_THIN` → 185,190,201,壁纸透得出来且文字对比度仍够。
★ 判定形状按"玻璃从例外变成默认"重写(判据 C 条)
原来是"允许出现玻璃的位置"白名单,逐处登记。那个形状在"玻璃是少数例外"时成立,
但用户要的是**全部玻璃化** ⇒ 白名单退化成"把每处抄一遍",且挡不住新写的裸枚举。
改成**规则**:材质档次只能来自 `Theme.navMaterial` / `Theme.cardMaterial`
(或 `BlurStyle.NONE`),页面里出现裸 `BlurStyle.XXX` 即红;
辅助方法(`this.materialOf()`)体内也查,否则等于开了后门。两条变异都验证咬得住。
★ 判据 C 条自身的两个 bug(都被本次触发)
· 嵌套判定**没比文件**:`c.at`/`o.at` 是各文件自己的字符下标,直接比大小
于是判出"MainPage 内含 SettingsPage 的玻璃"这种物理上不可能的红。
假红比漏报更坏 —— 会让人去改本来对的代码(本次差一点)。
· 参数抽取用 `[^)]*`:`this.materialOf()` 里含一层 `()`,在内层括号处截断,
把合法写法读成 `this.materialOf(` 判红。改成配对括号抽取。
★ 出现/消失动画:用户点名的三处硬弹
这些挂在 `if (cond)` 上,条件一变整块出现/消失,原来一帧过渡都没有 ——
而代码上"看着像挂了动画"(外层页面有 transition),所以最容易漏。
· `MainPage` 账号选择器下拉 → `menuIn()`(从上往下落的浮层档)
· `AdminUsersPage` 新建用户表单 → `paneRiseIn()`(就地展开档)
★ 过渡必须挂在**调用点的 Column**:`@Builder` 返回 void,链不上修饰符。
第一版写进了 `CreateForm()` 内部,是新判据抓出来的。
· `SettingsPage` 新增账号弹层 → `paneRiseIn()`(与回复/转发弹层同一挂法)
新增判据「出现/消失的那类元素真的挂了过渡」:**枚举所有条件挂载的浮层/展开块**
(不是数数 —— 数数挡不住"新增第四处又漏了"),变异验证过。
★ 顺手修的两处真嵌套玻璃
`SettingsPage` 的账号列表与密钥列表都是"卡里面的一段列表",两层都铺材质 =
WebUI 用 `.bg-white .bg-white { backdrop-filter: none }` 明确禁止的嵌套
(alpha 相乘 0.82×0.82=0.97 把壁纸吃光)。去掉内层材质,玻璃只留一层。
★ 判据放宽一处(不是放水)
`harmony-contacts` 那句写死 `pushUrl`,判的是"tryRestore 有没有跳转",
与用哪个原语无关。修 LoginPage 返回栈时改成 `replaceUrl` 后它误报了;
改成 `(pushUrl|replaceUrl)`,原语该用哪个由 `harmony-nav` 那条专门判。
判据:files=32 ran=32 checks=518 pass=518 fail=0 red=0;
baseline 7/7✓(第 6 次重算,两个文件逐个 `git diff --quiet HEAD` 取证为有意编辑)。
This commit is contained in:
@ -650,7 +650,16 @@ export struct SettingsPane {
|
||||
}, (account: AccountInfo) => account.id)
|
||||
}
|
||||
.width('100%').margin({ top: 10 })
|
||||
.backgroundColor(Theme.surface)
|
||||
/*
|
||||
* ★ 2026-09-19:这里**去掉**材质(原来加了,是错的)。
|
||||
*
|
||||
* 它是"个人资料"那张**卡里面**的一段列表 —— 即 `卡 > 列表`。
|
||||
* 两层都铺材质就是 WebUI 用后代选择器明确禁止的嵌套玻璃
|
||||
* (`.bg-white .bg-white { backdrop-filter: none }`):
|
||||
* alpha 相乘把壁纸吃光,而且每层各糊一次。
|
||||
* 判据 `cross-client-theme` 的 C 条正是按"玻璃不许套玻璃"判的,它抓到了这处。
|
||||
* 内容仍靠外层那张卡的材质透出玻璃感。
|
||||
*/
|
||||
.borderRadius(Theme.radiusCard)
|
||||
.border({ width: 1, color: Theme.border })
|
||||
.clip(true)
|
||||
@ -658,7 +667,8 @@ export struct SettingsPane {
|
||||
}
|
||||
.width('100%').alignItems(HorizontalAlign.Start)
|
||||
.padding(16).margin({ top: 8 })
|
||||
.backgroundColor(Theme.surface)
|
||||
.backgroundColor(this.bgActive ? Color.Transparent : Theme.surface)
|
||||
.backgroundBlurStyle(this.bgActive ? Theme.cardMaterial : BlurStyle.NONE)
|
||||
.borderRadius(Theme.radiusCard)
|
||||
|
||||
/* 外观(主题 / 壁纸) */
|
||||
@ -729,8 +739,19 @@ export struct SettingsPane {
|
||||
.width('100%')
|
||||
}
|
||||
.width('100%').height('68%')
|
||||
.padding(16).backgroundColor(Theme.surface)
|
||||
.padding(16).backgroundColor(this.bgActive ? Color.Transparent : Theme.surface)
|
||||
.backgroundBlurStyle(this.bgActive ? Theme.cardMaterial : BlurStyle.NONE)
|
||||
.borderRadius({ topLeft: 12, topRight: 12 })
|
||||
/*
|
||||
* ★ 2026-09-19 补(用户:「元素的出现消失动画呢?」)。
|
||||
*
|
||||
* 底部弹层挂在 `if (this.showAddDialog)` 上,原来硬弹。
|
||||
* 与 `MailDetailPage` 的回复框/转发框**同一档、同一挂法**:
|
||||
* `paneRiseIn()` 挂在**弹层本身**(下半部那块表面),遮罩立即出现。
|
||||
* 挂在遮罩上会让整个屏幕(含变暗的底层内容)一起位移 ——
|
||||
* 那看起来是"页面在动",而不是"弹层弹出来"。
|
||||
*/
|
||||
.transition(Theme.paneRiseIn())
|
||||
}
|
||||
.width('100%').height('100%')
|
||||
.position({ x: 0, y: 0 })
|
||||
@ -805,7 +826,8 @@ export struct SettingsPane {
|
||||
}
|
||||
.width('100%').alignItems(HorizontalAlign.Start)
|
||||
.padding(16).margin({ top: 10 })
|
||||
.backgroundColor(Theme.surface)
|
||||
.backgroundColor(this.bgActive ? Color.Transparent : Theme.surface)
|
||||
.backgroundBlurStyle(this.bgActive ? Theme.cardMaterial : BlurStyle.NONE)
|
||||
.borderRadius(Theme.radiusCard)
|
||||
}
|
||||
|
||||
@ -876,7 +898,8 @@ export struct SettingsPane {
|
||||
}
|
||||
.width('100%').alignItems(HorizontalAlign.Start)
|
||||
.padding(16).margin({ top: 8 })
|
||||
.backgroundColor(Theme.surface)
|
||||
.backgroundColor(this.bgActive ? Color.Transparent : Theme.surface)
|
||||
.backgroundBlurStyle(this.bgActive ? Theme.cardMaterial : BlurStyle.NONE)
|
||||
.borderRadius(Theme.radiusCard)
|
||||
}
|
||||
|
||||
@ -913,7 +936,8 @@ export struct SettingsPane {
|
||||
}
|
||||
.width('100%').alignItems(HorizontalAlign.Start)
|
||||
.padding(16).margin({ top: 8 })
|
||||
.backgroundColor(Theme.surface)
|
||||
.backgroundColor(this.bgActive ? Color.Transparent : Theme.surface)
|
||||
.backgroundBlurStyle(this.bgActive ? Theme.cardMaterial : BlurStyle.NONE)
|
||||
.borderRadius(Theme.radiusCard)
|
||||
}
|
||||
|
||||
@ -989,7 +1013,8 @@ export struct SettingsPane {
|
||||
}, (k: UserKey) => k.key_id)
|
||||
}
|
||||
.width('100%').margin({ top: 10 })
|
||||
.backgroundColor(Theme.surface)
|
||||
/* 同上(账号列表那处):这是"KeySection 卡**里面**的一段列表",
|
||||
两层都铺材质 = 嵌套玻璃(判据 C 条会红,且 alpha 相乘会把壁纸吃光)。 */
|
||||
.borderRadius(Theme.radiusCard)
|
||||
.border({ width: 1, color: Theme.border })
|
||||
.clip(true)
|
||||
@ -997,7 +1022,8 @@ export struct SettingsPane {
|
||||
}
|
||||
.width('100%').alignItems(HorizontalAlign.Start)
|
||||
.padding(16).margin({ top: 8 })
|
||||
.backgroundColor(Theme.surface)
|
||||
.backgroundColor(this.bgActive ? Color.Transparent : Theme.surface)
|
||||
.backgroundBlurStyle(this.bgActive ? Theme.cardMaterial : BlurStyle.NONE)
|
||||
.borderRadius(Theme.radiusCard)
|
||||
}
|
||||
|
||||
@ -1134,7 +1160,8 @@ export struct SettingsPane {
|
||||
}
|
||||
.width('100%').alignItems(HorizontalAlign.Start)
|
||||
.padding(16).margin({ top: 8 })
|
||||
.backgroundColor(Theme.surface)
|
||||
.backgroundColor(this.bgActive ? Color.Transparent : Theme.surface)
|
||||
.backgroundBlurStyle(this.bgActive ? Theme.cardMaterial : BlurStyle.NONE)
|
||||
.borderRadius(Theme.radiusCard)
|
||||
}
|
||||
|
||||
@ -1161,7 +1188,8 @@ export struct SettingsPane {
|
||||
}
|
||||
.width('100%').alignItems(HorizontalAlign.Start)
|
||||
.padding(16).margin({ top: 8 })
|
||||
.backgroundColor(Theme.surface)
|
||||
.backgroundColor(this.bgActive ? Color.Transparent : Theme.surface)
|
||||
.backgroundBlurStyle(this.bgActive ? Theme.cardMaterial : BlurStyle.NONE)
|
||||
.borderRadius(Theme.radiusCard)
|
||||
.onClick(() => {
|
||||
this.getUIContext().getRouter().pushUrl({ url: 'pages/AdminUsersPage' });
|
||||
|
||||
Reference in New Issue
Block a user