Files
jiapuapp/docs/superpowers/plans/2026-07-19-app-loading-essential-motion.md
T
2026-07-20 06:52:33 +08:00

161 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AppLoading 必要加载动效实施计划
> **执行要求:** 使用 `superpowers:executing-plans` 在当前会话内逐步执行。用户明确禁止多代理、worktree、`git add`、commit、push、reset 和 checkout。
**目标:** 修复 G01 在 `prefers-reduced-motion: reduce` 下加载态完全静止的问题,使印牌和如意结保留清晰的透明度呼吸,同时不产生缩放、旋转或位移。
**架构:** 继续由 `components/AppLoading.vue` 单一维护加载动效,不在 G01 复制样式。正常模式保持现有动画;减少动态效果模式改用独立的透明度关键帧,并强制 `transform: none`。测试先锁定旧行为必须失败,再验证静态契约和当前 Chrome 运行时证据。
**技术栈:** Vue 3、uni-app、SCSS、PowerShell 契约测试、Chrome DevTools Protocol、Node.js。
## 全局约束
- 只修改 G01 加载态直接依赖的公共 `AppLoading`、聚焦测试和当轮内部证据。
- 不改变 `variant``text``description` 组件契约。
- 不改变 G01 状态逻辑、布局、背景、文案、标题栏或底部导航。
- 不新增图片、CSS 图形、JavaScript 定时器或业务接口。
- `prefers-reduced-motion: reduce` 下只允许透明度变化,`transform` 必须为 `none`
- H5 截图只是内部候选;Android/HBuilderX 仍未验证。
- 不执行任何 Git 写操作。
---
### Task 1:修复并验证减少动态效果模式的必要加载反馈
**文件:**
- 修改:`tests/app-loading-contract.ps1`
- 修改:`components/AppLoading.vue`
- 验证:`tests/g01-loading-state-contract.ps1`
- 验证:`tests/g-series-app-loading-contract.ps1`
- 验证:`tests/module-app-loading-contract.ps1`
- 内部证据:`docs/design/screens/runtime/2026-07-19/g01-approval/03-loading-essential-motion-412x915.png`
**接口:**
- 消费:`AppLoading` 现有 `variant``text``description` 属性。
- 产出:正常模式继续使用 `app-loading-seal-breathe``app-loading-knot-breathe`;减少动态效果模式使用 `app-loading-seal-essential-pulse``app-loading-knot-essential-pulse`
- 不增加新的组件属性、事件或页面状态。
- [ ] **Step 1:先补充会失败的公共契约**
`tests/app-loading-contract.ps1` 的现有动画断言之后加入:
```powershell
Assert-Match -Content $component -Pattern '@keyframes\s+app-loading-seal-essential-pulse' -Message 'Reduced-motion AppLoading must retain an essential seal opacity pulse'
Assert-Match -Content $component -Pattern '@keyframes\s+app-loading-knot-essential-pulse' -Message 'Reduced-motion AppLoading must retain an essential knot opacity pulse'
Assert-Match -Content $component -Pattern '(?s)@media\s*\(prefers-reduced-motion:\s*reduce\).*?\.app-loading__seal\s*\{[^}]*animation:\s*app-loading-seal-essential-pulse\s+1\.2s\s+ease-in-out\s+infinite;[^}]*transform:\s*none;' -Message 'Reduced-motion seal must pulse opacity without transform motion'
Assert-Match -Content $component -Pattern '(?s)@media\s*\(prefers-reduced-motion:\s*reduce\).*?\.app-loading__knot\s*\{[^}]*animation:\s*app-loading-knot-essential-pulse\s+1\.2s\s+ease-in-out\s+\.2s\s+infinite;[^}]*transform:\s*none;' -Message 'Reduced-motion knot must pulse opacity without transform motion'
if ($component -match '(?s)@media\s*\(prefers-reduced-motion:\s*reduce\).*?animation:\s*none') {
throw 'Reduced-motion AppLoading must not become completely static'
}
```
- [ ] **Step 2:运行聚焦契约并确认 RED**
运行:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tests/app-loading-contract.ps1
```
预期:退出码非 0,错误首先指向缺少 `app-loading-seal-essential-pulse`,证明测试捕获的是当前完全静止行为,而不是语法或路径错误。
- [ ] **Step 3:实现最小降动效透明度脉冲**
`components/AppLoading.vue` 中保留现有正常模式关键帧,并把当前媒体查询替换为:
```scss
@keyframes app-loading-seal-essential-pulse {
0%, 100% { opacity: .58; }
50% { opacity: 1; }
}
@keyframes app-loading-knot-essential-pulse {
0%, 100% { opacity: .28; }
50% { opacity: .86; }
}
@media (prefers-reduced-motion: reduce) {
.app-loading__seal {
animation: app-loading-seal-essential-pulse 1.2s ease-in-out infinite;
transform: none;
}
.app-loading__knot {
animation: app-loading-knot-essential-pulse 1.2s ease-in-out .2s infinite;
transform: none;
}
}
```
不得修改模板、属性、尺寸、文案或正常模式关键帧。
- [ ] **Step 4:运行聚焦契约并确认 GREEN**
运行:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tests/app-loading-contract.ps1
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tests/g01-loading-state-contract.ps1
```
预期:分别输出 `APP-LOADING-CONTRACT PASS``G01-LOADING-STATE-CONTRACT PASS`,退出码均为 0。
- [ ] **Step 5:在当前唯一 Chrome 标签页验证真实动画**
保持 G01 `state=loading` 和 412×915 视口。通过 9222 CDP 在同一次检查中读取:
```js
const seal = document.querySelector('.app-loading__seal')
const knot = document.querySelector('.app-loading__knot')
const sealStyle = getComputedStyle(seal)
const knotStyle = getComputedStyle(knot)
({
reduced: matchMedia('(prefers-reduced-motion: reduce)').matches,
sealAnimation: sealStyle.animationName,
sealTransform: sealStyle.transform,
sealOpacity: sealStyle.opacity,
sealAnimations: seal.getAnimations().length,
knotAnimation: knotStyle.animationName,
knotTransform: knotStyle.transform,
knotOpacity: knotStyle.opacity,
knotAnimations: knot.getAnimations().length
})
```
等待约 420ms 后再次读取。预期:
- `reduced``true`
- 两个 `animationName` 分别以新的 essential pulse 名称开头;Vue scoped CSS 可在运行时追加哈希后缀;
- 两个 `getAnimations().length` 均大于 0
- 两次读取的透明度不同;
- 两个 `transform` 均为 `none`
- 页面仍为 G01 加载态。
- [ ] **Step 6:捕获并人工检查当前候选帧**
使用当前唯一标签页保存:
```text
docs/design/screens/runtime/2026-07-19/g01-approval/03-loading-essential-motion-412x915.png
```
人工检查:标题栏、背景、印牌、如意结、两行文案和底部导航均完整;没有列表、失败态或原生 Loading 串入。静态截图不作为动画运行证明,动画结论以 Step 5 的多时点数据为准。
- [ ] **Step 7:运行受影响合同与空白检查**
运行:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tests/g-series-app-loading-contract.ps1
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tests/module-app-loading-contract.ps1
git diff --check -- components/AppLoading.vue tests/app-loading-contract.ps1 docs/superpowers/specs/2026-07-19-app-loading-essential-motion-design.md docs/superpowers/plans/2026-07-19-app-loading-essential-motion.md
```
预期:两个合同均输出各自的 `PASS``git diff --check` 退出码为 0。不得把 Android/HBuilderX 写成已验证。
- [ ] **Step 8:回到同一加载态等待用户复核**
保持浏览器停留在修正后的 G01 加载态,不自动切换到失败态。只有用户明确说加载态“通过”后,才继续 G01 下一状态;在此之前 G01 保持 `[!]`