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

React项目横向滚动Banner:自动动画与双向手动滚动兼容问题

React 横向Banner:自动滚动+双向手动滚动实现方案

问题根源

你遇到的核心问题是CSS transform动画和容器scrollLeft是两套独立的位置控制逻辑:

  • 原方案用translateX动画把整个Banner元素平移,元素本身并没有在容器内产生滚动;
  • 手动滚动修改的是容器的scrollLeft,和transform的偏移完全无关,自然拉不回已经被移走的内容。

解决思路

放弃CSS transform动画,改用JS控制容器的scrollLeft属性实现自动滚动——这样自动滚动和手动滚动操作的是同一个属性,就能实现双向自由滚动了。

完整实现代码

import { useRef, useEffect } from 'react';

const AutoScrollBanner = () => {
  const bannerContainerRef = useRef(null);
  const scrollTimerRef = useRef(null);
  // 自动滚动速度(数值越大越快)
  const SCROLL_SPEED = 2;
  // 滚动间隔(毫秒,越小越流畅)
  const SCROLL_INTERVAL = 20;

  // 启动自动滚动
  const startAutoScroll = () => {
    if (scrollTimerRef.current) return;
    scrollTimerRef.current = setInterval(() => {
      const container = bannerContainerRef.current;
      if (!container) return;

      // 滚动到末尾后重置,实现循环滚动
      if (container.scrollLeft >= container.scrollWidth - container.clientWidth) {
        container.scrollLeft = 0;
      } else {
        container.scrollLeft += SCROLL_SPEED;
      }
    }, SCROLL_INTERVAL);
  };

  // 停止自动滚动
  const stopAutoScroll = () => {
    if (scrollTimerRef.current) {
      clearInterval(scrollTimerRef.current);
      scrollTimerRef.current = null;
    }
  };

  // 组件初始化启动滚动,卸载时清理定时器
  useEffect(() => {
    startAutoScroll();
    return () => stopAutoScroll();
  }, []);

  // 手动滚动逻辑
  const handleWheel = (event) => {
    event.preventDefault();
    const container = bannerContainerRef.current;
    if (!container) return;

    // 暂停自动滚动,避免和手动操作冲突
    stopAutoScroll();
    // 根据滚轮方向调整scrollLeft
    container.scrollLeft += event.deltaY;
  };

  return (
    <div
      ref={bannerContainerRef}
      className="banner-container"
      onMouseEnter={stopAutoScroll}
      onMouseLeave={startAutoScroll}
      onWheel={handleWheel}
      style={{
        overflowX: 'auto',
        whiteSpace: 'nowrap',
        width: '100%',
        // 可选:隐藏默认滚动条
        scrollbarWidth: 'none',
        MsOverflowStyle: 'none'
      }}
    >
      {/* Banner内容:需设置inline-block保持横向排列 */}
      <div className="banner-item" style={{ display: 'inline-block', width: '250px', height: '120px', margin: '0 15px', backgroundColor: '#f5f5f5', textAlign: 'center', lineHeight: '120px' }}>Banner内容1</div>
      <div className="banner-item" style={{ display: 'inline-block', width: '250px', height: '120px', margin: '0 15px', backgroundColor: '#f5f5f5', textAlign: 'center', lineHeight: '120px' }}>Banner内容2</div>
      <div className="banner-item" style={{ display: 'inline-block', width: '250px', height: '120px', margin: '0 15px', backgroundColor: '#f5f5f5', textAlign: 'center', lineHeight: '120px' }}>Banner内容3</div>
      <div className="banner-item" style={{ display: 'inline-block', width: '250px', height: '120px', margin: '0 15px', backgroundColor: '#f5f5f5', textAlign: 'center', lineHeight: '120px' }}>Banner内容4</div>
      <div className="banner-item" style={{ display: 'inline-block', width: '250px', height: '120px', margin: '0 15px', backgroundColor: '#f5f5f5', textAlign: 'center', lineHeight: '120px' }}>Banner内容5</div>
    </div>
  );
};

export default AutoScrollBanner;

关键说明

  • 容器样式:设置overflowX: auto开启横向滚动,whiteSpace: nowrap让子元素横向排列;
  • 自动滚动逻辑:通过定时器循环修改scrollLeft,滚动到容器末尾时重置为0,实现无限循环;
  • 悬停交互:鼠标进入容器时停止自动滚动,离开时恢复,避免手动操作和自动滚动冲突;
  • 双向手动滚动:直接通过滚轮事件修改scrollLeft,因为和自动滚动共用同一个属性,所以左右滚动都能正常显示内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 07:37:08