鸿蒙|动画对齐 all_five 落地 + 收敛 pageEnter 死方法

用户裁定「动画对齐 = 五条逐个对照」(all_five, 2026-09-23)。
本轮把五条核对完并把结果**沉淀成常驻判据**(本仓做法:审计不落判据就会漂)。

核对结果(WebUI 侧剥注释后实测,不是靠记忆):
  rise-in      定义✓ 使用✓  → Theme.paneRiseIn() ×6 + Motion.pageEnter() ×3
  pane-in      定义✗ 使用✗  → 9aa702b 删掉的死规则(碑文 index.css:1239)
                              用户 09-14 明确否掉「整屏一起淡」,两侧都不得复活
  menu-in      定义✓ 使用✓  → Theme.menuIn() ×2
  cal-in-next  定义✓ 使用✓  → Theme.calendarSlide(true)
  cal-in-prev  定义✓ 使用✓  → Theme.calendarSlide(false)
⇒ 五条全部落地;无缺口需要新写动画(避免了"自己发明一条")。

顺带修一处真问题:
  Motion.pageEnter() 定义了、返回的正是三个 @Entry 页各自手写的
  那份 {duration, curve} 字面量,却**一个调用点都没有**(死方法)。
  三个页面(AdminUsersPage/ComposePage/MailDetailPage)改为走它。
  这正是本仓反复出现的形状:一处定义 + 三处同源各自演化。

判据(animation-audit.test.mjs 12 → 15):
  ① WebUI 每个**在用**的 keyframe 都有鸿蒙实现,且实现**真的有人穿**
     —— 名单从 CSS 动态取(liveKeyframes),不是写死,将来加删会跟着动。
     「有调用点」是关键:本仓的失败形状是"定义了却没人用"(menuIn 一度如此),
     "方法存在"是弱断言 —— 删掉最后一个调用点它照样绿。
  ② pane-in 不得在**任一侧**复活(只查鸿蒙不够,WebUI 复活同样会两端不一致)。
     负面断言走剥注释版,碑文里的引用不会让它假红。
  ③ 一般化:**任何**返回 TransitionEffect 的动画辅助方法都必须有调用点
     —— 防的是"下一条",pageEnter 就是这里被抓出来的。

变异验证(三条新判据逐条打红,还原后回绿):
  · 删掉 calendarSlide 调用点 → ①③ 红(cal-in-next/prev 无调用点)
  · 鸿蒙复活 paneIn() → ② 红
  · WebUI 复活 @keyframes pane-in + 使用 → ①② 红
  ⇒ 判据有齿,不是橡皮图章。

套件:animation-audit 15/15、harmony-logic 34/34、harmony-nav 22/22、
      harmony-appearance 27/27。hvigorw BUILD SUCCESSFUL。
This commit is contained in:
2026-09-24 11:02:05 +08:00
parent 65de1c3884
commit 7151d3f91c
4 changed files with 100 additions and 12 deletions

View File

@ -331,13 +331,93 @@ check(
);
/*
* ★★ 2026-09-21 删除两条陈旧断言:
* · 「页面里不再直接出现 `Theme.durMorph`」
* · 「`Theme.durMorph` 确实存在且 = 220」
* ════════════════════════════════════════════════════════════════
* 鸿蒙侧:五个 `@keyframes` 的**逐条对照**(用户裁定 `all_five`,2026-09-23)
* ════════════════════════════════════════════════════════════════
*
* 令牌**已被删除**(morph 改用 `responsiveSpringMotion`,弹簧曲线下
* duration 不生效,保留那是误导)⇒ 这两条现在守的是一个不存在的东西。
* 上面那条改为钉**曲线令牌**的"不得下沉到页面",与原来的意图同一件事。
* 用户裁定:动画对齐 = **五条逐个对照补齐**,不是"整体观感差不多"。
* 盘点( 2026-09-24)结果 —— WebUI 侧五个 keyframe 的**实况**(剥注释后实测):
*
* rise-in 定义✓ 使用✓ → 鸿蒙 `Theme.paneRiseIn()` / `Motion.pageEnter()`
* pane-in 定义✗ 使用✗ → 9aa702b 删掉的**死规则**(碑文 index.css:1239)
* 用户 09-14 明确否掉"整屏一起淡",两侧都不得复活
* menu-in 定义✓ 使用✓ → 鸿蒙 `Theme.menuIn()`
* cal-in-next 定义✓ 使用✓ → 鸿蒙 `Theme.calendarSlide(true)`
* cal-in-prev 定义✓ 使用✓ → 鸿蒙 `Theme.calendarSlide(false)`
*
* ★ 为什么判据要盯"**有调用点**",而不只是"方法存在":
* 本仓反复出现的失败形状是**定义了却没人用** —— 盘点当场抓到两个:
* · `Motion.pageEnter()` 定义了、返回的正是三个 `@Entry` 页各自手写的
* 那份字面量,却**一个调用点都没有**(改前实测);
* · `Theme.menuIn()` 一度"只剩定义、没有调用点"(账号选择器换成官方
* `bindMenu` 后),当时靠人肉发现"候选列表还没挂"才没变成死方法。
* "方法存在"是弱断言:删掉最后一个调用点它照样绿 —— 而那正是死动画。
*
* ★ 对照表用 **WebUI 的 CSS 动态取**(`liveKeyframes`),不是抄一份名单:
* 将来谁在 WebUI 加/删一个 keyframe,这里会跟着动;写死名单就会漂。
*/
const liveKeyframes = keyframes.filter(
k => new RegExp(`animation:\\s*${k}\\b`).test(css)
);
/* WebUI keyframe → 鸿蒙实现方法(可多个备选,任一满足即可) */
const KMAP = {
'rise-in': ['paneRiseIn', 'pageEnter'],
'menu-in': ['menuIn'],
'cal-in-next': ['calendarSlide'],
'cal-in-prev': ['calendarSlide']
};
const harmonyPageSrc = hs.map(h => h.src).join('\n');
const harmonyAnimSrc = themeSrc + '\n' + MOTION;
const unmapped = [];
for (const k of liveKeyframes) {
const impls = KMAP[k];
if (!impls) {
unmapped.push(`${k}(WebUI 在用,但鸿蒙没有对照)`);
continue;
}
const defined = impls.some(m => new RegExp(`static\\s+${m}\\(`).test(harmonyAnimSrc));
const consumed = impls.some(m => new RegExp(`\\.${m}\\(`).test(harmonyPageSrc));
if (!defined) unmapped.push(`${k}→${impls.join('/')}(未定义)`);
else if (!consumed) unmapped.push(`${k}→${impls.join('/')}(**无调用点** = 死动画)`);
}
check(
'鸿蒙|WebUI 每个**在用**的 `@keyframes` 都有鸿蒙实现,且实现**真的有人穿**',
unmapped.length === 0,
'这些 keyframe 在鸿蒙侧没有落地或落地了没人用(看着有、永远不播):' + unmapped.join(';') +
'\n用户裁定 `all_five`:五条逐个对照,不是"整体观感差不多"'
);
/*
* ★ `pane-in` 的负面断言:它是**两边都不得复活**的那一条。
* 只查"鸿蒙有没有"不够 —— WebUI 侧复活了同样会让两端不一致。
* (`css` 是剥注释版,所以碑文里的引用不会让它假红。)
*/
check(
'鸿蒙|已删除的 `pane-in` 不得在任一侧复活(用户 09-14 否掉"整屏一起淡")',
!/@keyframes\s+pane-in/.test(css) && !/paneIn\s*\(/.test(harmonyAnimSrc),
'`pane-in` 是 9aa702b 删掉的**死规则**(碑文 index.css:1239):它让"已经在那儿的框架"'
+ '也一起暗一下,用户明确否掉过。复活它 = 把"闪"请回来'
);
/*
* ★ 同类失败形状的一般化:**任何**返回 `TransitionEffect` 的动画辅助方法
* 都必须有调用点。上面是用户点名的五条,这一条防的是**下一条**。
* (`Motion.pageEnter()` 就是这里被抓出来的:定义了、没人用。)
*/
const animHelpers = [
...themeSrc.matchAll(/static\s+([A-Za-z][A-Za-z0-9]*)\([^)]*\)\s*:\s*TransitionEffect/g)
].map(m => m[1]);
const deadHelpers = animHelpers.filter(
m => !new RegExp(`\\.${m}\\(`).test(harmonyPageSrc)
);
check(
'鸿蒙|不得有"定义了却没调用点"的动画辅助方法(死动画的固定来源)',
deadHelpers.length === 0,
'这些动画方法定义了但全仓没有调用点(删掉最后一个调用点它就静默失效):' +
deadHelpers.join(' ')
);
finish('动画盘点');

View File

@ -299,10 +299,16 @@ struct AdminUsersPage {
* ★ 退场只做**淡出、不做位移**:两端都位移会看起来互相推挤。
*/
pageTransition() {
PageTransitionEnter({ duration: Motion.dur(Theme.durRise), curve: Theme.easeRise })
/*
* ★ 2026-09-24:改走 `Motion.pageEnter()` 统一入口。
* 原先三个 `@Entry` 页各自手写同一份 `{duration, curve}` 字面量,
* 而 `Motion.pageEnter()` 返回的**正是**这段字面量、却一直**没有调用点**
* (定义了没人用的死方法)—— 两处同源各自演化正是本仓反复出现的形状。
*/
PageTransitionEnter(Motion.pageEnter())
.opacity(0)
.translate({ y: Theme.riseInOffset })
PageTransitionExit({ duration: Motion.dur(Theme.durRise), curve: Theme.easeRise })
PageTransitionExit(Motion.pageEnter())
.opacity(0)
}

View File

@ -950,10 +950,11 @@ struct ComposePage {
* ★ 退场只做**淡出、不做位移**:两端都位移会看起来互相推挤。
*/
pageTransition() {
PageTransitionEnter({ duration: Motion.dur(Theme.durRise), curve: Theme.easeRise })
/* 走统一入口(三个 `@Entry` 页原先各自手写同一份时长/曲线,见 `Motion.pageEnter`)*/
PageTransitionEnter(Motion.pageEnter())
.opacity(0)
.translate({ y: Theme.riseInOffset })
PageTransitionExit({ duration: Motion.dur(Theme.durRise), curve: Theme.easeRise })
PageTransitionExit(Motion.pageEnter())
.opacity(0)
}

View File

@ -2017,10 +2017,11 @@ struct MailDetailPage {
* ★ 退场只做**淡出、不做位移**:两端都位移会看起来互相推挤。
*/
pageTransition() {
PageTransitionEnter({ duration: Motion.dur(Theme.durRise), curve: Theme.easeRise })
/* 走统一入口(三个 `@Entry` 页原先各自手写同一份时长/曲线,见 `Motion.pageEnter`)*/
PageTransitionEnter(Motion.pageEnter())
.opacity(0)
.translate({ y: Theme.riseInOffset })
PageTransitionExit({ duration: Motion.dur(Theme.durRise), curve: Theme.easeRise })
PageTransitionExit(Motion.pageEnter())
.opacity(0)
}