在窗口上设置滚动时抖动

时间:2013-04-19 17:22:18

标签: javascript jquery dom javascript-events scroll

编辑:

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

原始邮件:

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

以下是代码(和fiddle):

$(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顺便提一下。

编辑:

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

2 个答案:

答案 0 :(得分:0)

有些情况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);
    }
});

答案 1 :(得分:0)

因此,根据问题顶部所述问题的原因,我认为使用代码中的方法并不能直接解决此问题。我最终使用的解决方案是实现一种虚拟滚动。我将要滚动的元素放入“视口”div中,其中固定位置填充窗口区域。然后我使用'scrollTarget'div扩展窗口大小,并根据窗口的滚动位置以44为增量滚动视口。请参阅此fiddle

的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;
}