7

このSO応答に基づいて、マウスホイールのスクロールにバインド/バインド解除を使用しています:

Jquery、マウスホイールイベントのバインドを解除し、アクションが完了した後に再バインドしますか?

X マウスホイールの値のみをターゲットにするために、デルタからイベント ツリーをトンネリングしています。すべてがうまくいっています。私が克服しようとしている問題: 1 つのパネルを前後にスクロールしてから、スクロールを停止したい。現在、移動直後にマウスホイールイベントのバインドを解除しているため、スクロールが効果的に停止します...ただし、マウスホイールイベントのバインドを解除すると、ページもジャックされます。私が必要としているのは、方向の最初の deltaX 値を盗聴し、移動して聞くのをやめることです。回答を自動スクロールする必要がありますか? バインド/バインド解除はぎこちなく感じますが、私の人生では、1回の移動後にキックアウトする方法を理解することはできませんが、その移動が完了した後にスクロールすることはできません. これが私のマウスホイール機能です:

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

また、タイマーの設定も検討しました。

jquery mousewheel プラグイン: スクロールごとに 1 つの関数のみを起動する方法

これは信じられないほど有望に思えましたが、うまくいきませんでした。この人のプラグイン ページは次のとおりです: 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、イベントが発生するたびに true かどうmousescrollかを確認できます (アニメーションが終了したら、コールバックで必ず false にします)。

ユーザーに耳障りな体験を提供する可能性があることを除けば、それは機能します。2 つのパネルをすばやくスクロールしたい人は、アニメーションの開始を見た後でも、スクロール間で一時停止しない可能性があります。上記のコードは、すべての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 に設定できます。

于 2012-01-27T02:18:12.710 に答える
1

「ぴかっぱ」が提供する解決策は、純粋なセクシーさだと思います。

私にとってはFirefoxで完全に機能していましたが、Chrome 26.0.xでスクロール中に「ちらつき」が発生していました

彼の驚くべきコードへの単純な汚い「パッチ」は、彼のJSfiddleの57 行目と 64 行目に「 return false;」を追加することです。

于 2013-04-28T23:31:03.653 に答える
0

これを見て、あなたがどう思うか見てください。まず、Firefoxは代わりにDOMMouseScrollを使用するため、mousewheelにバインドしません。これは、他のすべてのブラウザーで使用されているものの逆である別のデルタハンドラーも使用します。代わりに、動作を正規化する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 印刷行のコメントを外すと、正しく機能しません (マウスを使用している場合は、トラックパッドで試してみると、激しくスクロールする必要があるかもしれません)。デルタ値を取得するにつれて i が継続的に増加することは理解していますが、i が 4 に達したときにのみ何かを実行し、スクロールが 250 ミリ秒停止した場合にのみその値をリセットします。このコードは i を少なくとも 2 回または 3 回 0 にリセットするようです。 ==4 は実際には 2 回または 3 回実行されます。何が起こっているのかを見つけることができるかもしれません。私はほとんどそこにいます、ただそのバグを修正する必要があります。

そのif(i = = 4)ステートメント内でfadeIn、fadeOut効果などの変化するものを追加して、より目立つようにしてください。

編集:

新しい編集版はこちらです。マウスホイールのリンクは古いので、おそらくこれでよくわかります。速く動くと、ちらつくのが見えます...

于 2012-01-07T02:08:10.473 に答える