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

React实现列表垂直无缝滚动跑马灯的问题与解决

实现垂直无缝滚动的列表跑马灯

需求与问题

需要实现一个垂直自动滚动的列表跑马灯效果,要求列表滚动到末尾后无缝衔接重复显示,形成连续动画。

最初尝试通过CSS动画结合React的setState每25秒追加列表项,但实际效果是列表滚动到末尾后会跳回开头,无法实现无缝衔接,动画周期结束后会刷新重启出现断层。

初始问题代码

import styled, { keyframes } from "styled-components";
import React from "react";

// 动画关键帧
const marqueeTop = keyframes`
  0% {
    top: 0%;
  }
  100% {
    top: -100%;
  }
`;

// 样式组件
const MarqueeWrapper = styled.div`
  overflow: hidden;
  margin: 0 auto !important;
`;

const MarqueeBlock = styled.div`
  width: 100%;
  height: 44vh;
  flex: 1;
  overflow: hidden;
  box-sizing: border-box;
  position: relative;
  float: left;
`;

const MarqueeInner = styled.div`
  position: relative;
  display: inline-block;
  animation: ${marqueeTop} 25s linear infinite;
  animation-timing-function: linear;
  &:hover {
    animation-play-state: paused; /* 鼠标悬停时暂停动画 */
  }
`;

const MarqueeItem = styled.div`
  transition: all 0.2s ease-out;
`;

export default class MarqueeContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      itemsToShow: this.props.items, // 从父组件获取列表项
    };
  }

  // 追加列表项以实现重复渲染
  scrollItems = () => {
    setInterval(() => {
      this.setState((prevState) => {
        const newItems = [...prevState.itemsToShow];
        newItems.push(this.props.items);
        return { itemsToShow: newItems };
      });
    }, 25000);
  };

  componentDidMount() {
    this.scrollItems();
  }

  render() {
    return (
      <MarqueeWrapper>
        <MarqueeBlock>
          <MarqueeInner>
            {this.state.itemsToShow.map((item, index) => (
              <MarqueeItem key={index}>{item}</MarqueeItem>
            ))}
          </MarqueeInner>
        </MarqueeBlock>
      </MarqueeWrapper>
    );
  }
}

修正后的有效代码

通过提前生成多组重复列表项,并优化动画实现方式,解决了无缝衔接的问题:

import styled, { keyframes } from "styled-components";
import React from "react";

const marqueeTop = keyframes`
  0% {
    transform: translateY(0);
  }
  100% {
    transform: translateY(-100%);
  }
`;

const MarqueeWrapper = styled.div`
  overflow: hidden;
  margin: 0 auto !important;
`;

const MarqueeBlock = styled.div`
  width: 100%;
  height: 44vh;
  flex: 1;
  overflow: hidden;
  box-sizing: border-box;
  position: relative;
  float: left;
`;

const MarqueeInner = styled.div`
  position: relative;
  display: inline-block;
  animation: ${marqueeTop} 120s linear infinite;
  animation-timing-function: linear;
  &:hover {
    animation-play-state: paused;
  }
`;

const MarqueeItem = styled.div`
  transition: all 1s ease-out;
`;

export default class MarqueeContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      itemsToShow: this.generateMarqueeItems(),
    };
  }

  scrollItems = () => {
    setInterval(() => {
      this.setState({ itemsToShow: this.generateMarqueeItems() });
    }, 120000);
  };

  componentDidMount() {
    this.scrollItems();
  }

  generateMarqueeItems = () => {
    const items = this.props.items;
    const repeatCount = 5;
    const marqueeItems = Array.from({ length: repeatCount }, () => items).flat();

    return marqueeItems;
  };

  render() {
    return (
      <MarqueeWrapper>
        <MarqueeBlock>
          <MarqueeInner>{this.state.itemsToShow && this.state.itemsToShow.map((item, index) => <MarqueeItem key={index}>{item}</MarqueeItem>)}</MarqueeInner>
        </MarqueeBlock>
      </MarqueeWrapper>
    );
  }
}

核心优化点

  • 改用transform: translateY实现动画,避免top定位带来的高度计算误差,动画更流畅
  • 提前生成多组(示例为5组)重复的列表项,让动画滚动到末尾时,后续内容与开头完全一致,视觉上形成无缝衔接
  • 调整动画时长至120秒,配合多组列表,确保滚动过程中不会提前到达内容末尾
  • 定时刷新时重新生成多组列表,而非持续追加,避免DOM节点无限增长

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 14:15:58