Tips & Tricks (更新: 2026/6/2)

Claude Code 与 Framer Motion:Motion for React 实战指南

用 Claude Code 实装 Framer Motion/Motion for React:提示词、可运行代码、常见坑、无障碍与验证步骤。

Claude Code 与 Framer Motion:Motion for React 实战指南

很多开发者仍然用 Framer Motion 这个关键词搜索,但 2026 年的官方 React 文档已经把它称为 Motion for React。新项目建议安装 motion,并从 motion/react 引入 React API。维护旧项目时,如果仓库已经统一使用 framer-motion,不要在同一个文件里随意混用。本文以官方 Motion for React 文档为基准,讲如何让 Claude Code 生成可维护的动画代码。

Claude Code 很适合快速补齐交互动效,但它不会自动理解产品边界。只说“让这个页面更顺滑”,通常会得到过度的弹跳、缺少 Reduced Motion 的实现,或者因为父组件提前卸载导致退出动画失效。Masa 在一个内部仪表盘上试过这个流程:第一版视觉很炫,但卡片移动过大,通知没有 exit,滚动头图在低性能电脑上明显卡顿。真正有用的做法,是先给 Claude Code 明确目标、范围、无障碍要求和验证方式。

本文面向已经会写 React 组件、但经常用 CSS 手工调整动画的开发者。下面的 TSX 示例在安装 npm install motion 后可以直接放入 React 项目。Next.js 或 Astro 中使用时,请把动画部分放到客户端组件里。

先固定设计约束

动画不是装饰,而是帮助用户理解状态变化。让 Claude Code 动手之前,先写清楚四件事。

约束交给 Claude Code 的内容审查时看什么
目的哪个状态变化需要被解释动画是否真的提升理解
范围允许修改和禁止修改的文件是否混入无关重构
API使用 motion/react,支持 Reduced Motionimport 和行为是否符合官方文档
验证手动操作、键盘、低速设备是否按真实使用路径测试

可以把工作流理解为:

目标与约束
  -> Claude Code 提示词
  -> Motion for React 实装
  -> 手动验证与无障碍验证
  -> 有针对性的二次提示

建议的第一条提示词如下。

请为这个现有 React 组件加入 Motion for React 动画。
要求:
- 新增 Motion API 从 `motion/react` 引入
- 不改数据获取、表单提交、路由逻辑
- 支持 Reduced Motion
- 优先使用 opacity 与 transform
- 避免 layout shift
- 输出变更后需要手动验证的清单

这类提示能让 Claude Code 产出更容易 review 的差分。特别是 import 来源要说清楚,因为网上仍有大量旧文章使用 framer-motion

用例1:卡片列表的自然入场

仪表盘、文章列表、功能卡片适合使用轻量 stagger。目标不是让每张卡都表演,而是建立阅读顺序。Masa 的验证结果是,6 张卡片时 0.06 到 0.09 秒的错开比较舒服,再长就会拖慢操作。

import { motion, useReducedMotion } from "motion/react";

type Feature = {
  id: string;
  title: string;
  body: string;
  metric: string;
};

const demoFeatures: Feature[] = [
  {
    id: "review",
    title: "待审查",
    body: "人类负责确认 Claude Code 生成的代码差分。",
    metric: "8 项",
  },
  {
    id: "motion",
    title: "动画清理",
    body: "用短过渡解释画面变化,而不是拖慢工作。",
    metric: "14%",
  },
  {
    id: "a11y",
    title: "Reduced Motion",
    body: "用户希望减少动态时,改用更安静的淡入淡出。",
    metric: "已支持",
  },
];

export function AnimatedFeatureCards({
  items = demoFeatures,
}: {
  items?: Feature[];
}) {
  const shouldReduceMotion = useReducedMotion();

  const container = {
    hidden: { opacity: 0 },
    visible: {
      opacity: 1,
      transition: {
        staggerChildren: shouldReduceMotion ? 0 : 0.08,
      },
    },
  };

  const card = {
    hidden: {
      opacity: 0,
      y: shouldReduceMotion ? 0 : 16,
    },
    visible: {
      opacity: 1,
      y: 0,
      transition: {
        duration: 0.32,
        ease: "easeOut",
      },
    },
  };

  return (
    <motion.section
      aria-label="功能卡片"
      variants={container}
      initial="hidden"
      animate="visible"
      style={{
        display: "grid",
        gap: 16,
        gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
      }}
    >
      {items.map((item) => (
        <motion.article
          key={item.id}
          variants={card}
          whileHover={shouldReduceMotion ? undefined : { y: -4 }}
          style={{
            border: "1px solid #d9e2ec",
            borderRadius: 12,
            padding: 20,
            background: "#ffffff",
            boxShadow: "0 8px 24px rgba(15, 23, 42, 0.08)",
          }}
        >
          <p style={{ margin: 0, color: "#2563eb", fontWeight: 700 }}>
            {item.metric}
          </p>
          <h3 style={{ margin: "8px 0", fontSize: 18 }}>{item.title}</h3>
          <p style={{ margin: 0, lineHeight: 1.7, color: "#475569" }}>
            {item.body}
          </p>
        </motion.article>
      ))}
    </motion.section>
  );
}

审查时重点看三点:key 是否稳定,移动是否集中在 transform,Reduced Motion 是否取消大位移。若需要二次提示,可以要求 Claude Code 在 12 张卡片时仍保持页面可立即操作。

用例2:通知的追加与删除

AnimatePresence 用来让即将离开 React 树的元素播放退出动画。官方 AnimatePresence 文档说明,直接子元素需要稳定 key。最常见的错误是把整个通知区域放进条件渲染,导致父级先消失,子元素没有机会执行 exit

import { useState } from "react";
import { AnimatePresence, motion } from "motion/react";

type Toast = {
  id: number;
  message: string;
};

let nextToastId = 1;

export function AnimatedNotifications() {
  const [toasts, setToasts] = useState<Toast[]>([
    { id: 0, message: "保存成功" },
  ]);

  function addToast() {
    const id = nextToastId++;
    setToasts((current) => [
      ...current,
      { id, message: `后台任务 ${id} 已完成` },
    ]);
  }

  function dismissToast(id: number) {
    setToasts((current) => current.filter((toast) => toast.id !== id));
  }

  return (
    <div>
      <button type="button" onClick={addToast}>
        添加通知
      </button>

      <div
        aria-live="polite"
        style={{
          position: "fixed",
          right: 24,
          top: 24,
          display: "grid",
          gap: 12,
          width: "min(360px, calc(100vw - 48px))",
        }}
      >
        <AnimatePresence initial={false} mode="popLayout">
          {toasts.map((toast) => (
            <motion.div
              layout
              key={toast.id}
              initial={{ opacity: 0, x: 40, scale: 0.96 }}
              animate={{ opacity: 1, x: 0, scale: 1 }}
              exit={{ opacity: 0, x: 40, scale: 0.96 }}
              transition={{ duration: 0.2, ease: "easeOut" }}
              style={{
                borderRadius: 10,
                border: "1px solid #cbd5e1",
                background: "#ffffff",
                padding: 16,
                boxShadow: "0 12px 30px rgba(15, 23, 42, 0.16)",
              }}
            >
              <p style={{ margin: "0 0 12px", color: "#0f172a" }}>
                {toast.message}
              </p>
              <button type="button" onClick={() => dismissToast(toast.id)}>
                关闭
              </button>
            </motion.div>
          ))}
        </AnimatePresence>
      </div>
    </div>
  );
}

这个组件要额外确认:通知为 0 时容器不被卸载,键盘用户可以关闭通知,aria-live 不会连续读出过多内容。通知是高频 UI,读屏和焦点体验比动画是否华丽更重要。

用例3:长页面的滚动进度

长文档、教程和登录页常需要告诉读者“现在读到哪里”。Motion 的 useScroll 能返回页面或元素的滚动进度,再用 useSpring 可以让进度条更顺滑。不要把每个区块都做成视差场景;Reduced Motion 开启时,应该去掉大幅移动,保留可读性。

import { useRef } from "react";
import {
  motion,
  useReducedMotion,
  useScroll,
  useSpring,
  useTransform,
} from "motion/react";

const sections = [
  {
    title: "固定需求",
    body: "让 Claude Code 修改前,先写明目标、目标文件和禁止事项。",
  },
  {
    title: "少量移动",
    body: "先用 opacity 与 transform,让动效服务于理解。",
  },
  {
    title: "按真实路径验证",
    body: "发布前检查低速设备、键盘操作和 Reduced Motion。",
  },
];

export function ScrollReadingProgress() {
  const articleRef = useRef<HTMLElement | null>(null);
  const shouldReduceMotion = useReducedMotion();
  const { scrollYProgress } = useScroll({
    target: articleRef,
    offset: ["start start", "end end"],
  });

  const scaleX = useSpring(scrollYProgress, {
    stiffness: 120,
    damping: 28,
    mass: 0.2,
  });
  const y = useTransform(scrollYProgress, [0, 1], [0, -48]);

  return (
    <article ref={articleRef} style={{ position: "relative", padding: 24 }}>
      <motion.div
        aria-hidden="true"
        style={{
          position: "sticky",
          top: 0,
          zIndex: 10,
          height: 4,
          scaleX: shouldReduceMotion ? 1 : scaleX,
          transformOrigin: "0% 50%",
          background: "#2563eb",
        }}
      />

      <motion.header
        style={{
          y: shouldReduceMotion ? 0 : y,
          padding: "56px 0 32px",
        }}
      >
        <p style={{ color: "#2563eb", fontWeight: 700 }}>
          Claude Code x Motion
        </p>
        <h2 style={{ fontSize: 36, margin: 0 }}>
          随着阅读逐步呈现上下文的页面
        </h2>
      </motion.header>

      <div style={{ display: "grid", gap: 24 }}>
        {sections.map((section) => (
          <section
            key={section.title}
            style={{
              border: "1px solid #dbe4ee",
              borderRadius: 12,
              padding: 24,
              background: "#ffffff",
            }}
          >
            <h3>{section.title}</h3>
            <p style={{ lineHeight: 1.8 }}>{section.body}</p>
          </section>
        ))}
      </div>
    </article>
  );
}

这个模式适合文档、入门引导、报告页。它的作用是帮助定位,不是填充页面。一个清晰的进度条,通常比五个滚动触发的飞入动画更可靠。

让 Claude Code 审查差分

实现之后,不要只看浏览器里是否会动。把下面的审查提示交给 Claude Code,让它从失败模式出发检查差分。

请把当前差分当作 Motion for React 实装来审查。
重点严格检查:
- 是否混用了 `motion/react` 和旧的 `framer-motion`
- `AnimatePresence` 的直接子元素是否有稳定 key
- Reduced Motion 是否关闭大位移、视差和类似自动播放的效果
- 是否频繁动画 width、height、top、left
- 手动测试步骤是否对应真实用户操作

这样 Claude Code 就不只是生成代码,也能成为第一轮 reviewer。因为它能看到周边文件,所以还可以发现 CSS 冲突、状态逻辑变化、测试不足等代码片段看不到的问题。

常见坑与发布检查

第一个坑是旧 import。遗留项目继续使用 framer-motion 并不一定错,但新实现应优先跟随 motion 包和 motion/react。第二个坑是把 exit 失效误判为 easing 问题。多数情况下,真正原因是父级过早卸载、key 使用数组下标,或者元素没有从 AnimatePresence 内离开。

第三个坑是过度相信 layout。它适合排序和尺寸变化,但未设置尺寸的图片、延迟加载的字体、突然变化的 Grid 列数仍会造成跳动。第四个坑是最后才考虑无障碍。Motion 的无障碍指南介绍了 Reduced Motion;只要使用大位移、视差或自动播放式效果,就应该在第一版中实现替代路径。第五个坑是只在高速开发机上测试。opacitytransform 通常安全,但大面积 blur、频繁 shadow、多个大图同时移动仍可能卡顿。

发布前至少确认:官方文档与 import 一致,每个动画都有产品目的,Reduced Motion 去掉大运动,AnimatePresence 子元素有稳定 key,键盘操作和 focus 没坏,滚动效果不影响阅读,并在 PR 或文章末尾留下验证结果。

相关主题可以继续阅读 Radix UI 与 Claude Codeshadcn/ui 与 Claude CodeClaude Code 动画实装指南。Claude Code 本身的用法请参考 Anthropic 的 Claude Code 文档

总结

Claude Code 与 Framer Motion 的价值,不只是更快写出漂亮动画,而是把实现、无障碍和 review 条件放进同一个工作流。Masa 实测后最有效的一点,是在第一条提示词中写明 motion/react、Reduced Motion 和手动验证要求。比起事后修补,初始差分就带约束更省时间。想把提示词模板保存下来,可以领取免费速查表;如果要把多个页面一起做动效和 UI 审查,可以从培训与咨询开始。

#Claude Code #Framer Motion #Motion for React #React #动画 #UI/UX
免费

免费 PDF: Claude Code 速查表

输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。

我们会妥善保护你的信息,不发送垃圾邮件。

把 Claude Code 变成真正能带来结果的工作流

先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。

Masa

关于作者

Masa

专注 Claude Code 实务流程、团队导入和内容转化的工程师。