如何在React中实现组件切换时的平滑显隐动画?
实现组件切换的平滑淡入淡出动画
问题根源
你之前遇到的问题核心在于:当activeItem变化时,React会立即卸载旧组件并挂载新组件,导致旧组件的退出动画被中断;而CSS transition仅首次生效是因为后续切换时组件没有重新触发过渡条件(比如元素被直接移除而非样式变化)。
解决方案思路
要实现完整的"旧组件淡出→新组件淡入"效果,需要:
- 暂时保留旧组件在DOM中,直到其退出动画完成
- 让新旧组件在切换时重叠显示(避免布局跳动)
- 精确控制动画时序,确保旧组件动画结束后再卸载
完整实现代码
1. CSS动画定义
/* 容器样式:确保组件重叠 */ .content-container { position: relative; min-height: 400px; /* 根据你的内容高度调整 */ } /* 组件基础样式:绝对定位实现重叠 */ .content-item { position: absolute; top: 0; left: 0; width: 100%; } /* 淡出动画 */ .fade-out { animation: fadeOut 0.3s ease-out forwards; } /* 淡入动画 */ .fade-in { animation: fadeIn 0.3s ease-in forwards; } @keyframes fadeOut { from { opacity: 1; visibility: visible; } to { opacity: 0; visibility: hidden; } } @keyframes fadeIn { from { opacity: 0; visibility: hidden; } to { opacity: 1; visibility: visible; } }
2. React组件逻辑修改
import { useState, useEffect } from 'react'; function YourParentComponent() { const [activeItem, setActiveItem] = useState('home'); const [prevActiveItem, setPrevActiveItem] = useState(null); const [isTransitioning, setIsTransitioning] = useState(false); const ANIMATION_DURATION = 300; // 必须与CSS动画时长一致 // 处理导航切换事件 const switchContent = (newItem) => { if (newItem !== activeItem) { setIsTransitioning(true); setPrevActiveItem(activeItem); setActiveItem(newItem); } }; // 动画结束后清理旧组件 useEffect(() => { if (isTransitioning) { const timer = setTimeout(() => { setPrevActiveItem(null); setIsTransitioning(false); }, ANIMATION_DURATION); return () => clearTimeout(timer); // 组件卸载时清除定时器 } }, [isTransitioning]); // 渲染对应组件的通用函数 const renderContent = (item, isExiting) => { switch (item) { case "home": return <AppHome className={`content-item ${isExiting ? 'fade-out' : 'fade-in'}`} />; case "about-me": return <AppAboutMe className={`content-item ${isExiting ? 'fade-out' : 'fade-in'}`} />; default: return null; } }; return ( <div className="col-7 content-container"> {/* 渲染旧组件(执行淡出动画) */} {prevActiveItem && renderContent(prevActiveItem, true)} {/* 渲染新组件(执行淡入动画) */} {renderContent(activeItem, false)} {/* 示例导航按钮 */} <div className="nav-buttons"> <button onClick={() => switchContent('home')}>首页</button> <button onClick={() => switchContent('about-me')}>关于我</button> </div> </div> ); }
关键细节说明
- 重叠布局:通过
position: relative容器和position: absolute组件,让新旧组件在切换时完全重叠,避免页面跳动。 - 时序控制:使用
setTimeout等待动画完成后再卸载旧组件,确保淡出动画完整执行。 - 防重复触发:
isTransitioning状态防止用户快速点击时触发多个重叠的动画。 - 动画一致性:
ANIMATION_DURATION必须与CSS中定义的动画时长保持一致,否则会出现组件提前卸载或延迟消失的问题。
内容的提问来源于stack exchange,提问作者Alexx
相关产品推荐
相关产品推荐

