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

如何解决React动画结束后useState更新延迟引发的旧值闪现问题?

问题描述

我是React新手,正在制作一个简单动画,但动画结束后会有一瞬间显示opacity和translate的旧值,请问有没有办法消除这种现象?我尝试过使用useRef并重新渲染组件,但没有效果,请忽略timeout函数。

代码示例

JS代码

const { useEffect, useState } = React;

const Titlu = () => {
  let [opacitate, setOpacitate] = useState(0);

  let [translate, setTranslate] = useState(-30);

  function Schimba() {
    setOpacitate(prevOpacity => prevOpacity = 1);
    setTranslate(prevTranslate => prevTranslate = 20);
  }

  function timeout(timp) {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, 1000);
    })
  }

  return (
    <div className="Part1">
      <h1 className="ForkyTitlu" 
          onAnimationEnd={Schimba} 
          style={{
            opacity:opacitate, 
            transform: `translate(${translate}%, 0%)`
          }} >
        <span className ="Forky">Forky</span>
        <span className ="Nutrition">Nutrition</span>
      </h1>
    </div>
  );
}

ReactDOM
  .createRoot(root)
  .render(<Titlu />);

CSS代码

.Part1 {
  display: inline-flex;
  margin-bottom: 50vh;
  border-bottom: 3px black;
}

.ForkyTitlu {
  color: orange;
  font-size: 3vw;
  /*
    margin-left: 12vw;
    margin-top: 40vh;
    transform: translateX(-50%);
    */
  animation-name: titluAnimation;
  animation-delay: 1s;
  animation-duration: 2s;
  animation-iteration-count: 1;
  position: relative;
}

@keyframes titluAnimation {
  100% {
    opacity: 1;
    transform: translateX(20%);
  }
}

.Forky {
  color: black;
  font-size: 9vw;
  text-shadow: -30px 15px 30px #0000009c;
}

.Nutrition {
  color: orange;
  font-size: 3.5vw;
  text-shadow: -30px 15px 30px orange;
}

HTML代码

<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="root"></div>
解决方案

问题根源是CSS动画结束后会自动还原到元素的初始样式,而onAnimationEnd触发的React状态更新是异步的,两者的时间差导致了闪回现象。下面是两种解决方法:

方案1:使用CSS animation-fill-mode: forwards(推荐)

这个属性会让动画结束后,元素保持动画最后一帧的样式,彻底避免闪回,同时可以简化React代码。

修改后的CSS

.ForkyTitlu {
  color: orange;
  font-size: 3vw;
  animation-name: titluAnimation;
  animation-delay: 1s;
  animation-duration: 2s;
  animation-iteration-count: 1;
  position: relative;
  /* 添加这行,让动画结束后保留最后一帧 */
  animation-fill-mode: forwards;
  /* 补充初始样式,和动画起始状态一致 */
  opacity: 0;
  transform: translateX(-30%);
}

@keyframes titluAnimation {
  100% {
    opacity: 1;
    transform: translateX(20%);
  }
}

简化后的React代码

const { useEffect, useState } = React;

const Titlu = () => {
  // 移除不需要的状态和回调函数
  function timeout(timp) {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, 1000);
    })
  }

  return (
    <div className="Part1">
      <h1 className="ForkyTitlu">
        <span className ="Forky">Forky</span>
        <span className ="Nutrition">Nutrition</span>
      </h1>
    </div>
  );
}

ReactDOM
  .createRoot(root)
  .render(<Titlu />);

方案2:同步状态与动画逻辑(适合需要React控制样式的场景)

如果必须用React state控制样式,需要确保动画初始帧和React初始状态完全一致,同时可以通过添加类名代替行内样式更新,减少异步延迟:

  1. 在CSS中定义动画前后的类名
  2. 用React state控制类名切换,代替行内样式

不过方案1更简洁高效,优先选择前者。

内容的提问来源于stack exchange,提问作者ImaPug

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 00:05:27