React中onClick触发CSS动画异常:可切换侧边栏动画仅生效一次的解决方法
问题描述
我想要创建一个带有动画效果的可切换侧边栏。我在CSS中定义了动画,并通过React的useState值的条件运算符来添加动画类。点击BLUE元素会改变useState的值,相关代码如下:
function App() { const [visible, setVisible] = useState(false); const show = () => { setVisible(true); }; const hide = () => { setVisible(false); }; return ( <> <div className="container"> <div className="red-box"></div> <div className={"blue-box " + (visible ? "expandin" : "expandout")}> <div onClick={visible ? hide : show} className={"" + (visible ? "slidein" : "slideout")} > BLUE </div> </div> </div> </> ); }
目前遇到的问题是:动画仅在页面加载时触发一次,后续点击按钮时元素会瞬间移动,而非平滑过渡。我需要实现页面加载时不自动触发动画,点击后能正确播放正向或反向动画。我了解到不能直接切换动画类,需要先进行某种重置,但不知道具体如何操作,请问正确的实现方式是什么?
解决方案
核心问题在于直接切换动画类时,React的DOM更新会让浏览器无法识别状态变化来重新触发动画,且初始加载时动画类已存在导致自动触发。以下是两种可靠的实现方式:
方式一:利用setTimeout重置动画类
先移除所有动画类,等待浏览器完成重绘后再添加目标动画类,让浏览器重新识别动画触发:
function App() { const [visible, setVisible] = useState(false); const [animationClass, setAnimationClass] = useState(""); const toggleSidebar = () => { // 先清空动画类,触发DOM重绘 setAnimationClass(""); // 等待浏览器重绘完成后添加目标动画类 setTimeout(() => { setVisible(!visible); setAnimationClass(visible ? "expandout slideout" : "expandin slidein"); }, 10); }; return ( <> <div className="container"> <div className="red-box"></div> <div className={`blue-box ${animationClass.split(" ")[0]}`}> <div onClick={toggleSidebar} className={animationClass.split(" ")[1]}> BLUE </div> </div> </div> </> ); }
同时调整CSS,确保初始状态无动画类,避免页面加载时自动触发:
/* 初始状态基础样式 */ .blue-box { width: 50px; } .blue-box .toggle-btn { transform: translateX(0); } /* 展开动画 */ .expandin { animation: expandin 0.3s forwards; } .slidein { animation: slidein 0.3s forwards; } /* 收起动画 */ .expandout { animation: expandout 0.3s forwards; } .slideout { animation: slideout 0.3s forwards; } /* 动画关键帧保持原有定义 */ @keyframes expandin { from { width: 50px; } to { width: 200px; } } @keyframes slidein { from { transform: translateX(0); } to { transform: translateX(150px); } } @keyframes expandout { from { width: 200px; } to { width: 50px; } } @keyframes slideout { from { transform: translateX(150px); } to { transform: translateX(0); } }
方式二:用CSS transition替代animation(更简洁)
如果是简单的状态过渡,transition比animation更易维护,无需重置类:
function App() { const [visible, setVisible] = useState(false); return ( <> <div className="container"> <div className="red-box"></div> <div className={`blue-box ${visible ? 'expanded' : ''}`}> <div onClick={() => setVisible(!visible)} className="toggle-btn" > BLUE </div> </div> </div> </> ); }
对应的CSS:
.blue-box { width: 50px; transition: width 0.3s ease; /* 定义过渡属性及时长 */ } .blue-box.expanded { width: 200px; } .toggle-btn { transform: translateX(0); transition: transform 0.3s ease; } .blue-box.expanded .toggle-btn { transform: translateX(150px); }
这种方式下,初始状态无过渡触发条件,页面加载时不会动,点击切换类时浏览器会自动处理平滑过渡,无需额外重置逻辑。
内容的提问来源于stack exchange,提问作者Deepwalker
相关产品推荐
相关产品推荐

