jQuery でアニメーションの前後に .css() とか .addClass() とかしたい時に .queue() を使う

やりたいこと

jQueryanimate メソッドを使って見た目を作っている時、アニメーションの完了後にクラスを付与したいとか、 CSS を変更したいというようなことはよくある。これをエレガントな感じに書きたい。

何も考えずに jQuery っぽく書いてみる

$('.target')
    .animate({
        left: '100px',
        top: '100px'
    }, 500, 'swing')
    .css('background-color', 'red');

これだとうまくいかない。アニメーションが終了した時に背景色が赤になってほしいが、アニメーションが始まったと同時に赤くなってしまう。

animate のコールバック関数を指定する

解決策のひとつとして、 animate メソッドの最後の引数にコールバック関数を指定する方法がある。指定したコールバック関数は、アニメーションが完了した直後に実行されることになっている。

$('.target')
    .animate({
        left: '100px',
        top: '100px'
    }, 500, 'swing', function () {
        // アニメーションが完了した後に実行される
        $(this).css('background-color', 'red');
    });

正常に赤くなってくれた。これの問題点は、連なる動作が増えるとどんどん入れ子が深くなってしまうことだ。

$('.target').animate({
    left: '100px',
    top: '100px'
}, 500, 'swing', function() {
    $(this).css('background-color', 'red').animate({
        top: '0'
    }, 500, 'swing', function() {
        $(this).css('background-color', 'green').animate({
            'left': '0'
        }, 500, 'swing', function() {
            $(this).css('background-color', 'blue');
        });
    });
});

$.Deferred で連結する

かと言って、この程度の処理で Deferred の仕組みを使うのは少し冗長な感じがある……。

var tgt = $('.target');
$.when(tgt.animate({
    left: '100px',
    top: '100px'
}, 500, 'swing'))
.then(function () {
    tgt.css('background-color', 'red');
})
.then(function () {
    return tgt.animate({
        top: '0'
    }, 500, 'swing');
})
.then(function () {
    tgt.css('background-color', 'green');
})
.then(function () {
    return tgt.animate({
        left: '0'
    }, 500, 'swing');
})
.then(function () {
    tgt.css('background-color', 'blue');
});

.queue() メソッドを使う

そもそも、 jQueryanimate メソッドなどは、 jQuery 内部のキューという順番待ちの仕組みで制御されている。この仕組みを使い、アニメーションが終わったら次のを実行して……。という順次処理を実現している。

したがって、このキューに任意の処理を追加出来ればいい。 jQuery にはそのためのメソッド queue が用意されている。

queue を使った書き方をすると以下の様なコードになる。

$('.target')
    .animate({
        left:'100px',
        top:'100px'
    }, 500, 'swing')
    .queue(function () {
        $(this).css('background-color', 'red').dequeue();
    })
    .animate({
        top:'0'
    }, 500, 'swing')
    .queue(function () {
        $(this).css('background-color', 'red').dequeue();
    })
    .animate({
        left:'0'
    }, 500, 'swing')
    .queue(function () {
        $(this).css('background-color', 'red').dequeue();
    });

注意点として、 queue メソッドの引数に指定する関数内で、 $(this).dequeue() をする必要がある。 dequeue は処理が終了したという合図になるため、記述しないとキューの処理がそこで止まったままになってしまう。