React中菜单展开与收起的CSS Transition失效问题求助
修复菜单收起无动画的问题
你遇到的问题核心是:当isOpen变为false时,MenuBody被直接从DOM中移除,动画还没执行就消失了。下面给两种可行的修复方案:
方案一:用CSS Transition + 类切换(最简单推荐)
不用卸载组件,通过切换类来控制展开/收起的双向动画:
React代码调整
const [isOpen, setIsOpen] = useState(false); return ( <> <button onClick={() => setIsOpen(!isOpen)}>open menu</button> {/* 用类控制状态,不再直接卸载组件 */} <MenuBody isOpen={isOpen} /> </> ); const MenuBody = ({ isOpen }) => { return ( <div className={`${styles.menuBody} ${isOpen ? styles.open : ''}`}> {/* 菜单内容 */} </div> ); };
CSS代码替换
删掉原来的keyframes,用transition实现双向过渡:
.menuBody { /* 收起状态的初始样式 */ opacity: 0; visibility: hidden; /* 防止收起后误点 */ transition: opacity 0.5s ease-in-out, visibility 0.5s ease-in-out; /* 你的其他原有样式 */ } .menuBody.open { /* 展开状态 */ opacity: 1; visibility: visible; }
方案二:延迟卸载组件(适合必须卸载的场景)
如果一定要在收起后卸载组件,就监听动画结束事件,等动画跑完再移除:
React代码调整
import { useState, useEffect, useRef, forwardRef } from 'react'; const [isOpen, setIsOpen] = useState(false); const [showMenu, setShowMenu] = useState(false); const menuRef = useRef(null); useEffect(() => { if (isOpen) { // 展开时直接显示 setShowMenu(true); } else { // 收起时先触发动画,结束后再隐藏 if (menuRef.current) { const onAnimationEnd = () => { setShowMenu(false); menuRef.current.removeEventListener('animationend', onAnimationEnd); }; menuRef.current.addEventListener('animationend', onAnimationEnd); } } }, [isOpen]); return ( <> <button onClick={() => setIsOpen(!isOpen)}>open menu</button> {showMenu && <MenuBody ref={menuRef} isClosing={!isOpen} />} </> ); // 用forwardRef传递ref到内部元素 const MenuBody = forwardRef(({ isClosing }, ref) => { return ( <div ref={ref} className={`${styles.menuBody} ${isClosing ? styles.closing : ''}`} > {/* 菜单内容 */} </div> ); });
CSS代码补充
添加收起的动画:
/* 展开动画 */ @keyframes menuOpen { 0% { opacity: 0; } 100% { opacity: 1; } } /* 收起动画 */ @keyframes menuClose { 0% { opacity: 1; } 100% { opacity: 0; } } .menuBody { /* 原有样式 */ } .menuBody:not(.closing) { animation: menuOpen 0.5s ease-in-out forwards; } .menuBody.closing { animation: menuClose 0.5s ease-in-out forwards; }
注意点
- 方案一里的
visibility是必须的,不然菜单收起后还是能被点击到。 - 方案二要记得移除事件监听器,避免内存泄漏。
内容的提问来源于stack exchange,提问作者HardRock
相关产品推荐
相关产品推荐

