7

我正在使用绑定/取消绑定鼠标滚轮滚动,基于此 SO 响应:

Jquery,取消绑定鼠标滚轮事件,然后在动作完成后重新绑定它?

我正在从 delta 向上挖掘事件树,以仅针对 X 鼠标滚轮值。一切运作良好。我试图克服的问题:我想简单地向前/向后滚动一个面板,然后停止滚动。目前,我在移动后立即取消绑定鼠标滚轮事件,这有效地停止了滚动......但取消绑定鼠标滚轮事件也会顶住页面。我需要的是能够嗅探第一个 deltaX 值的方向,然后采取行动并停止收听。我需要寻找自动滚动的答案吗?绑定/解除绑定感觉很糟糕,但我一生都无法弄清楚如何在一个动作后踢出,同时在该动作完成后仍然能够滚动。这是我的鼠标滚轮功能:

function mouseHandler(event, delta) {
$('.homeScrollable').bind('mousewheel', mouseHandler);

var deltaX = event.originalEvent.wheelDeltaX;
//console.log(event.originalEvent.wheelDeltaX);

if(deltaX <= 0) {
    //move forward 1 screen and stop
    scrollapi.move(1);
    $('.homeScrollable').unbind('mousewheel', mouseHandler);
} else if(deltaX > 0) {
    //move backward 1 screen and stop
    scrollapi.move(-1);
    $('.homeScrollable').unbind('mousewheel', mouseHandler);
}   
event.preventDefault();

// Rebind mousewheel event:
$('.homeScrollable').bind('mousewheel', mouseHandler);     };

我也看过设置一个计时器,a la:

jquery mousewheel插件:如何每次滚动只触发一个功能

这似乎非常有希望,但不行。这是这个人的插件页面:http: //brandonaaron.net/code/mousewheel/docs

感谢检查出来。

4

4 回答 4

19

由于 DOM 没有提供任何方法来区分第一个触发的滚动事件和随后发生的滚动事件,这些事件恰好是同一滚动运动的一部分,我们不得不考虑区分它们的间接方法。

如果您快速滚动浏览任何特定元素,则会按顺序多次触发滚动事件。使用以下代码,我们可以大致了解这种情况发生的频率:

$('#exampleDiv').bind('mousewheel', function () {
    console.log(new Date().getTime());
});

当您滚动该 div 时,您将获得如下所示的控制台输出:

// Using mouse wheelbar
251327626600149
251327626600215
251327626600265
251327626600282
251327626600332
251327626600365

// Using touchpad
251327626626207
251327626626225
251327626626261
251327626626276
251327626626312
251327626626345

查看此输出,似乎mousescroll事件通常在 20 毫秒到 60 毫秒之间的某个时间触发。为了安全起见,我们将上限设为 100 毫秒。这是非常有用的,因为我们可以使用它来区分属于同一操作的滚动事件和可能不同且由用户有意发起的滚动事件。

您可以从这里做的是创建一个全局可访问的“时间戳”变量,每次mousescroll触发事件时更新它,无论成功与否。像这样的东西:

var timeStamp = new Date().getTime();

$('#exampleDiv').bind('mousewheel', function (event) {
    var timeNow = new Date().getTime();

    // Need to prevent the default scrolling behavior
    event.preventDefault();

    // If the last mousescroll happened less that 100 ms ago, update
    // timeStamp and do nothing
    if (timeNow - timeStamp < 100) {
        timeStamp = timeNow;
        return;
    } else {
        timeStamp = timeNow;
        scrollToSomeOtherDiv();
    }
});

这有效地忽略了mousescroll在它们之前的初始事件之后触发的所有事件,但在用户暂停 100 毫秒后再次开始工作。

这将解决您的问题,除非您的scrollToSomeOtherDiv()功能涉及某种耗时的动画。您当然可以创建一个全局 boolean isAnimating,并在每次mousescroll触发事件时检查它是否为真(确保在动画结束后在回调中将其变为假)。

这会奏效,只是它会给用户带来不和谐的体验。即使在看到动画开始后,想要快速滚动浏览两个面板的人也可能不会在滚动之间暂停。上面的代码会将它们的所有mousescroll事件视为相同滚动动作的一部分,并继续忽略它们!

在这种情况下,您可以简单地使用动画时间作为您的阈值。一旦动画开始,您设置一个时间戳,然后忽略mousescroll该时间段内的所有事件。我在这里写了一个例子:http: //jsfiddle.net/Sg8JQ/

相关代码在这里:

var lastAnimation = 0;
var animationTime = 1000; // in ms
var quietPeriod = 500; // in ms, time after animation to ignore mousescroll

function scrollThis(event, delta, deltaX, deltaY) {
    var timeNow = new Date().getTime();

    // change this to deltaX/deltaY depending on which
    // scrolling dir you want to capture
    deltaOfInterest = deltaY;

    if (deltaOfInterest == 0) {
        // Uncomment if you want to use deltaX
        // event.preventDefault();
        return;
    }

    // Cancel scroll if currently animating or within quiet period
    if(timeNow - lastAnimation < quietPeriod + animationTime) {
        event.preventDefault();
        return;
    }

    if (deltaOfInterest < 0) {
        if ($('.active').next('div').length) {
            lastAnimation = timeNow;
            $('.active').removeClass('active').next('div').addClass('active');
            $('html,body').animate( {
                scrollTop: $('.active').offset().top }, animationTime);
        }
    } else {
        if ($('.active').prev('div').length) {
            lastAnimation = timeNow;
            $('.active').removeClass('active').prev('div').addClass('active');
            $('html,body').animate( {
                scrollTop: $('.active').offset().top }, animationTime);
        }
    }
}

// Note: mousewheel() is defined in the mousewheel plugin by Brandon Aaron
// You could do without it, but you'd need to adjust for firefox and webkit
// separately.
//
// You couldn't use $(document).scroll() because it doesn't allow you to
// preventDefault(), which I use here.
$(document).mousewheel(scrollThis);

我还包括quietPeriod了超出动画时间的时间,在此期间您要继续忽略mousescroll事件。如果您希望在动画完成后立即“响应”滚动,则可以将其设置为 0。

于 2012-01-27T02:18:12.710 回答
1

我认为“pikappa”提供的解决方案是纯粹的性感。

对我来说,它在 Firefox 上运行良好,但在 Chrome 26.0.x 下滚动时我遇到了“闪烁”

A simple dirty "patch" to his amazing code is adding "return false;" at line 57 and 64 of his JSfiddle

于 2013-04-28T23:31:03.653 回答
0

看看这个,看看你的想法。首先,我不会绑定到鼠标滚轮,因为 Firefox 使用 DOMMouseScroll 代替。这也使用了一个不同的增量处理程序,它与所有其他浏览器使用的相反。而是绑定到规范化行为的 jQuery 滚动事件。您可以跟踪最后一个滚动顶部位置以确定用户是向上还是向下滚动。

我有点假设您在各个部分之间制作动画。是否有一个回调函数可以用来确定是否应该滚动?我创建了一个全局来跟踪元素当前是否正在动画。如果是这样,那么我们就不必费心执行该功能了。最后,我注意到滚动动画的回调似乎在最终滚动事件发生之前触发,所以我实际上不得不在那里使用 setTimeout。我不喜欢它,但我不知道如何让回调正常工作。

http://jsfiddle.net/Gj3Qy/

var lastScrollTop = 0;
var isDoingStuff = false;

$(document).scroll(function(event) {
    //abandon 
    if(isDoingStuff) { return; }

    if ($(this).scrollTop() > lastScrollTop) {
        //console.log('down');
        $('.active').removeClass('active').next('div').addClass('active');
        isDoingStuff = true;
        $('html,body').animate( {scrollTop: $('.active').offset().top }, 1000, function() {
            setTimeout(function() {isDoingStuff = false;}, 100);
            console.log('off');
        });
    } else {
        //console.log('up');
    }
    lastScrollTop = $(this).scrollTop();
})
于 2012-01-04T17:06:41.803 回答
0

所以,这就是我正在做的事情。它似乎工作,但仍然是错误的。

i=0;
$("#test").mousewheel(function(event, delta) {
    event.preventDefault();
    clearTimeout($.data(this, 'timer'));        

    // check if the mouse moved on right/up
    if( delta > 0 ) {               
        i++;                

        // hack to execute function just once
        if( i == 3 ) {                  
            $('#child').fadeIn().html("<h1>next</h1>").fadeOut();  // do something once when i=4, but happening multiple times
        }           

    } else { // check if mouse moved left/down

        i--;                

        if(i==-3) {     
            $('#child').fadeIn().html("<h1>previous</h1>").fadeOut();  // do something once when i=-4, but happening multiple times
        }               

    } // end if delta

    // only execute another mousewheel scroll after some delay
    $.data(this, 'timer', setTimeout(function() {
        i = 0;
    }, 100));

});

所以,虽然增加的 i 打印得很好,但如果我取消注释 if i = 4 打印行,它就不能正常工作(如果你使用鼠标,你可能需要用力滚动,我在我的触控板上尝试)。虽然我理解它,因为我会随着您获得增量值而不断增加,但您仅在我达到 4 时执行某些操作,并且仅在滚动停止 250 毫秒时才重置其值,此代码似乎将 i 重置为 0 至少两次或三次所以我==4 实际上执行了两次或三次。也许你可以找到可能发生的事情。我快到了,只需要修复那个错误。

尝试在 if(i==4) 语句中添加一些变化,如 fadeIn、fadeOut 效果,并且更加突出。

编辑:

这是新编辑的版本。鼠标滚轮链接很旧,所以现在可能显示得更好。如果你快速移动,你会看到它闪烁......

于 2012-01-07T02:08:10.473 回答