返回首页
🎨 前端 / Web

Motion + 动画设计实战:现代 Web 交互动效完整指南 2026

Motion(原 Framer Motion)是 2026 年最流行的 React 动画库。本文从 0 到生产级交互动效,含 4 个实战项目 + 性能优化 + AI 动画 + 设计令牌。

Motion · Framer Motion · 动画 · GSAP · View Transitions · 交互动效 · React
📰

今日技术简讯

📰 技术简讯 · 2026-08-05

今日聚合 6 条热门技术内容(中文素材优先)。

🤖 AI / LLM

1. Motion 推出 AI 自动动画

2. Lottie 推出 AI 生成器

  • 链接https://lottiefiles.com/blog/ai
  • 来源:LottieFiles
  • 摘要:LottieFiles 推出 AI 生成器,文本生成 Lottie 动画,集成 Figma / Web / iOS。

🎨 前端 / Web

3. View Transitions API 跨浏览器

4. shadcn/ui 推出 Motion Presets

⚙️ 后端 / 架构

5. GSAP 推出 WebGPU 渲染

🚀 独立开发 / OPC

6. 即刻"现代 Web 动画"专题


数据来源:掘金 / InfoQ 中文 / 即刻 / 少数派 / HN 采集时间:2026-08-05 09:00 (UTC+8)

📝

今日深度文

Motion + 动画设计实战:现代 Web 交互动效完整指南 2026

一句话结论:动画 = 2026 年 SaaS 差异化核心。Motion(前 Framer Motion)+ View Transitions API + GSAP + AI 自动动画。本文从 0 到生产级交互动效。

背景

2026 年 Web 动画生态:

Motion(前 Framer Motion)       ← React 首选(声明式)
View Transitions API             ← 原生页面切换
GSAP                             ← 复杂时间线
Lottie                           ← 设计师动画
CSS @keyframes + transitions     ← 基础动画
Web Animations API               ← 浏览器原生

新趋势:
- AI 自动动画(自然语言 → 代码)
- View Transitions 跨浏览器
- 滚动驱动动画(Scroll-driven)
- 3D + 物理引擎

为什么动画必备:

  1. 提升感知性能:用户感觉更快
  2. 引导注意力:聚焦核心元素
  3. 表达品牌:差异化竞争
  4. 提升转化:CTA 动画 +10%
  5. 愉悦体验:用户满意度 +25%

6 大核心优势

1. Motion(前 Framer Motion)

// framer-motion 已改名 motion
import { motion } from 'motion/react';

// 基础动画
<motion.div
  initial={{ opacity: 0, y: 20 }}
  animate={{ opacity: 1, y: 0 }}
  transition={{ duration: 0.5, ease: 'easeOut' }}
>
  Hello Motion
</motion.div>

// 交互动画
<motion.button
  whileHover={{ scale: 1.05 }}
  whileTap={{ scale: 0.95 }}
>
  Click me
</motion.button>

// 布局动画
<motion.div layout>
  <motion.div layoutId="card-1" />
  <motion.div layoutId="card-2" />
</motion.div>

2. View Transitions API(原生)

// app/page.tsx
import { unstable_ViewTransition as ViewTransition } from 'react';

// 启用原生页面切换
export default function Page() {
  return (
    <ViewTransition>
      <main>
        {/* 内容 */}
      </main>
    </ViewTransition>
  );
}
/* view-transitions.css */
::view-transition-old(root) {
  animation: fade-out 0.3s ease-out;
}

::view-transition-new(root) {
  animation: fade-in 0.3s ease-in;
}

@keyframes fade-out {
  to { opacity: 0; }
}

@keyframes fade-in {
  from { opacity: 0; }
}

3. GSAP(复杂时间线)

import { useGSAP } from '@gsap/react';
import gsap from 'gsap';

function ComplexAnimation() {
  useGSAP(() => {
    const tl = gsap.timeline({ repeat: -1 });
    tl.to('.box', { x: 100, duration: 1 })
      .to('.box', { y: 100, duration: 1 })
      .to('.box', { rotation: 360, duration: 1 });
  });
  
  return <div className="box">Box</div>;
}

4. Lottie(设计师动画)

import Lottie from 'lottie-react';
import successAnimation from './success.json';

< Lottie animationData={successAnimation} loop={false} />;

5. 滚动驱动动画(CSS)

/* CSS Scroll-driven Animations */
@keyframes grow {
  from { transform: scale(0.8); }
  to { transform: scale(1); }
}

.card {
  animation: grow linear;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}

6. AI 自动动画

// Motion AI(2026)
const animation = await motion.ai.generate({
  prompt: '卡片从下方滑入,依次淡入,间隔 0.1 秒',
  duration: 1,
  elements: ['card-1', 'card-2', 'card-3'],
});

// 输出代码
<motion.div
  initial={{ y: 50, opacity: 0 }}
  animate={{ y: 0, opacity: 1 }}
  transition={{ duration: 0.5, delay: 0.1 }}
/>

4 个实战项目

项目 1:按钮交互

// components/Button.tsx
import { motion } from 'motion/react';
import { Loader2 } from 'lucide-react';

interface ButtonProps {
  loading?: boolean;
  children: React.ReactNode;
  onClick?: () => void;
}

export function Button({ loading, children, onClick }: ButtonProps) {
  return (
    <motion.button
      whileHover={{ scale: 1.02 }}
      whileTap={{ scale: 0.98 }}
      transition={{ type: 'spring', stiffness: 400, damping: 17 }}
      onClick={onClick}
      disabled={loading}
      className="relative px-4 py-2 bg-primary text-white rounded-md"
    >
      {loading && (
        <motion.span
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          className="absolute inset-0 flex items-center justify-center"
        >
          <Loader2 className="w-4 h-4 animate-spin" />
        </motion.span>
      )}
      <motion.span animate={{ opacity: loading ? 0 : 1 }}>
        {children}
      </motion.span>
    </motion.button>
  );
}

项目 2:模态框

// components/Modal.tsx
import { motion, AnimatePresence } from 'motion/react';

interface ModalProps {
  open: boolean;
  onClose: () => void;
  children: React.ReactNode;
}

export function Modal({ open, onClose, children }: ModalProps) {
  return (
    <AnimatePresence>
      {open && (
        <>
          {/* 背景 */}
          <motion.div
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            onClick={onClose}
            className="fixed inset-0 bg-black/50 z-40"
          />
          
          {/* 内容 */}
          <motion.div
            initial={{ opacity: 0, scale: 0.95, y: 20 }}
            animate={{ opacity: 1, scale: 1, y: 0 }}
            exit={{ opacity: 0, scale: 0.95, y: 20 }}
            transition={{ type: 'spring', duration: 0.3 }}
            className="fixed inset-0 flex items-center justify-center z-50"
          >
            <div className="bg-white rounded-lg p-6 max-w-md">
              {children}
            </div>
          </motion.div>
        </>
      )}
    </AnimatePresence>
  );
}

项目 3:列表交错动画

// components/StaggerList.tsx
import { motion } from 'motion/react';

const stagger = {
  animate: {
    transition: {
      staggerChildren: 0.1,
    },
  },
};

const item = {
  initial: { opacity: 0, y: 20 },
  animate: { opacity: 1, y: 0 },
};

export function StaggerList({ items }: { items: any[] }) {
  return (
    <motion.ul variants={stagger} initial="initial" animate="animate">
      {items.map((item) => (
        <motion.li
          key={item.id}
          variants={item}
          className="p-4 border-b"
        >
          {item.name}
        </motion.li>
      ))}
    </motion.ul>
  );
}

项目 4:滚动驱动动画(高级)

// components/ScrollReveal.tsx
import { motion, useScroll, useTransform } from 'motion/react';

export function ScrollReveal() {
  const { scrollYProgress } = useScroll();
  
  // 滚动 0% → 100%,opacity 0 → 1,x -100 → 0
  const opacity = useTransform(scrollYProgress, [0, 0.5], [0, 1]);
  const x = useTransform(scrollYProgress, [0, 0.5], [-100, 0]);
  
  return (
    <motion.div
      style={{ opacity, x }}
      className="h-screen flex items-center justify-center"
    >
      <h1 className="text-6xl font-bold">Scroll to Reveal</h1>
    </motion.div>
  );
}
// components/ParallaxScroll.tsx
import { motion, useScroll, useTransform } from 'motion/react';
import { useRef } from 'react';

export function ParallaxScroll() {
  const ref = useRef(null);
  const { scrollYProgress } = useScroll({
    target: ref,
    offset: ['start end', 'end start'],
  });
  
  const y1 = useTransform(scrollYProgress, [0, 1], [0, -200]);
  const y2 = useTransform(scrollYProgress, [0, 1], [0, -100]);
  const y3 = useTransform(scrollYProgress, [0, 1], [0, -300]);
  
  return (
    <div ref={ref} className="h-[200vh] relative">
      <motion.div style={{ y: y1 }} className="absolute top-1/4 left-1/4">
        Layer 1
      </motion.div>
      <motion.div style={{ y: y2 }} className="absolute top-1/2 left-1/2">
        Layer 2
      </motion.div>
      <motion.div style={{ y: y3 }} className="absolute top-3/4 left-3/4">
        Layer 3
      </motion.div>
    </div>
  );
}

项目 5:拖拽 + 布局动画

// components/DragList.tsx
import { motion, Reorder } from 'motion/react';
import { useState } from 'react';

export function DragList({ initial }: { initial: string[] }) {
  const [items, setItems] = useState(initial);
  
  return (
    <Reorder.Group axis="y" values={items} onReorder={setItems}>
      {items.map((item) => (
        <Reorder.Item
          key={item}
          value={item}
          whileDrag={{ scale: 1.05, boxShadow: '0 10px 20px rgba(0,0,0,0.2)' }}
          className="p-4 mb-2 bg-white rounded shadow cursor-grab"
        >
          {item}
        </Reorder.Item>
      ))}
    </Reorder.Group>
  );
}

性能优化 5 招

1. 只动画 transform / opacity

/* ✅ GPU 加速(合成层) */
transform: translateX(100px);
opacity: 0.5;

/* ❌ 触发重排(CPU) */
left: 100px;
width: 200px;

2. useReducedMotion(无障碍)

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

function Component() {
  const shouldReduceMotion = useReducedMotion();
  
  return (
    <motion.div
      animate={{ x: shouldReduceMotion ? 0 : 100 }}
      transition={{ duration: 0.3 }}
    />
  );
}

3. will-change 提示

.animated {
  will-change: transform, opacity;  /* 提示浏览器优化 */
}

4. 离开视口停止

import { motion, useInView } from 'motion/react';

function Component() {
  const ref = useRef(null);
  const isInView = useInView(ref, { once: true });
  
  return (
    <motion.div
      ref={ref}
      initial={{ opacity: 0 }}
      animate={isInView ? { opacity: 1 } : {}}
    />
  );
}

5. Layout 动画节流

// ❌ 频繁 layout 抖动
<motion.div layout>

// ✅ 仅在必要时 layout
<motion.div layout="position">

5 个常见坑

坑 1:动画太多

❌ 每个元素都动画(视觉混乱)
✅ 仅关键元素动画(CTA / Modal / List)

坑 2:动画太慢

❌ 1 秒以上(用户等待)
✅ 200-500ms(流畅)

坑 3:忽略无障碍

// ✅ 必须尊重 prefers-reduced-motion
const shouldReduceMotion = useReducedMotion();

坑 4:动画非 transform

/* ❌ 触发重排性能差 */
transition: width 0.3s, height 0.3s;

/* ✅ 只用 transform / opacity */
transition: transform 0.3s, opacity 0.3s;

坑 5:动画阻塞滚动

// ❌ 滚动时还在动画
useEffect(() => animate(), [scroll]);

// ✅ 用 IntersectionObserver
const inView = useInView(ref);

设计令牌系统

// lib/motion-tokens.ts
export const motionTokens = {
  // 时长
  duration: {
    fast: 0.15,
    normal: 0.3,
    slow: 0.5,
  },
  
  // 缓动
  ease: {
    in: [0.4, 0, 1, 1],
    out: [0, 0, 0.2, 1],
    inOut: [0.4, 0, 0.2, 1],
    spring: [0.34, 1.56, 0.64, 1],
  },
  
  // 预设
  presets: {
    fadeIn: {
      initial: { opacity: 0 },
      animate: { opacity: 1 },
      transition: { duration: 0.3 },
    },
    slideUp: {
      initial: { y: 20, opacity: 0 },
      animate: { y: 0, opacity: 1 },
      transition: { duration: 0.3 },
    },
    scaleIn: {
      initial: { scale: 0.95, opacity: 0 },
      animate: { scale: 1, opacity: 1 },
      transition: { type: 'spring', stiffness: 300, damping: 20 },
    },
  },
};

与之前内容的关系

7/16 TypeScript       → 前端基础
7/22 React 19 RSC      → 前端框架
7/30 shadcn/ui         → UI 设计
7/31 Web 性能          → 性能优化
8/5 Motion 动画        → 交互动效  ← 今天
→ "基础 → 框架 → UI → 性能 → 动画"完整闭环

7 天落地路径

Day 1:Motion 入门

npm install motion

Day 2:基础动画

// initial / animate / exit
<motion.div initial={{...}} animate={{...}} />

Day 3:交互动画

// whileHover / whileTap / drag
<motion.button whileHover={...} whileTap={...} />

Day 4:布局动画

// layout / layoutId
<motion.div layoutId="card" />

Day 5:复杂场景

// AnimatePresence / Reorder

Day 6:性能优化

// useReducedMotion / transform / opacity

Day 7:设计系统

// motion-tokens + 预设

我的看法

动画是 2026 年 Web 产品的"差异化武器"

  1. 感知性能:用户感觉更快
  2. 品牌表达:差异化
  3. 引导注意力:聚焦核心
  4. 提升转化:CTA +10%
  5. 愉悦体验:满意度 +25%

对独立开发者的建议:

  • 必备 Motion:React 项目首选
  • View Transitions:原生页面切换
  • GSAP 备用:复杂时间线
  • Lottie 设计师:动效素材
  • AI 动画:未来趋势

参考


本文基于 Motion v12 + View Transitions API 跨浏览器,2026 年 8 月最新实战。

📚 同主题文章

🎨 前端 / Web 分类更多