スクロールイベントの開始 / 終了 / 方向を判定する jQuery プラグインを書いてみた

バージョンアップしました。詳細は以下記事をご覧ください。

[Ex Scroll] スクロールイベントの開始/終了、方向を判別できる jQuery プラグイン - Cyokodog






需要なさそうですが、備忘録ということで。

只今、以前作成した exFixed.js という jQuery プラグインの機能拡張を行っており、その中で、

  • スクロールイベントの開始、終了時にのみ処理を行う
  • その際、スクロールイベントのスクロール方向に応じて処理を変える


という実装が必要になりました。

jQuery におけるスクロールイベントの割当は下記のような記述になりますが、このままではイベントの開始,終了,スクロール方向は判別できません。

$( element ).scroll( function( evt ){
    //スクロールイベント内処理
});


以下の実装で判別できないか考えてみました。

スクロール方向の判別

  • スクロールイベント開始前のスクロール位置 ( scrollTop , scrollLeft ) を事前に保持しておく
  • スクロールイベント中のスクロール位置と保持しておいた値を比較し、値のずれの有無でスクロール方向を判別する
スクロールイベントの開始、終了の判別

  • 状態フラグを設けこれを参照することで開始 / 終了状態を判別する
  • スクロールイベントが開始されたら、「開始フラグ(1)」を立てる
  • スクロールイベントは連続して起こるので、後続するスクロールで「開始フラグ(1)」が立ってた場合は、「スクロール中フラグ(2)」を立てる
  • スクロールイベント内では setTimeout を使用し「終了フラグ(0)」セットルーチンを毎回起動する。但し、前回コール分の setTimeout が終了してない場合は、clearTimeout でこれをクリアする

jQuery プラグインで実装


汎用的に使用できるようプラグイン化してみました。

ソース

(function($j){
    $j.ex = $j.ex || {};
    $j.ex.scrollEvent = function( target , config ){
        var o = this;
        if( typeof config == 'function') config = {
            callback : config
        }
        var c = o.config = $j.extend({},$j.ex.scrollEvent.defaults,config,{
            target : target
        });
        c.status = 0;
        c.scroll = o.getPos();
        c.target.scroll(function( evt ){
            if (o.isMove()) {
                c.status = (c.status == 0 ? 1 : (c.status == 1 ? 2 : c.status) );
                c.callback( evt , c );
            }
            if(c.tm) clearTimeout(c.tm);
            c.tm = setTimeout(function(){
                o.isMove();
                c.status = 0;
                c.callback( evt , c );
            },c.delay);
        });
    }
    $j.extend($j.ex.scrollEvent.prototype,{
        isMove : function(){
            var o = this, c = o.config;
            var pos = o.getPos();
            var scrollY = (pos.top != c.scroll.top);
            var scrollX = (pos.left != c.scroll.left);
            if(scrollY || scrollX){
                c.scrollY = scrollY;
                c.scrollX = scrollX;
                c.prevScroll = c.scroll;
                c.scroll = pos;
                return true;
            }
            return false;

        },
        getPos : function(){
            var o = this, c = o.config;
            return {
                top : c.target.scrollTop(),
                left : c.target.scrollLeft()
            }       
        }
    });
    $j.ex.scrollEvent.defaults = {
        delay : 100
    }
    $j.fn.exScrollEvent = function( config ){
        new $j.ex.scrollEvent(this , config);
        return this;
    };
})(jQuery);

使い方


jquery.js、jquery.exscrollevent.js の順で読み込ませます。

<script src="jquery.js"></script>
<script src="jquery.exscrollevent.js"></script>


コールバック関数の第 2 引数にパラメータオブジェクトが渡されますので、これを参照し状態を判別します。

jQuery(function($){
    var logbox = $('#logbox');
    var target = $('#container');
    target.exScrollEvent(function(evt , param){
        var txt='';
        //X方向にスクロール?
        if(param.scrollX){
            txt += 'X ';
        }
        //Y方向にスクロール?
        if(param.scrollY){
            txt += 'Y ';
        }
        //スクロール開始
        if(param.status == 1){
            txt += '<span style="color:pink">scroll start</span>';
        }
        //スクロール終了
        if(param.status == 0){
            txt += '<span style="color:pink">scroll end</span>';
        }
        //スクロール中
        if(param.status == 2){
            txt += 'scroll now';
        }
        //スクロール位置
        txt += '  ( top:'+param.scroll.top+' , left:'+param.scroll.left+' )';

        logbox.html(txt+'<br/>'+logbox.html())
    })
    $('#clear').click(function(){
        logbox.html('');
    })
});

Demo

その他のパラメータ

  • prevScroll

サンプルでは scroll プロパティでスクロールイベント時のスクロール位置を取得してますが、スクロールイベント発生前のスクロール位置は status が 1 (スクロール開始) の時に、prevScroll を参照することで取得できます。

  • delay

スクロールイベントの終了判定の間隔を指定できます。

$( element ).exScrollEvent({
    delay : 10, // 10ms
    callback : function( evt , param ){
        ...
    }
});
活用方法?

いまいち使いどころが思いつきませんが、jQuery Scroll Follow ( demo ) のような実装が若干簡単になるかな..? ...たいしたメリットじゃないですね。。

jQuery(function($){
    $(window).exScrollEvent({
        delay : 10,
        callback : function(evt,param){
            if(param.status == 0){
                $('#box').queue([]).animate({
                    top : 100 + param.scroll.top,
                    left : 100 + param.scroll.left
                })
            }
        }
    });
});


Demo