是否可以使用javascript确定滚动结束的位置?如果是这样,怎么样?

时间:2016-08-14 01:49:51

标签: javascript jquery html dom scroll

我的情况是,例如,如果用户的滚动会导致scrollTop发生1000像素的变化,我想提前知道。

完美的例子是iCalendar对用户滚动的控制。无论你在iCalendar应用程序中滚动多么努力,你可以滚动的最远的是下个月或上个月。

我目前有一个非常强硬的解决方案来限制滚动行为,它只考虑用户当前滚动的位置。

MyConstructor.prototype._stopScroll = function(){

    //Cache the previous scroll position and set a flag that will control
    //whether or not we stop the scroll
    var previous = this._container.scrollTop;
    var flag     = true;

    //Add an event listener that stops the scroll if the flag is set to true
    this._container.addEventListener('scroll', function stop(){
        if(flag) {
            this._container.scrollTop = previous;
        }
    }.bind(this), false);

    //Return a function that has access to the stop function and can remove it
    //as an event listener
    return function(){
        setTimeout(function(){
            flag = false;
            this._container.removeEventListener('scroll', stop, false);
        }.bind(this), 0);
    }.bind(this);
};

这种方法有效,并且会阻止正在进行的滚动,但它并不顺畅,我很想知道是否有更好的方法来实现这一目标。

这个问题的关键是我可以提前知道 滚动结束的地方。感谢!!!

3 个答案:

答案 0 :(得分:6)

编辑:刚刚在github上找到了以下项目:

https://github.com/jquery/jquery-mousewheel

我尝试了演示,它能够报告我的触摸板和鼠标滚动速度。它还可以在没有任何固定位置的情况下停止滚动:D

我将在接下来的几天看看,看看我是否可以编写任何报告滚动速度,方向,速度,设备等的内容。希望我能够制作一些可以覆盖的jquery插件所有滚动互动。

当我获得有关此主题的更多信息时,我会更新此帖子。

无法预测鼠标滚动的结束位置。

另一方面,触摸屏/触摸板滑动具有一定的速度,在用户停止滑动之后将会减速,就像推动汽车并且之后开始减速一样。

可悲的是,每个浏览器/操作系统/驱动程序/触摸屏/触摸板/等都有它自己的实现,因此我们无法预测到这一点。


但我们当然可以编写自己的实现。

我们有3个可以实现的实现:

一个。方向

B中。方向和速度

℃。方向,速度和速度


  

iCalender可能使用实现A.


实施A:

  

将滚动方向输出到控制台,用户可以滚动+/- 1px   在检测到方向之前。

     

Demo on JSFiddle

     

Demo with animation on JSFiddle

(function iDirection() {
    var preventLoop = true;
    var currentScroll = scrollTop();
    function scroll() {
        if(preventLoop) {
            //Get new scroll position
            var newScroll = scrollTop();

            //Stop scrolling
            preventLoop = false;
            freeze(newScroll);

            //Check direction
            if(newScroll > currentScroll) {
                console.log("scrolling down");
                //scroll down animation  here
            } else {
               console.log("scrolling up");
                //scroll up animation here
            }
            /*
            Time in milliseconds the scrolling is disabled,
            in most cases this is equal to the time the animation takes
            */
            setTimeout(function() {
                //Update scroll position
                currentScroll = newScroll;

                //Enable scrolling
                unfreeze();

                /*
                Wait 100ms before enabling the direction function again
                (to prevent a loop from occuring).
                */
                setTimeout(function() {
                    preventLoop = true;
                }, 100);
            }, 1000);
        }
    }
    $(window).on("scroll", scroll);
})();


实施B:

  

向控制台输出滚动方向,距离和平均速度,用户可以滚动distance变量中设置的像素数量。

     

如果用户快速滚动,他们可能会滚动几个像素。

     

Demo on JSFiddle

(function iDirectionSpeed() {
    var distance = 50; //pixels to scroll to determine speed
    var preventLoop = true;
    var currentScroll = scrollTop();
    var currentDate = false;
    function scroll() {
        if(preventLoop) {
            //Set date on scroll
            if(!currentDate) {
                currentDate = new Date();
            }

            //Get new scroll position
            var newScroll = scrollTop();

            var scrolledDistance = Math.abs(currentScroll - newScroll);

            //User scrolled `distance` px or scrolled to the top/bottom
            if(scrolledDistance >= distance || !newScroll || newScroll == scrollHeight()) {
                //Stop scrolling
                preventLoop = false;
                freeze(newScroll);

                //Get new date
                var newDate = new Date();

                //Calculate time
                var time = newDate.getTime() - currentDate.getTime();

                //Output speed
                console.log("average speed: "+scrolledDistance+"px in "+time+"ms");

                /*
                To calculate the animation duration in ms:
                x: time
                y: scrolledDistance
                z: distance you're going to animate

                animation duration = z / y * x
                */

                //Check direction
                if(newScroll > currentScroll) {
                    console.log("scrolling down");
                    //scroll down animation  here
                } else {
                   console.log("scrolling up");
                    //scroll up animation here
                }

                /*
                Time in milliseconds the scrolling is disabled,
                in most cases this is equal to the time the animation takes
                */

                setTimeout(function() {
                    //Update scroll position
                    currentScroll = newScroll;

                    //Unset date
                    currentDate = false;

                    //Enable scrolling
                    unfreeze();

                    /*
                    Wait 100ms before enabling the direction function again
                    (to prevent a loop from occuring).
                    */
                    setTimeout(function() {
                        preventLoop = true;
                    }, 100);
                }, 1000);
            }
        }
    }
    $(window).on("scroll", scroll);
})();


实施C:

  

向控制台输出滚动方向,距离和速度,用户可以滚动distance变量中设置的像素数量。

     

如果用户快速滚动,他们可能会滚动几个像素。

     

Demo on JSFiddle

(function iDirectionSpeedVelocity() {
    var distance = 100; //pixels to scroll to determine speed
    var preventLoop = true;
    var currentScroll = [];
    var currentDate = [];
    function scroll() {
        if(preventLoop) {
            //Set date on scroll
            currentDate.push(new Date());

            //Set scrollTop on scroll
            currentScroll.push(scrollTop());

            var lastDate = currentDate[currentDate.length - 1];
            var lastScroll = currentScroll[currentScroll.length - 1];

            //User scrolled `distance` px or scrolled to the top/bottom
            if(Math.abs(currentScroll[0] - lastScroll) >= distance || !lastScroll || lastScroll == scrollHeight()) {
                //Stop scrolling
                preventLoop = false;
                freeze(currentScroll[currentScroll.length - 1]);

                //Total time
                console.log("Time: "+(lastDate.getTime() - currentDate[0].getTime())+"ms");

                //Total distance
                console.log("Distance: "+Math.abs(lastScroll - currentScroll[0])+"px");

                /*
                Calculate speeds between every registered scroll
                (speed is described in milliseconds per pixel)
                */
                var speeds = [];
                for(var x = 0; x < currentScroll.length - 1; x++) {
                    var time = currentDate[x + 1].getTime() - currentDate[x].getTime();
                    var offset = Math.abs(currentScroll[x - 1] - currentScroll[x]);
                    if(offset) {
                        var speed = time / offset;
                        speeds.push(speed);
                    }
                }

                //Output array of registered speeds (milliseconds per pixel)
                console.log("speeds (milliseconds per pixel):");
                console.log(speeds);

                /*
                We can use the array of speeds to check if the speed is increasing
                or decreasing between the first and last half as example
                */ 
                var half = Math.round(speeds.length / 2);
                var equal = half == speeds.length ? 0 : 1;
                var firstHalfSpeed = 0;
                for(var x = 0; x < half; x++ ) {
                    firstHalfSpeed += speeds[x];
                }
                firstHalfSpeed /= half;
                var secondHalfSpeed = 0;
                for(var x = half - equal; x < speeds.length; x++ ) {
                    secondHalfSpeed += speeds[x];
                }
                secondHalfSpeed /= half;
                console.log("average first half speed: "+firstHalfSpeed+"ms per px");
                console.log("average second half speed: "+secondHalfSpeed+"ms per px");
                if(firstHalfSpeed < secondHalfSpeed) {
                    console.log("conclusion: speed is decreasing");
                } else {
                    console.log("conclusion: speed is increasing");
                }

                //Check direction
                if(lastScroll > currentScroll[0]) {
                    console.log("scrolling down");
                    //scroll down animation  here
                } else {
                   console.log("scrolling up");
                    //scroll up animation here
                }

                /*
                Time in milliseconds the scrolling is disabled,
                in most cases this is equal to the time the animation takes
                */
                setTimeout(function() {
                    //Unset scroll positions
                    currentScroll = [];

                    //Unset dates
                    currentDate = [];

                    //Enable scrolling
                    unfreeze();

                    /*
                    Wait 100ms before enabling the direction function again
                    (to prevent a loop from occuring).
                    */
                    setTimeout(function() {
                        preventLoop = true;
                    }, 100);
                }, 2000);
            }
        }
    }
    $(window).on("scroll", scroll);
})();


上述实现中使用的辅助函数:

//Source: https://github.com/seahorsepip/jPopup
function freeze(top) {
    if(window.innerWidth > document.documentElement.clientWidth) {
        $("html").css("overflow-y", "scroll");
    }
    $("html").css({"width": "100%", "height": "100%", "position": "fixed", "top": -top});
}
function unfreeze() {
    $("html").css("position", "static");
    $("html, body").scrollTop(-parseInt($("html").css("top")));
    $("html").css({"position": "", "width": "", "height": "", "top": "", "overflow-y": ""});
}
function scrollTop() {
    return $("html").scrollTop() ? $("html").scrollTop() : $("body").scrollTop();
}
function scrollHeight() {
    return $("html")[0].scrollHeight ? $("html")[0].scrollHeight : $("body")[0].scrollHeight;
}

只看一下评论中提到的scrollify,它的10kb并且需要挂钩每个简单的事件:触摸,鼠标滚动,键盘按钮等。

这似乎是未来的证据,谁知道未来可能会有什么用户互动导致滚动?

另一方面,onscroll事件将始终在页面滚动时触发,因此,让我们将动画代码挂钩,而不必担心任何输入设备交互。

答案 1 :(得分:0)

正如@seahorsepip所述,通常不可能知道滚动结束的位置而不用JavaScript添加自定义行为。 MDN文档未列出任何访问排队滚动事件的方法:https://developer.mozilla.org/en-US/docs/Web/Events/scroll

我发现此信息有用: Normalizing mousewheel speed across browsers

它突出了根据用户输入知道页面的去向的难度。我的建议是当代码预测达到阈值时触发滚动到Y事件。在您的示例中,如果滚动在250毫秒的时间窗口中移动了1000像素的页面800,则将滚动设置为该1000像素标记并将滚动切断500毫秒。

https://developer.mozilla.org/en-US/docs/Web/API/window/scrollTo

答案 2 :(得分:0)

我不确定我是否有你想要的东西。我有一次项目,我必须控制滚动。那时我已经覆盖了默认的滚动事件,之后您可以为“一个”滚动设置自定义距离。另外添加了jQuery动画以滚动到特定位置。 在这里你可以看看:http://c-k.co/zw1/ 如果这就是你要找的东西,你可以联系我,我会看到我对自己的东西有多了解