You need to enable JavaScript to run this app. React 基于状态切换多Div组件时如何实现过渡动画
优惠活动
大模型
产品
解决方案
定价
更多

React 基于状态切换多Div组件时如何实现过渡动画

Slide切换动画实现方案

核心问题前置

你现在的代码实现是直接根据count值替换DOM节点,旧slide会被React立刻卸载,根本没有时间执行离场动画,不管选哪种实现方案,都需要解决「新旧slide重叠渲染、等离场动画执行完再卸载旧节点」的逻辑,否则只能做简单的入场效果,做不出流畅的过渡。

选型对比

  • 纯CSS Transitions/Animations:适合只需要简单淡入、无方向感知的基础动画场景,不需要引入第三方依赖,不会增加打包体积
  • Framer Motion:适合需要方向跟随、离场动画、手势交互的复杂场景,内置的组件可以帮你省掉大量处理动画时序的边界逻辑,开发效率更高

纯CSS实现步骤

  1. 给渲染容器加定位属性,避免slide切换时布局跳动:
    .slide-container {
      position: relative;
      width: 400px;
      height: 300px;
      overflow: hidden;
    }
    .slide-content {
      position: absolute;
      inset: 0;
      animation: fadeIn 0.3s ease forwards;
    }
    @keyframes fadeIn {
      from { opacity: 0; transform: translateX(20px); }
      to { opacity: 1; transform: translateX(0); }
    }
    
  2. 渲染时给slide加唯一key,让React能准确识别新旧节点:
    return (
      <div className="slide-container">
        <div key={count} className="slide-content">
          {components[count]}
        </div>
      </div>
    )
    
  3. 可选增强:如果要加离场动画,需要额外维护一个临时缓存的旧count值,等动画执行完成后(比如300ms之后)再清除缓存,手写定时器判断时序。

这个方案的短板:没法自动识别前进/后退方向,写离场动画时的时序判断很容易出bug(比如快速连点按钮时,旧节点还没等播完动画就被清除了)。

Framer Motion 实现步骤(推荐复杂交互场景)

Framer Motion内置的AnimatePresence组件,专门处理组件卸载前的离场动画时序,不需要自己写定时器判断动画进度:

  1. 安装依赖后,改造状态逻辑,额外记录切换方向,实现方向跟随的动画:
    import { useState } from 'react'
    import { AnimatePresence, motion } from 'framer-motion'
    
    const App = props => {
      const [currentCount, setCurrentCount] = useState(0)
      // 记录切换方向:1=前进,-1=后退
      const [direction, setDirection] = useState(0)
    
      const components = [
        // 保留你原来的三个slide内容
      ]
    
      // 动画规则配置
      const slideVariants = {
        enter: (dir) => ({
          x: dir > 0 ? 300 : -300,
          opacity: 0
        }),
        center: {
          x: 0,
          opacity: 1
        },
        exit: (dir) => ({
          x: dir > 0 ? -300 : 300,
          opacity: 0
        })
      }
    
      // 切换方法
      const handleNext = () => {
        if (currentCount >= components.length -1) return
        setDirection(1)
        setCurrentCount(prev => prev +1)
      }
      const handlePrev = () => {
        if (currentCount <= 0) return
        setDirection(-1)
        setCurrentCount(prev => prev -1)
      }
    
      return (
        <div style={{position: 'relative', width: 400, height: 300, overflow: 'hidden'}}>
          <AnimatePresence initial={false} custom={direction}>
            <motion.div
              key={currentCount}
              custom={direction}
              variants={slideVariants}
              initial="enter"
              animate="center"
              exit="exit"
              transition={{duration: 0.3, ease: 'easeOut'}}
            >
              {/* 把原来的slide按钮事件替换成上面的handleNext/handlePrev就行 */}
              {components[currentCount]}
            </motion.div>
          </AnimatePresence>
        </div>
      )
    }
    
  2. 这个实现的实际效果:点Next的时候新slide从右侧滑入,旧slide同步向左滑出;点Prev的时候方向反过来,动画全程没有生硬的跳转,也不需要自己处理快速连点时的动画时序问题。如果后续要加手势滑动切页,只要给motion.div加drag="x"属性就能直接扩展,不需要重写动画逻辑。

选型小结

  • 如果只是做个简单的页面切换,没有复杂的交互需求,纯CSS的轻量方案完全够用
  • 如果需要流畅的方向跟随动画、离场效果、后续要加手势交互,Framer Motion的开发成本远低于自己手写CSS动画的边界处理逻辑,更适合这类多步骤切换的场景

内容的提问来源于stack exchange,提问作者Denver Dang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.05 08:24:03