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

ReactJS 结合react-use Intersection Observer实现GSAP单次入场动画

问题根源定位

按教程注释淡出逻辑不生效,和淡出代码本身没关系,核心问题出在原有判断逻辑没有状态记忆:

  1. 现有三元表达式是每次组件重渲染都会重新执行的:交叉观察器只要状态变化就会触发组件更新,只要元素交叉比例≥0.4,就会无差别执行一次fadeIn。哪怕fadeOut是空函数,高频重复调用GSAP的补间方法,会导致正在播放的动画被反复打断,视觉上出现卡顿、闪烁,看起来和没写逻辑一样。
  2. 交叉观察器的状态是随滚动连续变化的:元素在视口内移动时,交叉比例会实时波动,没有触发锁的情况下,入场逻辑会被持续反复调用。
实现方案

完全可以用if判断替代原有三元逻辑实现“首次进视口才播一次动画”的需求,核心要加一个不触发额外重渲染的触发锁,避免重复执行动画,具体写法:

  • 用useRef存储动画是否已触发的标记,相比useState,修改Ref的值不会触发组件额外重渲染,性能更好
  • 逻辑优先检查锁状态,只要动画已经触发过,后续所有交叉状态变化都直接跳过
  • 直接用useIntersection官方提供的isIntersecting属性判断元素是否满足进入视口的阈值,比手动判断intersectionRatio更可靠
完整可用代码
const App = () =>{
  const sectionRef = useRef(null);
  // 动画触发锁:标记是否已经播放入场动画
  const hasAnimatedRef = useRef(false);
  const headlineFirst = ".animate-this-div";

  const intersection = useIntersection(sectionRef, {
    root: null,
    rootMargin: "0px",
    threshold: 0.4
  });

  const fadeIn = () => {
    gsap.to(headlineFirst, 1, {
      opacity: 1,
      y: 11,
    });
  };

  // 替换原有三元判断的核心逻辑
  // 锁未触发时,才检查交叉观察器状态
  if (!hasAnimatedRef.current) {
    // 元素首次达到进入视口的阈值
    if (intersection?.isIntersecting) {
      // 先把锁标记为已触发,后续所有逻辑都会永久跳过动画执行
      hasAnimatedRef.current = true;
      // 执行一次完整的入场动画
      fadeIn();
    }
  }

  return (
    <div ref={sectionRef} className="some-div">
      <div className="animate-this-div"></div>
    </div>
  )
}
实际运行效果
  • 不需要写任何退出动画相关的逻辑,锁触发后不管元素是滑出视口还是再次滑回,都不会触发任何动画操作
  • 彻底避免了重复调用GSAP导致的补间打断问题,入场动画只会在元素首次达到视口阈值时完整播放一次

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.25 00:06:03