Googlebot
Googlebot

Reputation: 15683

Synchronizing jQuery setInterval for slideshow

I created a very basic slideshow by repeating the jQuery effects with setInterval. However, there is a mismatch in timing after each cycle of the setInterval. After a few cycles, it leads to a visible mismatch in two parallel slide effects.

The code is

$(document).ready(function(){
var frontslides =$("#first li").length,
    slideTime=500,
    slideCycle=parseInt(slideTime*frontslides);

function frontSlide(){
    $("#first li").each(function(n){
        $(this).delay(slideTime*n).fadeIn(400).fadeOut(100);
        $("#second li").eq(n).delay(slideTime*n).fadeIn(400).fadeOut(100);
    });}
frontSlide();setInterval(frontSlide, slideCycle);});​

The working example is here

To save your valuable time, its speed is fast, but it happens on any speed. After a few cycles, you can see that left and right slides are no longer synchronized.

Upvotes: 0

Views: 1184

Answers (3)

Jared Farrish
Jared Farrish

Reputation: 49238

Since I opened my mouth, here is the version I came up with. I added a blocked flag in there to indicate an effects loop was currently running, hopefully preventing the script from overrunning itself. I also added a factor value so that if overrunning did occur, it would loop more quickly, to preserve the appearance that the loop was not disrupted.

If you have any other questions about it, just let me know. It's pretty straight forward.

$(document).ready(function() {
    var $sel = $('#left, #right').find('li:first-child'),
        slidetime = 200,
        timein = 400,
        timeout = 100,
        blocked = false;

    var fadeout = function() {
        $sel.fadeOut(timeout, next);
    };

    var next = function() {
        if ($sel.is(':last-child')) {
            $sel = $sel.siblings(':first-child');
        } else {
            $sel = $sel.next();
        }

        blocked = false;
    };

    (function slider() {
        var factor = .2;

        if (!blocked) {
            factor = 1;
            blocked = true;

            $sel.fadeIn(timein, fadeout);
        }

        setTimeout(slider, slidetime * factor);
    })();
});​

http://jsfiddle.net/j63vH/

Upvotes: 1

Mattias Buelens
Mattias Buelens

Reputation: 20179

First approach: setTimeout

You could move the transitioning to the next slide in a separate function and call it with setTimeout. Then, you just store the slide number in a variable and increment it after each function call.

$(document).ready(function(){
    var firstSlides = $("#first li"),
        secondSlides = $("#second li"),
        nbSlides = firstSlides.length,
        slideTime = 500,
        nextSlide = 0;

    function slideshow() {
        firstSlides.eq(nextSlide).fadeIn(400).fadeOut(100);
        secondSlides.eq(nextSlide).fadeIn(400).fadeOut(100);

        nextSlide = (nextSlide+1) % nbSlides;
        setTimeout(slideshow, slideTime);
    }

    slideshow();
});

Here's a fiddle of this first approach.

Second approach: promises

If you want to absolutely guarantee that both animations are completed when starting the next animation, you can use the new promises from jQuery 1.6. You can call $.promise() on both jQuery objects to get a promise which will be resolved when all animations of that element are completed. Then, you can set up a master promise with $.when(promises...) which will be resolved when all the given promises are resolved and set up a then handler.

$(document).ready(function(){
    var firstSlides = $("#first li"),
        secondSlides = $("#second li"),
        nbSlides = firstSlides.length,
        fadeInTime = 400,
        fadeOutTime = 100,
        nextSlide = 0;

    function slideIn() {
        var p1 = firstSlides.eq(nextSlide).fadeIn(400).promise();
        var p2 = secondSlides.eq(nextSlide).fadeIn(400).promise();

        $.when(p1, p2).then(slideOut);
    }

    function slideOut() {
        var p1 = firstSlides.eq(nextSlide).fadeOut(100).promise();
        var p2 = secondSlides.eq(nextSlide).fadeOut(100).promise();

        nextSlide = (nextSlide+1) % nbSlides;

        $.when(p1, p2).then(slideIn);
    }

    slideIn();
});

For a simple slideshow, you probably won't notice much of a difference. However, with promises you can make much more advanced transition sequences with funky timings while still maintaining synchronisation.

Here's a fiddle of that approach.

Upvotes: 4

Martin Ernst
Martin Ernst

Reputation: 5679

Set the animations to both run at the "same" time:

function frontSlide(){
    $("#first li").each(function(n){
        var _ = $(this);
        setTimeout(function() {
            _.fadeIn(400).fadeOut(100);
            $("#second li").eq(n).fadeIn(400).fadeOut(100);
        }, slideTime*n);
    });
}

Upvotes: 2

Related Questions