1
votes

I am putting together a powerpoint type web page, where the slides slide in and and of view with various animations. The animation sequence is held in an array (ultimately loaded from a database), and called using eval()

When the slide is clicked, the next set of animations is actioned, but I need each one to wait until the previous one has finished. So for the code below, when the SeqNo is 1, I want the ShowSlideLeft to wait until HideSlideRight as finished.

I have looked at callbacks and promises, but I cant get them to work as I cannot hard code the chaining events.

var SeqNo;

$(document).ready(function () {
    $('.slide').click(function (e) {
        ActivateSequence(++SeqNo);
    });

    SeqNo = 0;

    SetupSequence();

    ActivateSequence(SeqNo);
});

function SetupSequence() {
    Sequence = [];

    Sequence[0] = [];
    Sequence[0][0] = "ShowSlideLeft('S1');";

    Sequence[1] = [];
    Sequence[1][0] = "HideSlideRight('S1');";
    Sequence[1][1] = "ShowSlideLeft('S2');";
}  

function ActivateSequence(Seq) {
    var action;
    var element;

    if (Seq < Sequence.length) {
        for (var i = 0; i < Sequence[Seq].length; i++) {
            eval(Sequence[Seq][i]);
        }
    }
}

function ShowSlideLeft(div) {
    var showoptions = { "direction": "left", "mode": "show" };
    $('#' + div).effect("slide", showoptions, 1000);
}

function ShowSlideRight(div) {
    var showoptions = { "direction": "right", "mode": "show" };
    $('#' + div).effect("slide", showoptions, 1000);
}

function HideSlideLeft(div) {
    var showoptions = { "direction": "left", "mode": "hide" };
    $('#' + div).effect("slide", showoptions, 1000);
}

function HideSlideRight(div) {
    var showoptions = { "direction": "right", "mode": "hide" };
    $('#' + div).effect("slide", showoptions, 1000);
}

Many thanks for any help you can give.

2
Well, you know that each animation will last 1000ms, why not make each animation wait 1000ms before it's allowed to begin (if another has previously started and not ended)?Wes Foster

2 Answers

1
votes

You can pass .effect() a fourth parameter callback, which is a function to be called after the animation is complete. So you can change your custom functions by adding a callback parameter and passing it down to the .effect call, like this:

function ShowSlideLeft(div, callback) {
    var showoptions = { "direction": "left", "mode": "show" };
    $('#' + div).effect("slide", showoptions, 1000, callback);
}

You will also need to change SetupSequence and ActivateSequence, and I suggest that you:

  • replace eval with window[function_name] to implement the actual function call
  • initialize the Sequence array with objects rather than strings (they can be stored in db as JSON strings, so this change should not impact on the DB design)

The result would be something like this:

function SetupSequence() {
    Sequence = [];

    Sequence[0] = [];
    Sequence[0][0] = { action: 'ShowSlideLeft', target: 'S1' };

    Sequence[1] = [];
    Sequence[1][0] = { action: 'HideSlideRight', target: 'S1' };
    Sequence[1][1] = { action: 'ShowSlideLeft', target: 'S2' };
}  

and

function ActivateSequence(Seq, Step) {
    if (Seq < Sequence.length) {
        (window[Sequence[Seq][Step].action])(Sequence[Seq][Step].target, function() {
            // when the first effect is complete, execute the next one
            ActivateSequence(Seq, Step + 1);
        });
    }
}

Please notice that with a slight modification you can also pass an array of parameters to the custom effect functions instead of the single parameter target as it is now.

And of course you'll need to invoke ActivateSequence with ActivateSequence(SeqNo, 0) in the click handler.

0
votes

I see that you're already using jQuery, why not use their animation API?

$('.some-element').animate({width:200}, 400, callback);

You provide an object of changes, the time in which it should execute and a callback function.