如何实现原生JS控制的div滑块无限循环滚动效果?
div滑块无缝无限滚动实现方案
问题原因
当前实现通过直接修改滚动容器scrollLeft值控制位移,原生滚动容器内容存在明确起止边界,滚动到首尾位置时会自然停止,无法实现循环效果。
要做到无缝无限滚动,核心是通过「首尾克隆占位+边界无感知复位」的方式,消除滚动边界带来的停顿感。
核心实现逻辑
- 初始化阶段:在滑块内容头部克隆尾部若干幻灯片、尾部克隆头部若干幻灯片,制造足够的滚动过渡余量,克隆的项总宽度只要能覆盖滑块容器可视宽度即可
- 初始位置校准:页面加载后把滚动位置偏移到真实首项的位置,把前置的克隆项隐藏在可视区域外
- 边界复位:每次滚动结束后判断当前位置,如果滚到最前端的克隆区域,就瞬间无动画跳转到真实尾部对应的位置;如果滚到最后端的克隆区域,就瞬间无动画跳转到真实头部对应的位置,整个跳转过程用户完全感知不到,不会出现视觉断层
- 增加防抖逻辑,避免快速点击按钮导致滚动错乱
完整可运行代码
JavaScript 部分
原有简单的加减scrollLeft逻辑替换为以下代码:
document.addEventListener('DOMContentLoaded', function() { const slider = document.getElementById('slider') const buttonLeft = document.getElementById('slide_left') const buttonRight = document.getElementById('slide_right') const thumbnails = slider.querySelectorAll('.thumbnail') const itemWidth = 90 // 单个幻灯片宽度80px + 10px右间距 const cloneCount = 4 // 克隆项数量,覆盖350px可视宽度足够 let isAnimating = false // 首尾插入克隆项 for(let i = thumbnails.length - cloneCount; i < thumbnails.length; i++) { slider.prepend(thumbnails[i].cloneNode(true)) } for(let i = 0; i < cloneCount; i++) { slider.append(thumbnails[i].cloneNode(true)) } // 初始位置对准真实首项 slider.scrollLeft = itemWidth * cloneCount // 边界复位逻辑 function checkBoundary() { isAnimating = false // 滚到左克隆区,瞬间跳转到真实尾部位置 if(slider.scrollLeft <= 0) { slider.style.scrollBehavior = 'auto' slider.scrollLeft = thumbnails.length * itemWidth } // 滚到右克隆区,瞬间跳转到真实头部位置 if(slider.scrollLeft >= (thumbnails.length + cloneCount) * itemWidth) { slider.style.scrollBehavior = 'auto' slider.scrollLeft = cloneCount * itemWidth } } slider.addEventListener('scroll', function() { if(isAnimating) return window.requestAnimationFrame(checkBoundary) }) // 左右按钮点击事件 buttonLeft.addEventListener('click', function() { if(isAnimating) return isAnimating = true slider.style.scrollBehavior = 'smooth' slider.scrollLeft -= itemWidth }) buttonRight.addEventListener('click', function() { if(isAnimating) return isAnimating = true slider.style.scrollBehavior = 'smooth' slider.scrollLeft += itemWidth }) })
CSS 部分
原有样式基本保留,不要给#slider加scroll-behavior: smooth属性,滚动行为完全由JS控制,避免复位时出现动画穿帮:
body{ background-color: #555; height: 100vh; display: grid; align-items: center; justify-items: center; font-family: 'Helvetica'; } div#slide_wrapper{ width: 440px; display: flex; justify-content: space-between; height: fit-content; } div#slider{ width: 350px; display: flex; height: fit-content; flex-wrap: nowrap; overflow: hidden; } div.thumbnail{ min-width: 80px; min-height: 80px; cursor: pointer; display: grid; place-items: center; font-size: 30px; } div.thumbnail:not(:last-child){ margin-right: 10px; } div.thumbnail:nth-child(1){background-color: darkturquoise;} div.thumbnail:nth-child(2){background-color: goldenrod;} div.thumbnail:nth-child(3){background-color: rebeccapurple;} div.thumbnail:nth-child(4){background-color: powderblue;} div.thumbnail:nth-child(5){background-color: firebrick;} div.thumbnail:nth-child(6){background-color: sienna;} div.thumbnail:nth-child(7){background-color: bisque;} div.thumbnail:nth-child(8){background-color: navy;} div#slide_wrapper > button{ height: fit-content; align-self: center; font-size: 24px; font-weight: 800; border: none; outline: none; } div#slide_wrapper > button:hover{ cursor: pointer; background-color: dodgerblue; color: #fff; }
HTML 部分
原有结构完全不需要修改:
<div id="slide_wrapper"> <button id="slide_left" class="slide_arrow">❮</button> <div id="slider"> <div class="thumbnail active">1</div> <div class="thumbnail">2</div> <div class="thumbnail">3</div> <div class="thumbnail">4</div> <div class="thumbnail">5</div> <div class="thumbnail">6</div> <div class="thumbnail">7</div> <div class="thumbnail">8</div> </div> <button id="slide_right" class="slide_arrow">❯</button> </div>
适配说明
如果后续调整幻灯片尺寸、数量或者滑块容器宽度,只需要对应修改itemWidth(单个幻灯片占位宽度)和cloneCount(保证克隆项总宽度≥滑块容器可视宽度即可),其他逻辑无需调整。
内容的提问来源于stack exchange,提问作者Matt Don
相关产品推荐
相关产品推荐

