2016-08-14 35 views
21

我有一个情况,例如,如果用户的滚动会导致scrollTop的一个1000像素的变化,我想提前知道。是否有可能确定使用JavaScript将滚动到哪里?如果是这样,怎么样?

最好的例子是的iCalendar的控制用户的滚动控制。无论您在iCalendar应用程序中滚动多么艰难,您可以滚动的最远处都是下一个月或上一个月。

我现在有一个非常hackish的解决方案,以限制滚动行为,只考虑到了用户的滚动目前。

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

这种方法的工作原理,并停止正在进行的滚动,但它并不顺利,我很想知道,如果有更好的方法来做到这一点。

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

+0

你会如何知道用户何时会停止提前时间? – epascarello

+0

@epascarello我基本上是指1个手势。即使我们的滚动显示为连续的,它们也是由具有特定滚动值的一系列离散手势组成的。不同的硬件以不同的方式对待这些手势,但是计算机的某处知道滚动将会有多远。 – Robert

+1

您可能想要提到您指的是惯性/动量滚动,而不是正常的鼠标滚动。我不知道有什么办法可以做到这一点。 –

回答

6

编辑:刚刚发现下面的项目在GitHub上:

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

我尝试了演示,它是能够报告我的触摸板和鼠标的滚动速度。它也能够停止滚动没有任何位置固定的黑客:D

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

当我已经有了关于这个问题的详细信息我会更新这个帖子。


这是不可能预测鼠标滚动将结束。

,另一方面触屏/触摸板刷卡有一定的速度,用户停止刷卡,就像一辆汽车,得到了一推,之后开始减缓之后将放缓。

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


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

我们得到了3个实现,可以作出:

A.方向

B.方向和速度

C.方向,速度和速度


iCalender可能使用实现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事件将始终在页面滚动时触发,因此我们只需将动画代码挂钩即可,无需担心任何输入设备交互。

+0

@Kaiido这与我写的几乎一样(除了花哨的画布),但遗憾的是,这在触摸屏,键盘导航等上不起作用。所以我决定只使用滚动事件,因为它是由一切触发的:P – seahorsepip

+1

不幸的是,它只在窗口上显示2个速度(快或慢):/我写了很多在滚动中触发的东西过去和我已经知道,为每一个事件编写代码(触摸,滚动等)变得烦人,并成为很多工作:P写上述实现也需要一些摆弄工作,因为没有真正的方法来停止正确滚动,我不得不使用之前使用的定位技巧。 – seahorsepip

+0

mousewheel插件看起来很有希望,但是一个webkit bug阻止它能够在Safari中工作,这是有问题的。 – Robert

0

正如@seahorsepip指出的那样,通常不可能知道滚动最终会在何处添加自定义行为而不是JavaScript。该MDN文档没有列出任何方式来访问排队滚动事件:https://developer.mozilla.org/en-US/docs/Web/Events/scroll

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

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

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

+0

触摸屏和鼠标滚动不像我在答案中显示的那样巨大,但触摸板滚动是EVIL。一个快速的触摸板滚动将触发滚动事件近3秒D:这意味着如果你像我的例子中停止滚动,然后动画500毫秒,然后启用滚动,它将在动画后滚动2.5秒:/ – seahorsepip

0

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

相关问题