使用scrollTo实现平滑滚动:按钮触发滚动失效问题求助
修复手动按钮触发的平滑滚动问题
你的按钮滚动失效主要有三个核心问题:
- 手动滚动时,
getBoundingClientRect().top是视口相对位置,未结合当前滚动距离计算文档绝对位置,且在动画循环中重复获取会因滚动导致值动态变化 - 按钮点击时硬传
direction: 'down',无法处理向上滚动的场景,且currentSection未正确更新为目标索引 - 手动滚动的位置计算逻辑错误,应该基于起始位置到目标位置的差值进行插值,而非直接用目标位置乘进度
修复后的完整代码
HTML(无修改)
<div id="div"> <button class="button" type="button">Go to 1</button> <button class="button" type="button">Go to 2</button> <button class="button" type="button">Go to 3</button> </div> <section class="section"></section> <section class="section"></section> <section class="section"></section>
CSS(无修改)
body { margin: 0; } #div { position: fixed; top: 0; left: 0; display: flex; column-gap: 10px; } .section { height: 100vh; width: 100%; } .section:nth-of-type(1) { background-color: red; } .section:nth-of-type(2) { background-color: yellow; } .section:nth-of-type(3) { background-color: green; }
JS(核心修改)
(function () { const lis = document.querySelectorAll('.section'), btnsNav = document.querySelectorAll('.button'), speed = 500; let currentSection = 0, isScrolling = false; function smoothScrollTo(time, targetIndex) { if (isScrolling) { return; } isScrolling = true; const start = performance.now(), startY = window.scrollY, // 计算目标section的文档绝对位置:视口位置 + 当前滚动距离 targetY = startY + lis[targetIndex].getBoundingClientRect().top, distance = targetY - startY; function animateScroll() { const now = performance.now(), elapsed = now - start, progress = Math.min(elapsed / time, 1); // 防止进度超过1 window.scrollTo(0, startY + distance * progress); if (elapsed < time) { requestAnimationFrame(animateScroll); } else { isScrolling = false; currentSection = targetIndex; // 更新当前section为目标索引 } } requestAnimationFrame(animateScroll); } document.addEventListener('wheel', function (event) { if (event.deltaY > 0 && currentSection < lis.length - 1) { smoothScrollTo(speed, currentSection + 1); } else if (event.deltaY < 0 && currentSection > 0) { smoothScrollTo(speed, currentSection - 1); } }); for (let index = 0; index < btnsNav.length; index++) { btnsNav[index].addEventListener('click', function () { smoothScrollTo(speed, index); }); } })();
关键修改说明
- 重构
smoothScrollTo函数:直接接收目标section索引,统一处理滚轮和按钮的滚动逻辑,避免区分auto/manual模式的冗余 - 正确计算目标位置:在动画开始前一次性计算
targetY,结合window.scrollY将视口相对位置转为文档绝对位置,避免滚动过程中位置动态变化 - 统一进度计算:所有滚动都基于起始位置到目标位置的差值进行线性插值,确保平滑过渡
- 更新
currentSection:动画结束后直接设置为目标索引,保证滚轮和按钮操作的状态一致性 - 限制进度最大值:用
Math.min(progress, 1)防止滚动结束时位置超出目标
内容的提问来源于stack exchange,提问作者KarChung
相关产品推荐
相关产品推荐

