为何我的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
相关产品推荐
相关产品推荐

