views:

62

answers:

3

I'm building a simple slideshow using jQuery. I have the basics down, but as a relative jQuery newcomer, I feel like I'm fighting with the animation queue and not making much progress.

I've created a slideshow by making a function that receives a numeric index of the first slide to show. It fades in the slide, pauses, fades out the slide, then calls itself, passing the numeric index of the next slide. This much works perfectly.

The problem happens when I try to allow user interaction. I've created a button that should allow the user to start the slideshow at a specific slide. This button clears the queue, then fades out the current slide, then starts the slideshow function, passing in the index of a specific slide to start at. This also works well. Problems start to happen when the button is clicked quickly several times. Quirky things start happening, like the intended slide will fade in, but only halfway, or the slide following it fades in several times in a row.

I've tried throwing "stop()" and "clearQueue()" in at semi-random places, but so far I haven't much luck. For someone with a firm grasp on how the animation queue works, this is (hopefully) a simple fix -- unfortunately, that someone is not me yet.

Here's the heart of the code. To see a live, ultra-simplified version of the problem, see: http://martinsmucker.com/demo/slideshow.html

HTML:

<div id="slideshow_container">
    <div class="slide" id="slide1"></div>
    <div class="slide" id="slide2"></div>
    <div class="slide" id="slide3"></div>
    <div class="slide" id="slide4"></div>
</div>
<button>Purple</button>

jQuery:

runSlideShow(1);

$('button').click(function(){
    $('.slide:visible').clearQueue().fadeOut(500, function(){
        runSlideShow(3);
    });
});

function runSlideShow (slideNumber)
{
    $('#slide' + slideNumber).fadeIn(500).delay(3000).fadeOut(500, function() {
        // if we're on the last slide (number 4), start over at number 1
        if (slideNumber == 4) {
            runSlideShow(1);
        }
        // otherwise, just move to the next slide
        else {
            slideNumber++;
            runSlideShow(slideNumber);
        }
    });
}

Any help is much appreciated, thanks!

+1  A: 

One way I've handled this in the past is to use a tracking variable to indicate when animation is executing (such as 'isAnimating'). In the click listener, I first check to see if isAnimating=true. If so, skip it. Otherwise, set isAnimating=true and execute the animation. Inside the callback for the animation, I reset isAnimating back to false.

Brian Flanagan
Thanks for the idea. This will be sure to come in handy for larger and more complex problems.
mlms13
+1  A: 

Disable the button until the animation is complete then re-enable?

If this is going to be a larger project then it might be an idea to use a good scroller plugin (although it is great to make one for yourself).

Have a look at JQuery Scrollable i have just implemented on this page tonight (wait a couple of sec for the top are to load, or check out the bottom right history section with the arrows.

Top Scroller Code

$('.section_2').scrollable({circular: true, mousewheel: true, speed: 1000}).autoscroll({
    interval: 5000      
});
var api = $(".section_2").data("scrollable");
// do something upon scroll
api.onSeek(function() {
    //console.info("current position is: " + this.getIndex())
});

Bottom Scroller Code

$('.timeline-container').scrollable({circular: true, next: ".timeline-next", prev: ".timeline-prev"});
var api_timeline = $(".timeline-container").data("scrollable");
//console.info(api_timeline.getSize());
var timeline_random_index = Math.floor( Math.random() * ( api_timeline.getSize()+1) )+1;
//console.info(timeline_random_index);
api_timeline.seekTo( timeline_random_index, 0 );
Shadi Almosri
That's a good idea, and thanks for the code examples. I'll be sure to take a look at this.
mlms13
+3  A: 

Instead of using .clearQueue() use .stop(true,true) (ref)

Here is an updated demo.

fudgey
+1 for a working demo :)
Shadi Almosri
Oops, I replied to your comment as you were changing it to an answer... :) I hadn't done this, because from a comment here: http://api.jquery.com/delay/, it didn't sound possible to break a `delay()` with `stop()`. From your example, it looks like this works, though. Thanks, I'm going to do a bit more testing.
mlms13
This definitely seems to work. I just tried clicking that "Purple" button over and over as fast as I could and there was no breaking. Thanks!
mlms13
Hmm, upon further testing, I can still break it reliably by clicking the 'Purple' button while any slide is in the process of fading out. This causes the orange slide to display twice, skipping the blue slide. Ideas?
mlms13
I've updated the demo (http://jsfiddle.net/T2znT/1/)... basically I added a check to see if a slide is being animated: `$('.slide:visible:not(:animated)')`
fudgey
Okay, definitely seems unbreakable this time :) In the example, a slide spends 1/4 of its time being animated, which could be a problem, however for the real slideshow, it will only be animated for about 1/20 of the time that it's visible, so this limitation shouldn't be a problem. Thanks again for the great answer and helpful example!
mlms13