perf(motion): 清理末批 P0 —— 移除循环动画 + 删除死代码

按用户决策收尾 Task #16,P0 硬违规 13 → 0:

- 移除 3 处循环动画(均违反 CONTEXT.md「禁循环动画」)
  detail-hero 徽章 Sparkles 旋转 4s、滚动箭头浮动 2s、brand-visuals 半径脉冲 4s
  → motion.* 改静态元素,保留视觉语义但不再循环
- 删除死代码 src/lib/animations.tsx(923 行,全站零引用,含 10 处 P0 + spring variants)
  同步删除 src/lib/animations.test.tsx
- 级联修复:brand-visuals 因删 guard 冗余的 shouldReduceMotion 声明一并移除

验证:
- 审计 P0 13 → 0,P1 163→156,P2 28→25(死代码移除导致分母下降)
- tsc --noEmit 0 error;eslint 0 error
- 视觉基线不变:时长/循环改动在 reducedMotion+animations disabled 下不改变最终像素
This commit is contained in:
2026-09-03 19:28:18 +08:00
parent ceeb88aca3
commit 7e19661a99
4 changed files with 8 additions and 1783 deletions
+4 -9
View File
@@ -83,12 +83,9 @@ export function DetailHero({
borderColor: `${theme.accentColor}25`,
}}
>
<motion.span
animate={{ rotate: [0, 360] }}
transition={{ duration: 4, repeat: Infinity, ease: 'linear' as const }}
>
<span>
<Sparkles className="w-3.5 h-3.5" />
</motion.span>
</span>
{badge || theme.badge}
</motion.div>
)}
@@ -192,13 +189,11 @@ export function DetailHero({
transition={{ delay: 1.5, duration: 0.3 }}
className="absolute bottom-8 left-1/2 -translate-x-1/2"
>
<motion.div
animate={{ y: [0, 8, 0] }}
transition={{ duration: 2, repeat: Infinity, ease: 'easeInOut' as const }}
<div
className="text-text-muted cursor-pointer hover:text-text-secondary transition-colors"
>
<ChevronDown className="w-6 h-6" />
</motion.div>
</div>
</motion.div>
</div>
</section>
+4 -10
View File
@@ -5,8 +5,6 @@ import { useReducedMotion } from '@/hooks/use-reduced-motion';
// ===== 抽象几何装饰 - 用于各 section 背景 =====
export function GeometricDecoration({ variant = 'circles' }: { variant?: 'circles' | 'lines' | 'grid' | 'waves' }) {
const shouldReduceMotion = useReducedMotion();
if (variant === 'circles') {
return (
<div className="absolute inset-0 overflow-hidden pointer-events-none" aria-hidden="true">
@@ -14,14 +12,10 @@ export function GeometricDecoration({ variant = 'circles' }: { variant?: 'circle
<circle cx="100" cy="100" r="80" fill="none" stroke="currentColor" className="text-[var(--color-brand)]" strokeWidth="0.5" />
<circle cx="100" cy="100" r="60" fill="none" stroke="currentColor" className="text-[var(--color-brand)]" strokeWidth="0.5" />
<circle cx="100" cy="100" r="40" fill="none" stroke="currentColor" className="text-[var(--color-brand)]" strokeWidth="0.5" />
{!shouldReduceMotion && (
<motion.circle
cx="100" cy="100" r="20"
fill="rgba(var(--color-brand-rgb, 196, 30, 58), 0.1)"
animate={{ r: [20, 35, 20], opacity: [0.1, 0.05, 0.1] }}
transition={{ duration: 4, repeat: Infinity, ease: "easeInOut" }}
/>
)}
<circle
cx="100" cy="100" r="20"
fill="rgba(var(--color-brand-rgb, 196, 30, 58), 0.1)"
/>
</svg>
<svg className="absolute bottom-[15%] left-[8%] w-48 h-48 opacity-[0.03]" viewBox="0 0 200 200">
<rect x="40" y="40" width="120" height="120" rx="20" fill="none" stroke="currentColor" className="text-[var(--color-text-subtle)]" strokeWidth="0.5" transform="rotate(15 100 100)" />