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

JavaScript双向Marquee动画循环复位时闪烁故障求助

双向Marquee跑马灯动画闪烁回跳问题

我正在用JavaScript类实现双向跑马灯效果,通过CSS的transform属性结合translate3d()方法,根据类实例配置控制容器左右平移。核心动画逻辑如下:

const moveReel = () => {
    if (this.direction === "left") {
      currentPosition -= this.speed;
      if (currentPosition < -totalWidth - 1) {
        currentPosition = 0;
      }
    } else {
      currentPosition += this.speed;
      if (currentPosition > 1) {
        currentPosition = -totalWidth;
      }
    }

    wrapper.style.transform = `translate3d(${currentPosition}px, 0, 0)`;
    requestAnimationFrame(moveReel);
};

当currentPosition被设置为0时,动画出现闪烁故障,内容会回跳1-2像素。我尝试过将currentPosition改为-0.1这类小负数,也调整了速度参数,但都没解决问题,而且速度越快故障越明显。

以下是完整代码:

Reeller JavaScript类

class Reeller {
  constructor(options) {
    this.containers = document.querySelectorAll(options.container);
    this.speed = options.speed || 1;
    this.direction = options.reversed ? "right" : "left"; // 默认向左

    this.containers.forEach((container) => {
      let wrapper = container.querySelector(options.wrapper);
      let items = wrapper.querySelectorAll(options.itemSelector);
      // 计算单个元素宽度(含margin-right)
      const itemWidth =
        items[0].offsetWidth +
        parseFloat(getComputedStyle(items[0]).marginRight);

      // 计算所有元素总宽度
      const totalWidth = itemWidth * items.length;

      // 克隆元素并添加到wrapper中
      const cloneItems = [];
      let count = 10;
      while (count--) {
        for (let j = 0; j < items.length; j++) {
          const clonedItem = items[j].cloneNode(true);
          cloneItems.push(clonedItem);
          // prepend()没用,之前的蠢想法
          wrapper.appendChild(clonedItem);
        }
      }

      // 设置wrapper宽度以容纳所有元素
      wrapper.style.width = totalWidth + "px";

      // 初始化交叉观察器,检测最后一个克隆元素是否进入视口
      const observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          if (
            entry.isIntersecting &&
            entry.target === cloneItems[cloneItems.length - 1]
          ) {
            // 移除所有克隆元素
            cloneItems.forEach((item) => item.remove());
            cloneItems.length = 0;

            // 将wrapper位置重置为初始状态
            wrapper.style.transform = `translate3d(${-totalWidth}px, 0, 0)`;
          }
        });
      });

      // 观察最后一个克隆元素
      observer.observe(cloneItems[cloneItems.length - 1]);

      // 动画逻辑
      let currentPosition = -totalWidth;
      const moveReel = () => {
        if (this.direction === "left") {
          currentPosition -= this.speed;
          if (currentPosition < -totalWidth - 1) {
            currentPosition = 0;
          }
        } else {
          currentPosition += this.speed;
          if (currentPosition > 1) {
            currentPosition = -totalWidth;
          }
        }

        wrapper.style.transform = `translate3d(${currentPosition}px, 0, 0)`;
        requestAnimationFrame(moveReel);
      };

      requestAnimationFrame(moveReel);
    });
  }
}

HTML代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .my__reel {
        width: 100%;
        overflow: hidden;
      }

      .my__reel__wrap {
        display: flex;
        padding: 20px;
      }

      .my__reel__item {
        flex: 0 0 auto;
        min-width: 100px;
        height: auto;
        margin-right: 40px;
      }

      .h1 {
        font-size: 50px;
        font-weight: 200;
      }

      .my-reel-item-2 {
        background: rgb(222, 231, 255);
        padding: 5px 20px;
        border-radius: 20px;
      }
    </style>
  </head>
  <body>

    <!-- 第一个跑马灯 -->
    <div class="my__reel my-reel-1">
      <div class="my__reel__wrap my-reel-wrap-1">
        <div class="my__reel__item my-reel-item-1">
          <h1>REEEEEL</h1>
        </div>
      </div>
    </div>

    <!-- 第二个跑马灯,故障更明显 -->
    <div class="my__reel my-reel-2">
      <div class="my__reel__wrap my-reel-wrap-2">
        <div class="my__reel__item my-reel-item-2">
          <h1>REEEEEL</h1>
        </div>
        <div class="my__reel__item my-reel-item-2">
          <h1>WOW</h1>
        </div>
        <div class="my__reel__item my-reel-item-2">
          <h1>AHHHHHHH</h1>
        </div>
      </div>
    </div>

    <!-- JavaScript -->
    <script src="reeller.js"></script>
    <script>
      new Reeller({
        container: ".my-reel-1",
        wrapper: ".my-reel-wrap-1",
        itemSelector: ".my-reel-item-1",
        speed: 0,
      });

      new Reeller({
        container: ".my-reel-2",
        wrapper: ".my-reel-wrap-2",
        itemSelector: ".my-reel-item-2",
        speed: 2,
        reversed: true,
      });
    </script>
  </body>
</html>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 10:37:01