为什么这个 "scroll to top" 函数有问题?

Why is this "scroll to top" function glitchy?

我一直在寻找一个很好的解决方案,可以通过单击以动画方式将 div(当然是可滚动的)滚动到其顶部。我遇到了 this(#2 解决方案),这看起来不错,但它滚动整个 window 而不是特定的 div.

我稍微修改了此函数,使其可以滚动容器而不是整个容器 window。这是我的版本:

function scrollContainerToTop(scrollDuration, container_id) {
    const container = document.getElementById(container_id),
    scrollHeight = container.scrollTop,
    scrollStep = Math.PI / (scrollDuration / 15),
    cosParameter = scrollHeight / 2;
    var scrollCount = 0,
        scrollMargin,
        scrollInterval = setInterval(function () {
            if (container.scrollTop != 0) {
                scrollCount = scrollCount + 1;
                scrollMargin = cosParameter - cosParameter * Math.cos(scrollCount * scrollStep);
                container.scrollTop = (scrollHeight - scrollMargin);
            } else clearInterval(scrollInterval);
        }, 15);
}

here 是一个功能示例,您可以尝试:将所有 "Hello" 的部分滚动到底部,然后单击按钮将其向上滚动 javascript.

所以我的问题是,它不是仅仅向上滚动,而是快速上下滚动两到三遍,然后才停在顶部。但是,如果您以更大的延迟(例如 300 毫秒)调用滚动条函数,那么一切都很好。我在 Safari、Chrome 和旧版本的 Firefox 上也尝试过,它们中的每一个都会产生相同的故障结果。

如果我提供更快的滚动时间,为什么这个函数会出现故障?

快速检查调试器(仔细放置 console.log)将回答以下问题:

function scrollContainerToTop(scrollDuration, container_id) {
    const container = document.getElementById(container_id),
    scrollHeight = container.scrollTop,
    scrollStep = Math.PI / (scrollDuration / 15),
    cosParameter = scrollHeight / 2;
    var scrollCount = 0,
        scrollMargin,
        scrollInterval = setInterval(function () {
            if (container.scrollTop != 0) {
                scrollCount = scrollCount + 1;
                scrollMargin = cosParameter - cosParameter * Math.cos(scrollCount * scrollStep);
                container.scrollTop = (scrollHeight - scrollMargin);
                // debug here:
                console.log( 'container.scrollTop = ' + container.scrollTop );
            } else clearInterval(scrollInterval);
        }, 150);
}

结果:

container.scrollTop = 554
container.scrollTop = 465
container.scrollTop = 338
container.scrollTop = 202
container.scrollTop = 85
container.scrollTop = 14
container.scrollTop = 3
container.scrollTop = 55
container.scrollTop = 159
container.scrollTop = 293
container.scrollTop = 426
container.scrollTop = 530
container.scrollTop = 582
container.scrollTop = 571
container.scrollTop = 500
container.scrollTop = 383
container.scrollTop = 247
container.scrollTop = 120
container.scrollTop = 31
container.scrollTop = 0

更完整的解释:

当你未能达到 0 时,下一次迭代实际上会产生一个负数 scrollMargin(因为那是 how cosine works)。迭代继续来回直到你最终达到 0。

看看当我们输出变量的实际值时会发生什么:

console.log( 'scrollMargin = ' + cosParameter + ' - ' + cosParameter + ' * ' + Math.cos(scrollCount * scrollStep) );

scrollMargin = 293 - 293 * 0.8910065241883679
scrollMargin = 293 - 293 * 0.5877852522924731
scrollMargin = 293 - 293 * 0.15643446504023092
scrollMargin = 293 - 293 * -0.30901699437494734
scrollMargin = 293 - 293 * -0.7071067811865475
scrollMargin = 293 - 293 * -0.9510565162951535
scrollMargin = 293 - 293 * -0.9876883405951378
scrollMargin = 293 - 293 * -0.8090169943749475
scrollMargin = 293 - 293 * -0.4539904997395469
scrollMargin = 293 - 293 * -1.8369701987210297e-16
scrollMargin = 293 - 293 * 0.45399049973954664
scrollMargin = 293 - 293 * 0.8090169943749472
scrollMargin = 293 - 293 * 0.9876883405951377
scrollMargin = 293 - 293 * 0.9510565162951536
scrollMargin = 293 - 293 * 0.7071067811865477
scrollMargin = 293 - 293 * 0.30901699437494773
scrollMargin = 293 - 293 * -0.15643446504022968
scrollMargin = 293 - 293 * -0.5877852522924729
scrollMargin = 293 - 293 * -0.8910065241883681
scrollMargin = 293 - 293 * -1

一种解决方案是简单地检查结果 container.scrollTop。如果您在 scrollMargin 范围内,则继续并完成滚动:

if (container.scrollTop < scrollMargin) container.scrollTop = 0;