You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 21:15:42