Skip to content

实现 Reeder 风格内容面板过渡动画(Framer Motion 封装) #189

@Neo-Neooo

Description

@Neo-Neooo

背景

当前内容库界面右侧内容预览面板切换无过渡动画,缺乏层次感与方向感。我们决定采用 Framer Motion 复刻 Reeder 风格的过渡效果,并封装为可复用组件。

动效说明(对标 Reeder)

阶段 旧面板 (i) 新面板 (i+1) 观感
初始 固定宽度,右侧留约 24 px 间隔 在屏幕外 100% + 间隔处 布局稳定
点击后 scale(0.94) + translateY(40px) + opacity 0.85 translateX(100% + gap) → 0 深度感 + 方向感
结束 从 DOM 移除或隐藏 变成新的 current 释放内存

两张面板位于同一 grid 单元,但各自包在 position:absolute 的容器中,确保新面板可突破右边距再落位。所有动画仅操作 transform / opacity,避免回流。

技术方案(Framer Motion)

  1. 依赖framer-motion@latest(若项目尚未安装)。
  2. 封装组件frontend/app/components/animations/PreviewTransition.tsx
import { AnimatePresence, motion } from "framer-motion";

export function PreviewTransition({ id, children }: { id: string; children: React.ReactNode }) {
  return (
    <AnimatePresence mode="wait">
      <motion.article
        key={id}
        initial={{ x: "100%", scale: 1 }}
        animate={{ x: 0 }}
        exit={{ y: 40, scale: 0.94, opacity: 0.85 }}
        transition={{ type: "spring", stiffness: 400, damping: 40 }}
        style={{ position: "absolute", inset: "0 24px 0 0" }}
      >
        {children}
      </motion.article>
    </AnimatePresence>
  );
}
  1. 集成:在 ContentPreview 外包裹 PreviewTransitionid 使用 contentId
  2. 无障碍与性能
    • @media (prefers-reduced-motion) 自动关闭动画(Framer 自带 useReducedMotion)。
    • 动画结束后执行 focus()scrollTop = 0

实现技巧 & 踩坑提示

  1. 预览面板必须 position:absolute,外层 relative + overflow:hidden
  2. 仅动画 transform/opacity,GPU 加速,无 reflow。
  3. 使用 CSS 变量 --gap 代替 Magic Number(24px),便于响应式。
  4. AnimatePresence 会等待 exit 动画完成再卸载,避免 DOM 泄漏。

任务拆分

  • A. 安装并配置 framer-motion
  • B. 创建 PreviewTransition 组件并 Storybook 演示
  • C. 将 ContentPreview 迁移至新动画组件
  • D. prefers-reduced-motion 适配 & focus/scroll 处理
  • E. 单元测试(RTL)卸载验证
  • F. Playwright 视觉回归测试
  • G. 文档更新(docs/product/animation-guidelines.md)

验收标准

  1. 点击左侧卡片后触发流畅动画,FPS ≥ 55,无 reflow。
  2. 旧面板在动画结束后从 DOM 卸载;新面板获得焦点并重置滚动。
  3. 启用 prefers-reduced-motion 时不播放动画,直接切换。
  4. 单元测试与 E2E 测试全部通过。

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status

Done

Relationships

None yet

Development

No branches or pull requests

Issue actions