jQuery Tools Scrollable with Mousewheel - 滚动一个位置并停止

时间:2012-01-04 16:36:56

标签: jquery mousewheel scrollable

我正在使用bind / unbind进行鼠标滚轮滚动,基于此SO响应:

Jquery, unbinding mousewheel event, then rebinding it after actions are completed?

我正在从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);     };

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

jquery mousewheel plugin: how to fire only one function every scroll

看起来非常有前景,但是没有去。这是这个家伙的插件页面: http://brandonaaron.net/code/mousewheel/docs

感谢您查看。

4 个答案:

答案 0 :(得分: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 ms。这是非常有用的信息,因为我们可以用它来区分属于同一动作的滚动事件和可能由用户有意识地发起的滚动事件。

您可以从这里做的是创建一个全局可访问的'timestamp'变量,每次触发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 ms后再次开始工作。

这可以解决您的问题,除非您的scrollToSomeOtherDiv()函数涉及某种耗时的动画。您当然可以创建一个全局布尔值isAnimating,并在每次触发mousescroll事件时检查它是否为真(确保在动画结束后在回调中将其设置为false)。 / p>

这样可行,但它可以为用户提供刺耳的体验。即使在看到动画开始后,想要快速滚动两个面板的人也可能不会在滚动之间暂停。上面的代码会将所有mousescroll事件视为同一滚动动作的一部分,并继续忽略它们!

在这种情况下,您可以简单地使用动画时间作为阈值。您可以在动画开始后设置timeStamp,然后在该段时间内忽略所有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.

答案 1 :(得分:1)

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

对我而言,它在Firefox上工作得很好,但在Chrome 26.0.x下滚动时我遇到了“闪烁”

一个简单的脏“补丁”令他惊人的代码是在 第57行和第64行添加“返回false; ”他的 JSfiddle

答案 2 :(得分:0)

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

我有点假设你在各个部分之间制作动画。是否有回调函数可用于确定是否应该滚动?我创建了一个全局来跟踪元素当前是否具有动画效果。如果是这样,那么我们不打扰执行该功能。最后,我注意到滚动动画的回调似乎在最终滚动事件发生之前触发,所以我实际上必须在那里使用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();
})

答案 3 :(得分: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打印行,它就无法正常工作(如果使用鼠标,可能需要滚动,我试着在我的触控板上)。虽然我理解它,因为当你得到delta值时我会不断增加,你只有当我达到4并且只在滚动停止250ms时才重置它的值,这段代码似乎将i重置为0至少两次或三次所以我== 4实际执行两次或三次。也许你可以找到可能发生的事情。我几乎就在那里,只需要修复那个bug。

尝试在if(i == 4)语句中添加更改为fadeIn,fadeOut效果的内容,并使其更加突出。

编辑:

Heres the new edited version。鼠标滚轮链接很旧,所以现在可能会更好。如果你快速移动,你会看到它闪烁......