React 中实现动画的两种方式:CSS Transition 与 Framer Motion 的对比实践

356 阅读4分钟

在现代 Web 开发中,动画不仅是提升用户体验的重要手段,更是增强用户交互、引导用户行为、提升界面美感的关键因素。React 本身不直接提供动画功能,但通过 CSS 的 transition 和第三方动画库如 Framer Motion,我们可以轻松实现丰富的动画效果。

本文将通过两个例子,讲解一下如何在 React 中使用 CSS transition 和 Framer Motion 实现动画,并对比它们的优缺点,帮助我们在项目中做出合适的技术选型。


一、使用 CSS transition 实现动画

1.1 示例代码解析

我们先来看一个简单的动画组件,实现一个点击按钮后展开/收起的盒子效果。

// Box.jsx
import styles from './box.module.styl';
import { useState } from 'react';

const Box = () => {
    const [open, setOpen] = useState(false);
    return (
        <div>
            <button onClick={() => setOpen(!open)}>
                {open ? 'Close' : 'Open'}
            </button>
            <div className={`${styles.box} ${open ? styles.open : ''}`}></div>
        </div>
    );
};

export default Box;

对应的 box.module.styl 文件(使用 Stylus):

.box
    width: 100px
    height: 0
    background-color: lightblue
    transition: height 0.3s ease
    overflow: hidden

.box.open
    height: 100px

动画展示:

css-transition.gif

1.2 工作原理

  • .box 初始高度为 0
  • 当点击按钮时,open 状态改变,组件的类名变为 .box.open
  • .box.open 的高度变为 100px,通过 transition 属性,高度变化会以 0.3 秒的动画形式完成。

1.3 优点与限制

特性说明
简单易用适用于简单的属性变化动画,如颜色、大小、位置等
性能良好浏览器对 CSS 动画进行了优化
无需依赖不需要引入额外库
控制力弱难以实现复杂动画逻辑
状态管理繁琐需要手动管理类名和状态切换

二、使用 Framer Motion 实现动画

2.1 安装 Framer Motion

在使用 Framer Motion 之前,需要先安装它。小编使用的是pnpm,可以使用以下命令安装:

pnpm install framer-motion

安装完成后,我们就可以在 React 组件中导入 motion 组件并开始使用它了。


2.2 示例代码解析

下面是一个使用 Framer Motion 实现的动画组件,展示点击按钮后内容从上方向下淡入的动画效果:

// MotionBox.jsx
import { motion } from 'framer-motion'
import { useState } from 'react'

const MotionBox = () => {
    const [isOpen, setIsOpen] = useState(false)

    return (
        <div>
            <button onClick={() => setIsOpen(!isOpen)}>
                {isOpen ? '隐藏内容' : '显示内容'}
            </button>
            <motion.div
                initial={{ opacity: 0, y: -50 }}
                animate={{ opacity: isOpen ? 1 : 0, y: isOpen ? 0 : -50 }}
                transition={{ duration: 0.5 }}
                style={{
                    backgroundColor: 'skyblue',
                    padding: 20,
                    marginTop: 10,
                    pointerEvents: 'none' // 防止动画区域被误点
                }}
            >
                <h2>Motion Box</h2>
                <p>这是一个使用 Framer Motion 的动画组件</p>
            </motion.div>
        </div>
    )
}

export default MotionBox

动画展示

framermotion.gif

2.3 工作原理

  • initial: 定义组件初始状态,如透明度为 0,垂直方向向上偏移 50px。
  • animate: 根据 isOpen 状态变化定义动画的目标状态。当 isOpentrue 时,组件显示为不透明并回到原位置;为 false 时则隐藏。
  • transition: 定义动画过渡的持续时间(0.5 秒)和缓动函数(默认 ease),控制动画播放的节奏。
  • motion.div: 是 Framer Motion 提供的动画化组件,自动处理从初始状态到目标状态的插值与渲染过程。

2.4 优点与限制

特性说明
动画控制能力强支持延迟、循环、组合动画等高级动画逻辑
内置手势支持支持拖拽、点击、悬停等交互事件
性能优化良好基于 requestAnimationFrame,支持硬件加速
丰富的 API支持 TypeScript、React Hooks,生态完善
引入依赖需要引入第三方库,增加项目体积
学习成本略高需要一定时间学习其 API 和动画控制方式

三、CSS Transition 与 Framer Motion 对比总结

特性CSS TransitionFramer Motion
动画类型属性变化动画支持路径、物理模拟、组合动画
控制能力依赖类名和状态切换支持编程控制(暂停、重放、中断)
性能表现一般良好优化良好,支持硬件加速
学习成本中等
适用场景简单 UI 状态变化复杂交互动画、动效设计
依赖第三方库

四、使用选择建议

  • 简单 UI 动画(如按钮悬停、菜单展开):使用 CSS transition,轻量且易于实现。
  • 复杂交互动画(如模态框、页面切换、拖拽效果):使用 Framer Motion,其强大的控制能力和丰富的 API 能满足大多数复杂动画需求。
  • 混合使用:可以在项目中同时使用两者,CSS 负责基础动画,Framer Motion 负责高级动效。

五、传送门