React 基于状态切换多Div组件时如何实现过渡动画
Slide切换动画实现方案
核心问题前置
你现在的代码实现是直接根据count值替换DOM节点,旧slide会被React立刻卸载,根本没有时间执行离场动画,不管选哪种实现方案,都需要解决「新旧slide重叠渲染、等离场动画执行完再卸载旧节点」的逻辑,否则只能做简单的入场效果,做不出流畅的过渡。
选型对比
- 纯CSS Transitions/Animations:适合只需要简单淡入、无方向感知的基础动画场景,不需要引入第三方依赖,不会增加打包体积
- Framer Motion:适合需要方向跟随、离场动画、手势交互的复杂场景,内置的组件可以帮你省掉大量处理动画时序的边界逻辑,开发效率更高
纯CSS实现步骤
- 给渲染容器加定位属性,避免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); } } - 渲染时给slide加唯一key,让React能准确识别新旧节点:
return ( <div className="slide-container"> <div key={count} className="slide-content"> {components[count]} </div> </div> ) - 可选增强:如果要加离场动画,需要额外维护一个临时缓存的旧count值,等动画执行完成后(比如300ms之后)再清除缓存,手写定时器判断时序。
这个方案的短板:没法自动识别前进/后退方向,写离场动画时的时序判断很容易出bug(比如快速连点按钮时,旧节点还没等播完动画就被清除了)。
Framer Motion 实现步骤(推荐复杂交互场景)
Framer Motion内置的AnimatePresence组件,专门处理组件卸载前的离场动画时序,不需要自己写定时器判断动画进度:
- 安装依赖后,改造状态逻辑,额外记录切换方向,实现方向跟随的动画:
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> ) } - 这个实现的实际效果:点Next的时候新slide从右侧滑入,旧slide同步向左滑出;点Prev的时候方向反过来,动画全程没有生硬的跳转,也不需要自己处理快速连点时的动画时序问题。如果后续要加手势滑动切页,只要给
motion.div加drag="x"属性就能直接扩展,不需要重写动画逻辑。
选型小结
- 如果只是做个简单的页面切换,没有复杂的交互需求,纯CSS的轻量方案完全够用
- 如果需要流畅的方向跟随动画、离场效果、后续要加手势交互,Framer Motion的开发成本远低于自己手写CSS动画的边界处理逻辑,更适合这类多步骤切换的场景
内容的提问来源于stack exchange,提问作者Denver Dang
相关产品推荐
相关产品推荐

