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

为何我的Carousel导致页面过宽?附循环滚动需求咨询

作品集Carousel组件优化方案

问题说明

个人作品集网站的Carousel组件功能正常,但向右滑动时页面过宽出现底部滚动条,需要隐藏超出屏幕的幻灯片;同时需要实现图片循环滚动,避免滑动到两端停止。


一、解决页面过宽与滚动条问题

通过给父容器添加溢出隐藏,并调整轨道定位逻辑,彻底隐藏超出视口的内容:

修改后的CSS代码

.wallpapers {
  position: relative;
  width: 100vw;
  height: 100vh;
  overflow: hidden; /* 隐藏超出视口的内容 */
}

#image-track {
  display: flex;
  gap: 4vmin;
  position: absolute;
  top: 50%;
  transform: translate(0%, -50%);
  user-select: none;
  /* 移除left:50%,让轨道从左侧起始,超出部分被父容器隐藏 */
}

#image-track .image {
  width: 40vmin;
  height: 56vmin;
  object-fit: cover;
  object-position: 100% center;
}

二、实现循环滚动功能

通过动态复制首尾图片,结合滚动位置重置,实现无缝循环效果:

修改后的JS代码

const track = document.getElementById("image-track");
const images = Array.from(track.getElementsByClassName("image"));

// 复制首尾图片,实现循环的视觉衔接
const firstImage = images[0].cloneNode(true);
const lastImage = images[images.length - 1].cloneNode(true);
track.appendChild(firstImage);
track.insertBefore(lastImage, images[0]);

// 更新图片列表,包含复制的元素
const updatedImages = Array.from(track.getElementsByClassName("image"));

const handleOnDown = e => track.dataset.mouseDownAt = e.clientX;

const handleOnUp = () => {
  track.dataset.mouseDownAt = "0";
  track.dataset.prevPercentage = track.dataset.percentage;
  
  // 滚动到复制的首图时,重置到真实首图位置
  const currentPercentage = parseFloat(track.dataset.percentage);
  if (currentPercentage >= 0) {
    track.dataset.percentage = -100 * images.length;
    track.style.transform = `translate(${-100 * images.length}%, -50%)`;
    track.dataset.prevPercentage = track.dataset.percentage;
    // 同步更新图片的object-position
    updatedImages.forEach(img => {
      img.style.objectPosition = `${100 + parseFloat(track.dataset.percentage)}% center`;
    });
  }
  // 滚动到复制的尾图时,重置到真实尾图位置
  else if (currentPercentage <= -100 * (updatedImages.length - 1)) {
    track.dataset.percentage = -100;
    track.style.transform = `translate(-100%, -50%)`;
    track.dataset.prevPercentage = track.dataset.percentage;
    updatedImages.forEach(img => {
      img.style.objectPosition = `${100 + parseFloat(track.dataset.percentage)}% center`;
    });
  }
}

const handleOnMove = e => {
  if (track.dataset.mouseDownAt === "0") return;

  const mouseDelta = parseFloat(track.dataset.mouseDownAt) - e.clientX,
    maxDelta = window.innerWidth / 2;

  const percentage = (mouseDelta / maxDelta) * -100,
    nextPercentageUnconstrained = parseFloat(track.dataset.prevPercentage) + percentage,
    // 放宽百分比约束,允许滚动到复制的图片区域
    nextPercentage = Math.max(Math.min(nextPercentageUnconstrained, 0), -100 * (updatedImages.length - 1));

  track.dataset.percentage = nextPercentage;

  track.animate({
    transform: `translate(${nextPercentage}%, -50%)`
  }, {
    duration: 1200,
    fill: "forwards"
  });

  for (const image of updatedImages) {
    image.animate({
      objectPosition: `${100 + nextPercentage}% center`
    }, {
      duration: 1200,
      fill: "forwards"
    });
  }
}

window.onmousedown = e => handleOnDown(e);
window.ontouchstart = e => handleOnDown(e.touches[0]);
window.onmouseup = e => handleOnUp(e);
window.ontouchend = e => handleOnUp(e.touches[0]);
window.onmousemove = e => handleOnMove(e);
window.ontouchmove = e => handleOnMove(e.touches[0]);

// 初始化位置,从真实第一张图开始
track.dataset.percentage = -100;
track.style.transform = `translate(-100%, -50%)`;
updatedImages.forEach(img => {
  img.style.objectPosition = `${100 + parseFloat(track.dataset.percentage)}% center`;
});

对应HTML(无需修改,JS动态添加复制图片)

<section class="wallpapers">
  <div id="image-track" data-mouse-down-at="0" data-prev-percentage="0">
    <img class="image" src="https://images.unsplash.com/photo-1524781289445-ddf8f5695861?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80" draggable="false" />
    <img class="image" src="https://images.unsplash.com/photo-1610194352361-4c81a6a8967e?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1674&q=80" draggable="false" />
    <img class="image" src="https://images.unsplash.com/photo-1618202133208-2907bebba9e1?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80" draggable="false" />
    <img class="image" src="https://images.unsplash.com/photo-1495805442109-bf1cf975750b?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80" draggable="false" />
    <img class="image" src="https://images.unsplash.com/photo-1548021682-1720ed403a5b?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80" draggable="false" />
    <img class="image" src="https://images.unsplash.com/photo-1496753480864-3e588e0269b3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2134&q=80" draggable="false" />
    <img class="image" src="https://images.unsplash.com/photo-1613346945084-35cccc812dd5?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1759&q=80" draggable="false" />
    <img class="image" src="https://images.unsplash.com/photo-1516681100942-77d8e7f9dd97?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80" draggable="false" />
  </div>
</section>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 17:46:01