在窗口上设置滚动时抖动

Jitters when setting scroll on window

本文关键字:抖动 滚动 设置 窗口      更新时间:2023-09-26

编辑:

我找出了问题的原因。当您使用滚动条滚动,然后以编程方式设置滚动时,滚动条会移动,这会导致浏览器根据鼠标相对于新滚动条位置的位置重新计算滚动位置。现在我正在寻找解决方案。这是Youtube上的一段视频,视频中使用了以下答案之一的代码。

原始帖子:

我试图实现这样一种效果,即窗口滚动的增量等于网格中元素的大小,这样滚动看起来很平滑。元素是40px的正方形,有4px的填充,所以我试图以44px的增量滚动页面。我已经写了一些代码,当页面垂直滚动时,它非常有效。我为水平滚动设置了完全相同的操作(或者我认为是这样(,但当我水平滚动时,页面会前后抖动。我试过调试它,似乎每调用一秒或三次滚动处理程序,向左滚动的窗口就会被设置为某个看似随机的值。

这是代码(和小提琴(:

$(function(){
    // Here is the scroll function in question
    $(window).scroll(function(){
        var lastScrollTop = $(window).scrollTop();
        var lastScrollLeft = $(window).scrollLeft();
        return function(){
            var scrollTop = $(this).scrollTop();
            var scrollLeft = $(this).scrollLeft();
            var yDir = scrollTop - lastScrollTop > 0 ? 1 : -1;
            var xDir = scrollLeft - lastScrollLeft > 0 ? 1 : -1;
            $(this).scrollTop(~yDir ? scrollTop + ((44 - ((scrollTop + 44) % 44))) : scrollTop - (scrollTop % 44));
            $(this).scrollLeft(~xDir ? scrollLeft + ((44 - ((scrollLeft + 44) % 44))) : scrollLeft - (scrollLeft % 44));
            scrollTop = lastScrollTop = $(this).scrollTop();
            scrollLeft = lastScrollLeft = $(this).scrollLeft();
        }
    }());
});

编辑:

我正在Chrome 26.0.1410.65 btw.中测试这个

编辑:

垂直滚动也会出现问题,但只有在使用滚动条而不是鼠标滚轮时才会出现问题。

在某些情况下,scrollTop - lastScrollTopscrollLeft - lastScrollLeft为零,从而导致逻辑出现问题。

参见的这两行

var yDir = scrollTop - lastScrollTop > 0 ? 1 : -1;
var xDir = scrollLeft - lastScrollLeft > 0 ? 1 : -1;

假设差值大于0,则xDir变量将为1,并将scrollLeft设置为scrollTop + ((44 - ((scrollTop + 44) % 44))

下一次迭代差为0,xDir变量将为-1,现在将使用scrollLeft - (scrollLeft % 44))。即使滚动位置没有改变,这两个方程的计算结果也不相同。

试试这个。

$(function(){
    // Here is the scroll function in question
    $(window).scroll(function(){
        var lastScrollLeft = $(window).scrollLeft();
        var lastScrollTop = $(window).scrollTop();
        return function(){
            var scrollLeft = $(this).scrollLeft();
            var scrollTop = $(this).scrollTop();
            if (lastScrollLeft !== scrollLeft) {
                lastScrollLeft = scrollLeft - (scrollLeft % 44);
                $(this).scrollLeft(lastScrollLeft);               
            } 
            if (lastScrollTop !== scrollTop) {
                lastScrollTop = scrollTop - (scrollTop % 44);
                $(this).scrollTop(lastScrollTop);   
            }
       }
    }());
    // Generates the grid, ignore this part
    for(var i=0;i<50;i++){
        var row = $('<div></div>').addClass('row').appendTo('body');
        for(var j=0; j<50; j++)
            $('<span></span>').appendTo(row).addClass('cell').text(50*i+j);
    }
});

因此,根据问题顶部所述的问题原因,我认为使用代码中的方法无法直接解决此问题。我最终使用的解决方案是实现一种虚拟滚动。我将要滚动的元素放入一个"viewport"div中,固定位置填充窗口区域。然后,我使用"scrollTarget"div扩展窗口大小,并根据窗口的滚动位置以44的增量滚动视口。看看这把小提琴。

Javascript

$(function(){
    // Here is the scroll function in question
    $(this).scroll(function(){
        var scrollTop = $(this).scrollTop();
        var scrollLeft = $(this).scrollLeft();
        $('#viewport').scrollTop(scrollTop - (scrollTop % 44));
        $('#viewport').scrollLeft(scrollLeft - (scrollLeft % 44));
    });

    // Generates the grid, ignore this part
    for(var i=0;i<50;i++){
        var row = $('<div></div>').addClass('row').appendTo('#viewport');
        for(var j=0; j<50; j++)
            $('<span></span>').appendTo(row).addClass('cell').text(50*i+j);
    }
    $('#scrollTarget').height((50 * 44) + 48)
        .width((50 * 44) + 48);
});

HTML

<div id="viewport"></div>
<div id="scrollTarget"></div>

CSS

#viewport{
    position:fixed;
    height:100%;
    width:100%;
    overflow:hidden;
}
#scrollTarget{
    position:absolute;
}
.row{
    height:44px;
    width:2204px;
}
.row:first-child{
    height:40px;
}
.cell{
    display:inline-block;
    height:40px;
    width:40px;
    margin-top:4px;
    margin-right:4px;
    background-color:#859DE1;
    color:#0F1219;
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
    border-radius: 5px;
}
.row:first-child .cell{
    margin-top:0px;
}