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

使用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);
        });
    }
})();

关键修改说明

  1. 重构smoothScrollTo函数:直接接收目标section索引,统一处理滚轮和按钮的滚动逻辑,避免区分auto/manual模式的冗余
  2. 正确计算目标位置:在动画开始前一次性计算targetY,结合window.scrollY将视口相对位置转为文档绝对位置,避免滚动过程中位置动态变化
  3. 统一进度计算:所有滚动都基于起始位置到目标位置的差值进行线性插值,确保平滑过渡
  4. 更新currentSection:动画结束后直接设置为目标索引,保证滚轮和按钮操作的状态一致性
  5. 限制进度最大值:用Math.min(progress, 1)防止滚动结束时位置超出目标

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 06:07:46