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

requestAnimationFrame导致自定义光标CSS过渡卡顿问题求助

解决React中requestAnimationFrame导致自定义光标过渡卡顿的问题

核心解决方案

  1. 用useRef替代useState存储光标坐标:避免鼠标移动时频繁触发组件重渲染,减少主线程负载
  2. 将光标位置更新纳入requestAnimationFrame调度:让光标动画与滚动动画同帧执行,避免帧冲突
  3. 优化光标CSS的合成层策略:确保光标在独立合成层渲染,减少重绘重排

修改后的代码

React组件代码

function App() {
  // 用useRef存光标位置,避免触发重渲染
  const cursorRef = useRef({ x: 0, y: 0 });
  const cursorElRef = useRef(null);
  const [mouseIn, setMouseIn] = useState(true);
  const scrollWrapRef = useRef(null);
  const contentRef = useRef(null);
 
  let offset = 0;
  let currentPos = 0;

  useEffect(() => {
    const mouse = (e) => {
      // 直接更新ref值,不触发重渲染
      cursorRef.current.x = e.clientX;
      cursorRef.current.y = e.clientY;
    };

    window.addEventListener("mousemove", mouse);

    const scrollWrap = scrollWrapRef.current;
    const content = contentRef.current;
    const cursorEl = cursorElRef.current;

    const updateAnimations = () => {
      // 滚动平滑动画
      offset += (window.pageYOffset - offset) * 0.04;
      scrollWrap.style.transform = `translateY(-${offset}px) translateZ(0)`;

      // 内容扭曲动画
      const newPos = window.pageYOffset;
      const diff = newPos - currentPos;
      const speed = diff * 0.6;
      content.style.transform = `skewY(${speed}deg)`;
      currentPos = newPos;

      // 光标位置更新,和其他动画同帧执行
      cursorEl.style.left = `${cursorRef.current.x}px`;
      cursorEl.style.top = `${cursorRef.current.y}px`;

      requestAnimationFrame(updateAnimations);
    };

    updateAnimations();

    return () => {
      window.removeEventListener("mousemove", mouse);
    };
  }, []);

  return (
    <div className="App ">
       {/* custom cursor */}
        <div
          ref={cursorElRef}
          className="cursor"
        ></div>

      <div ref={scrollWrapRef}>
        <div ref={contentRef} className="section">
              <Intro />
              <About />
              <Projects />
              <Work />
              <Contact />
        </div>
      </div>
    </div>
  );
}

优化后的CSS代码

/* custom cursor */
.cursor {
  width: 20px;
  height: 20px;
  background: rgb(255, 255, 255);
  position: fixed;
  border-radius: 50%;
  mix-blend-mode: difference;
  pointer-events: none;
  z-index: 100;
  transform: translate(-50%, -50%);
  transition: 0.15s linear, transform 0.8s ease-in-out;
  /* 强制光标进入合成层,避免重绘 */
  will-change: left, top;
  backface-visibility: hidden;
}

/* Scroll animation */
.section {
  position: relative;
  transform-style: preserve-3d;
  transform-origin: 0% 0%;
  transition: transform 0.25s;
  will-change: transform;
}

问题根源分析

  1. 原代码中useState触发频繁重渲染:每次鼠标移动调用setCursorX/setCursorY都会触发App组件重渲染,加上两个独立的requestAnimationFrame循环不断修改DOM样式,导致主线程过载,CSS过渡动画被打断。
  2. 动画调度不同步:光标过渡是CSS自动调度,而滚动动画是JS的RAF调度,两者帧时序不一致,导致视觉上的卡顿。
  3. 合成层未优化:光标默认可能不在独立合成层,每次位置更新都会触发页面重绘,加重性能负担。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 16:45:23