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

快速滚动时Infinite carousel无限轮播出现卡顿问题

无限轮播快速滚动卡顿问题解决方案

问题说明

开发可拖拽/滚动的无限轮播组件时,遇到以下问题:当用户快速滚动(可按住Shift键测试),infiniteScroll函数内的判断逻辑会在carousel.scrollLeft()的更新生效前多次触发,导致carousel.scrollLeft()被重复调用,最终引发轮播卡顿。

解决思路

通过添加状态锁机制,避免滚动重置操作被重复触发:

  • 新增一个状态变量标记当前是否正在执行滚动位置重置
  • 触发重置逻辑前先检查状态锁,已锁定则直接跳过
  • 完成滚动位置重置后再解锁,确保同一时间段内只执行一次重置操作

修改后的代码

JavaScript

const carousel = $('.carousel');
const carouselCards = carousel.children('.card');

let cardAmount = 3; // 轮播内可见卡片数量
let isScrollLock = false; // 滚动锁定状态,防止重复触发

carouselCards.slice(-cardAmount).get().reverse().forEach((e) => {
  let card = $(e);
  carousel.prepend(card.prop('outerHTML')); // 将最后三张卡片逆序添加到开头
})

carouselCards.slice(0, cardAmount).get().forEach((e) => {
  let card = $(e);
  carousel.append(card.prop('outerHTML')); // 将前三张卡片添加到末尾
})

let scrollTriggerStart = carouselCards.outerWidth() + carouselCards.outerWidth() / 2 - carousel.outerWidth() / 2; // 第二张卡片居中时的scrollLeft值
let scrollTriggerEnd = carousel[0].scrollWidth - carouselCards.outerWidth() - carouselCards.outerWidth() / 2 - carousel.outerWidth() / 2; // 倒数第二张卡片居中时的scrollLeft值
let seamlessPosStart = carouselCards.length * carouselCards.outerWidth() - carouselCards.outerWidth() / 2 - carousel.outerWidth() / 2; // 对应倒数第二张卡片的目标scrollLeft值
let seamlessPosEnd = carousel[0].scrollWidth - carouselCards.length * carouselCards.outerWidth() + carouselCards.outerWidth() / 2 - carousel.outerWidth() / 2; // 对应第二张卡片的目标scrollLeft值

const infiniteScroll = () => {
  // 如果正在执行重置,直接返回
  if (isScrollLock) return;

  if (carousel.scrollLeft() <= scrollTriggerStart) {
    isScrollLock = true;
    carousel.addClass('no-transition');
    carousel.scrollLeft(seamlessPosEnd);
    // 用requestAnimationFrame确保DOM更新完成后解锁
    requestAnimationFrame(() => {
      carousel.removeClass('no-transition');
      isScrollLock = false;
    });
  } else if (carousel.scrollLeft() >= scrollTriggerEnd) {
    isScrollLock = true;
    carousel.addClass('no-transition');
    carousel.scrollLeft(seamlessPosStart);
    requestAnimationFrame(() => {
      carousel.removeClass('no-transition');
      isScrollLock = false;
    });
  }
}

carousel.on('scroll', infiniteScroll);

CSS

*,
::before,
::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

* {
  font: inherit;
}

html,
body {
  min-height: 100vh;
}

ul:has([class]) {
  list-style: none;
}

body {
  font-family: sans-serif;
  display: flex;
  align-items: center;
  justify-content: center;
}

.wrapper {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5em;
  width: 100%;
  padding-inline: 10em;
}

.carousel {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: calc(100% / 2);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  border-radius: .5em;
  scroll-behavior: smooth;
  scrollbar-width: none;
  width: 100%;
}

.carousel::-webkit-scrollbar {
  display: none;
}

.carousel .card {
  aspect-ratio: 1;
  scroll-snap-align: center;
  background: rgba(255, 0, 0, 0.25);
  border-radius: .5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2rem;
  scale: 0.95;
  border: 2px solid transparent;
  transition: 250ms ease;
}

.card.selected {
  border: 2px solid red;
  scale: 1;
}

.no-transition {
  scroll-behavior: auto;
}

.no-transition .card {
  scroll-snap-align: unset;
}

HTML

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="style.css">
  <title>Infinite carousel</title>
</head>

<body>
  <div class="wrapper">
    <ul class="carousel">
      <li class="card">
        <h2>Card 1</h2>
      </li>
      <li class="card">
        <h2>Card 2</h2>
      </li>
      <li class="card">
        <h2>Card 3</h2>
      </li>
      <li class="card">
        <h2>Card 4</h2>
      </li>
      <li class="card">
        <h2>Card 5</h2>
      </li>
    </ul>
  </div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script src="main.js"></script>
</body>

</html>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 01:24:54