-
Notifications
You must be signed in to change notification settings - Fork 1
Closed
Description
背景
当前内容库界面右侧内容预览面板切换无过渡动画,缺乏层次感与方向感。我们决定采用 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)
- 依赖:
framer-motion@latest(若项目尚未安装)。 - 封装组件:
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>
);
}- 集成:在
ContentPreview外包裹PreviewTransition,id使用contentId。 - 无障碍与性能
@media (prefers-reduced-motion)自动关闭动画(Framer 自带useReducedMotion)。- 动画结束后执行
focus()与scrollTop = 0。
实现技巧 & 踩坑提示
- 预览面板必须
position:absolute,外层relative+overflow:hidden。 - 仅动画 transform/opacity,GPU 加速,无 reflow。
- 使用 CSS 变量
--gap代替 Magic Number(24px),便于响应式。 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)
验收标准
- 点击左侧卡片后触发流畅动画,FPS ≥ 55,无 reflow。
- 旧面板在动画结束后从 DOM 卸载;新面板获得焦点并重置滚动。
- 启用
prefers-reduced-motion时不播放动画,直接切换。 - 单元测试与 E2E 测试全部通过。
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Type
Projects
Status
Done