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

React电商网站滚动缩放动画优化方案咨询

电商网站React滚动动画优化方案

问题背景

我正在开发一个带动画效果的电商网站(第一个React项目),需求是:向下滚动时图片缩小且header固定,向上滚动时图片放大。当前代码能运行,但存在两个问题:

  1. 频繁更新状态可能影响应用性能
  2. 使用document.body.style.overflow = "hidden"会导致其他页面板块无法正常滚动

当前实现代码

React组件代码

const Header = () => {
  
  const [count, setCount] = useState(0);
  const [direction, setDirection] = useState('down');
  const [isVisible, setIsVisible] = useState(false);
  const header = useRef(null);

  const callbackFunction = (entries) => {
    const [entry] = entries;
    setIsVisible(entry.isIntersecting);
  };

  const options = {
    root: null,
    rootMargin: "0px",
    threshold: 1.0
  };

  useEffect(() => {
    const observer = new IntersectionObserver(callbackFunction, options);
    if (header.current) observer.observe(header.current);

    return () => {
      if(header.current) observer.unobserve(header.current);
    };
  }, [header, options]);


  const handleDirection = (event) => {
    if (isVisible) {
      const value = event.deltaY;
      if (value > 0 && count <= 6) {
        setCount((count) => count + 1);
        setDirection((direction) => direction = 'down');
      } else if (value < 0 && count >= 1) {
        setCount((count) => count - 1);
        setDirection((direction) => direction = 'up');
      }
    } 
  };

  if (isVisible && count < 6) {
    document.body.style.overflow = 'hidden';
  } else if (isVisible && count === 7) {
    document.body.style.overflow = '';
  }

  console.log(count);

  return (
    <>
      <div className="img-container" 
        style={{ animation: `${direction}Scale${count} 0.2s ease forwards`}}>
        <img src={require('../img/header-img (3).jpg')} alt="Skater" />
      </div>
      <OverlayHero />

      <div className="background-text-container">
        <span className="scrolling-texts">* WE ALWAYS CHECK THE QUALITY *<span className="hollow-texts"> WE ALWAYS CHECK THE QUALITY * </span>* ART ON MAPLE MATERPIECES *</span>
        <span className="scrolling-texts"><span className="hollow-texts"> RECYCLED MATERIAL * </span>* RECYCLED MATERIALS *<span className="hollow-texts"> RECYCLED MATERIAL * </span></span>
        <span className="scrolling-texts">* ART ON MAPLE MATERPIECES * ART ON MAPLE MASTERPIECES * * ART ON MAPLE MATERPIECES *</span>
      </div>
    </>
  );
};

CSS关键帧示例

@keyframes upScale1 {
    from {
        width: 90vw;
        height: 98vh;  
    }
    to {
        width: 100vw;
        height: 100vh;  
    }
}

解决方案与优化建议

1. 修复滚动锁定范围问题

不要直接修改body的overflow,而是给header的外层容器添加滚动锁定,仅作用于当前板块:

  • 给header容器添加ref:
const headerWrapperRef = useRef(null);
  • 用className切换替代直接操作DOM样式:
.header-wrapper.scroll-locked {
  overflow: hidden;
}
  • 在组件中控制className:
<div ref={headerWrapperRef} className={`header-wrapper ${isVisible && count < 6 ? 'scroll-locked' : ''}`}>
  {/* 原有的img-container、OverlayHero等内容 */}
</div>

这样滚动锁定只会影响header板块,不会干扰页面其他部分。

2. 性能优化:减少状态更新次数

将count和direction合并为一个状态对象,每次滚动仅触发一次状态更新:

const [scrollState, setScrollState] = useState({ count: 0, direction: 'down' });

const handleDirection = (event) => {
  if (!isVisible) return;
  const value = event.deltaY;
  setScrollState(prev => {
    if (value > 0 && prev.count <= 6) {
      return { ...prev, count: prev.count + 1, direction: 'down' };
    } else if (value < 0 && prev.count >= 1) {
      return { ...prev, count: prev.count - 1, direction: 'up' };
    }
    return prev;
  });
};

后续使用时通过scrollState.count和scrollState.direction获取值。

3. 替换冗余关键帧:用CSS变量实现动态缩放

放弃14个冗余关键帧,改用CSS变量+过渡动画,维护更简单且性能更好:

  • 定义CSS基础样式:
.img-container {
  width: var(--scale-width, 100vw);
  height: var(--scale-height, 100vh);
  transition: width 0.2s ease, height 0.2s ease;
}
  • 在组件中动态计算CSS变量:
const getScaleStyles = () => {
  // 根据count线性计算尺寸,count从0到6对应从100%缩小到90vw/98vh
  const width = 100 - (scrollState.count * (10 / 6));
  const height = 100 - (scrollState.count * (2 / 6));
  return {
    '--scale-width': `${width}vw`,
    '--scale-height': `${height}vh`
  };
};
  • 应用到容器上:
<div className="img-container" style={getScaleStyles()}>
  <img src={require('../img/header-img (3).jpg')} alt="Skater" />
</div>

4. 节流滚动事件,降低触发频率

滚动事件触发极频繁,用节流函数减少handleDirection的调用次数:

const throttle = (func, delay) => {
  let lastInvoke = 0;
  return (...args) => {
    const now = Date.now();
    if (now - lastInvoke >= delay) {
      func(...args);
      lastInvoke = now;
    }
  };
};

// 绑定节流后的滚动事件
useEffect(() => {
  const throttledHandle = throttle(handleDirection, 100);
  window.addEventListener('wheel', throttledHandle);
  return () => window.removeEventListener('wheel', throttledHandle);
}, [isVisible, scrollState]);

5. 其他细节优化

  • 用useCallback包裹回调函数,避免每次渲染创建新函数:
const callbackFunction = useCallback((entries) => {
  const [entry] = entries;
  setIsVisible(entry.isIntersecting);
}, []);

const handleDirection = useCallback(throttle((event) => {
  // 原逻辑
}, 100), [isVisible, scrollState]);
  • 图片优化:使用loading="lazy"预加载图片,或转换为WebP格式减小体积,避免动画卡顿。

学习资料方向

  • React官方文档:重点掌握useState、useCallback、useEffect的性能优化用法
  • CSS动画指南:深入了解CSS变量、transition的性能特性,避免不必要的重绘重排
  • 事件优化:学习节流、防抖的原理与实际应用场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 00:50:24