# Hero 数字水墨动画设计规格 > 日期:2026-05-03 > 状态:待审查 > 决策者:张翔 ## 1. 需求与场景 ### 背景 当前 Hero 区域(`hero-section-v2.tsx`)使用纯白背景 + 基础 FadeUp 文字动画,视觉表现力不足,缺乏品牌辨识度。 ### 目标 为 Hero 区域添加"数字水墨"动画效果,传达"传统智慧 → 数字化转型"的品牌叙事。 ### 成功标准 - Hero 入场时有明确的三阶段动画叙事(墨粒扩散 → 沉淀 → 数据化) - 动画在 60fps 下流畅运行,移动端无卡顿 - `prefers-reduced-motion` 下优雅降级 - 动画播放完毕后零持续 CPU 开销 - 与现有 Hero 文字内容无视觉冲突 ### 约束 - 纯入场动画,无鼠标/滚动交互 - 零新增外部依赖(纯 Canvas 2D API) - 遵循项目现有特效组件模式(`src/components/effects/`) ## 2. 技术选型 **方案:Canvas 2D 粒子系统** | 维度 | 评价 | |------|------| | 视觉效果 | 120-200 粒子 + 墨晕 + 连接线,效果完整 | | 性能 | Canvas 2D 渲染 200 粒子无压力,移动端流畅 | | 兼容性 | Canvas 2D 全浏览器支持,无 WebGL 依赖 | | 包体积 | 零依赖,< 5KB gzipped | | 可维护性 | 状态机驱动三阶段,逻辑清晰 | | 项目一致性 | 与 DataParticleFlow、SubtleParticles 等现有组件模式一致 | 排除方案:SVG + CSS 动画(feTurbulence 滤镜移动端不稳定,三阶段叙事难以用 CSS 实现) ## 3. 组件设计 ### 3.1 新组件:InkDataMorph **文件**:`src/components/effects/ink-data-morph.tsx` ```tsx interface InkDataMorphProps { particleCount?: number; // 默认 150 primaryColor?: string; // 墨色,默认 '#1C1C1C' accentColor?: string; // 朱砂红,默认 '#C41E3A' connectionColor?: string; // 连接线色,默认 '#C41E3A' connectionDistance?: number; // 连接线触发距离,默认 80px className?: string; } ``` **渲染**:绝对定位 `` 元素,`pointer-events: none`,`aria-hidden="true"` ### 3.2 三阶段状态机 ``` Phase 1: SPREADING (0s ~ 2s) ├── 墨粒从两个中心点向外扩散 ├── 粒子带有机随机速度 + 方向 ├── 大粒子附带墨晕光晕(径向渐变) └── 25% 粒子为朱砂红色 Phase 2: SETTLING (2s ~ 3.5s) ├── 粒子速度衰减至接近静止 ├── 透明度缓慢降低(模拟墨汁沉淀) └── 整体画面趋于稳定 Phase 3: MORPHING (3.5s ~ 6s) ├── 粒子缓慢移向目标数据点位置 ├── 粒子半径缩小为数据点大小 ├── 相邻粒子间浮现连接线(距离 < connectionDistance) └── 最终状态:静态数据网络图 COMPLETE (6s+) ├── 停止 requestAnimationFrame └── 零持续 CPU 开销 ``` ### 3.3 粒子数据结构 ```ts interface Particle { x: number; // 当前 x y: number; // 当前 y vx: number; // x 速度 vy: number; // y 速度 radius: number; // 当前半径 initialRadius: number; // 初始半径(墨粒大小) dataRadius: number; // 数据点目标半径 opacity: number; // 当前透明度 isAccent: boolean; // 是否为朱砂红 phase: 'spreading' | 'settling' | 'morphing' | 'complete'; spreadTime: number; maxSpreadTime: number; settleTime: number; morphProgress: number; targetX: number; // 数据点目标 x targetY: number; // 数据点目标 y } ``` ### 3.4 扩散中心点布局 ``` ┌─────────────────────────────────────────┐ │ ● 中心1 │ │ (70%, 35%) │ │ │ │ 文字区域 ←── │ │ │ │ ● 中心2 │ │ (25%, 65%) │ │ │ └─────────────────────────────────────────┘ ``` - 中心1(右上):120 粒子,主墨色 - 中心2(左下):60 粒子,延迟 500ms 启动 - Phase 3 数据点目标位置分布在右侧和下方,不遮挡文字 ### 3.5 性能策略 - **2x Canvas 渲染**:`canvas.width = clientWidth * 2`,CSS 缩放,Retina 清晰 - **连接线优化**:Phase 3 才计算,使用网格空间分区避免 O(n²) - **一次性动画**:Phase 3 完成后停止 rAF - **`prefers-reduced-motion`**:跳过动画,直接渲染最终静态数据网络图 ## 4. Hero Section 集成 ### 4.1 修改文件 `src/components/sections/hero-section-v2.tsx` ### 4.2 变更点 | 项目 | 当前 | 改造后 | |------|------|--------| | 背景色 | `bg-white` | `bg-[#FAFAF5]`(宣纸暖白) | | Canvas 层 | 无 | `` 绝对定位 inset-0 z-0 | | 内容层 z-index | 默认 | `z-10` | | 品牌标签 delay | 0.1s | 1.6s | | 标题 delay | 0.2s | 1.9s | | 副标题 delay | 0.3s | 2.2s | | 描述 delay | 0.4s | 2.5s | | 按钮组 delay | 0.5s | 2.8s | | IntersectionObserver | 保留 | 保留,只在进入视口时启动 Canvas 动画 | | useReducedMotion | 保留 | 保留,禁用时 Canvas 直接渲染最终状态 | ### 4.3 导入方式 使用 `dynamic import` + `ssr: false`,与项目现有特效组件加载方式一致: ```tsx const InkDataMorph = dynamic( () => import('@/components/effects/ink-data-morph').then(mod => ({ default: mod.InkDataMorph })), { ssr: false } ); ``` ## 5. 测试策略 ### 5.1 单元测试(ink-data-morph.test.tsx) - 组件渲染 canvas 元素 - 接受自定义 props(particleCount, colors 等) - `prefers-reduced-motion` 下不启动 rAF - unmount 时清理 rAF ### 5.2 集成测试(hero-section-v2.test.tsx 更新) - InkDataMorph 在 Hero section 中渲染 - 文字内容 z-index 在 Canvas 之上 - 背景色为 `#FAFAF5` ### 5.3 视觉验证 - `npm run dev` 打开首页,观察三阶段动画 - 移动端视口(375px)验证粒子密度和性能 - `prefers-reduced-motion` 模拟验证降级效果 ## 6. 文件清单 | 操作 | 文件 | |------|------| | 新建 | `src/components/effects/ink-data-morph.tsx` | | 新建 | `src/components/effects/ink-data-morph.test.tsx` | | 修改 | `src/components/sections/hero-section-v2.tsx` | | 修改 | `src/components/effects/index.ts`(导出新组件) |