我正在为一个客户制作一个垂直网站,当希望在视口中看到大部分元素时,该网站希望窗口“捕捉”到最近的页面。因此,如果页面可见85%,则应滚动至100%可见。
我的问题是,当偶尔滚动到视口的顶部或底部时,视口将“粘住”到第一个或最后一个元素,从而阻止了一些滚动事件并导致高度明显的闪烁。
这里有一个工作小提琴:http://jsfiddle.net/RTzu8/1/ 要重现错误,请使用滚动条滚动到页面底部。然后,用鼠标滚轮向上滚动。你应该看到闪烁。有时需要一些刷新或尝试,但问题是高度可重复的。
我对可能导致此问题的原因感到茫然。请参阅下面的代码,以及我迄今为止试图阻止它的代码。
为了实现我的捕捉,我需要检测元素是否是可见的特定百分比。所以,我在下面添加了一个jQuery函数isNearScreen
。我已经彻底测试了这个功能,据我所知它可以返回准确的结果。
//Modification of http://upshots.org/javascript/jquery-test-if-element-is-in-viewport-visible-on-screen
//Returns "true" if element is percent visible within the viewport
$.fn.isNearScreen = function(percent){
var offset = 1 - percent;
var win = $(window);
var viewport = {
top : win.scrollTop()
};
viewport.bottom = viewport.top + win.height();
var bounds = this.offset();
bounds.bottom = bounds.top + this.outerHeight();
bounds.top = bounds.top;
//If the element is visible
if(!(viewport.bottom < bounds.top || viewport.top > bounds.bottom)){
//Get the percentage of the element that's visible
var percentage = (viewport.bottom - bounds.top) / this.height();
//If it's greater than percent, but less than 1 + (1 - percent), return true;
return (percentage > (1 - offset) && percentage < (1 + offset));
}
return false;
};
然后我创建了一个snap
函数,该函数使用Underscore.js的_.debounce
函数,仅触发连续滚动事件的尾端。它在500毫秒超时后触发,我相当(虽然不是100%)确信它正在正确触发。我无法重现控制台日志,这些日志表明多个并发发射。
//Snaps the screen to a page after scroll input has stopped arriving.
var snap = _.debounce(function(event){
//Check each page view
$.each($('.page-contents'), function(index, element){
//If the page view is 70% of the screen and we are allowed to snap, snap into view
if($(element).isNearScreen(0.7)){
$('html,body').animate({
scrollTop: $(element).offset().top
}, 300);
}
});
}, 500);
最后,我绑定到窗口的滚动事件
$(window).on('scroll', snap});
(极简化)HTML:
<div class="page">
<div class="page-contents"></div>
</div>
<div class="page">
<div class="page-contents"></div>
</div>
<div class="page">
<div class="page-contents"></div>
</div>
<div class="page">
<div class="page-contents"></div>
</div>
和CSS:
.page{
height: 750px;
width: 100%;
margin: 10px 0;
background: gray;
}
.page-contents{
height: 100%;
width: 100%;
}
我尝试过以下操作,但没有成功:
snap
的动画部分。在动画之后,将其设置为true,然后在500ms后将其设置为false(理论上应该防止双击)。.stop()
动画之前,在元素上调用snap
。event.preventDefault()
。_.debounce
延迟时间。 有趣的是,较低的_.debounce
延迟(200-300毫秒)似乎会加剧问题,并且更高的_.debounce
延迟(1000毫秒)似乎可以修复它。然而,这不是一个可接受的解决方案,因为它感觉“长”等待1秒钟的页面“快速”。如果我能提供任何其他信息,请告诉我。我不知所措!
答案 0 :(得分:1)
我认为这是事件的组合以及_.debounce
的工作原理。我注意到在小提琴中(在Chrome中)这些元素在拍完后很久就会“抖动”。如果您将控制台日志放在快照事件处理程序中,即使没有滚动输入,您也可以看到它在捕捉后不断被调用。
这必须是滚动动画本身设置快照,我试图设置一个标志,以防止动画完成后双重捕捉和清除标志 - 但是这不起作用我认为因为_.debounce
将事件排队以后发生(在动画结束并清除标志之后)。
那么将此作为快照处理程序的开头添加是什么工作:
var nosnap = false;
var snap = _.debounce(function(event){
// Don't snap if already animating ...
if (nosnap) { nosnap = false; return; }
nosnap = true;
这可以防止动画直接触发下一个快照事件 - 但是如果在动画期间再次滚动,则会导致问题。
所以,这有点像黑客。理想情况下,您希望能够分辨出导致滚动事件的原因并作出相应的反应,但没有简单的方法可以做到这一点。
我绝对认为你在处理第二个滚动事件时也需要停止动画。