web-dev-qa-db-fra.com

Comment puis-je animer plusieurs éléments séquentiellement à l'aide de jQuery?

Je pensais que ce serait simple mais je n'arrive toujours pas à le faire fonctionner. En cliquant sur un bouton, je veux que plusieurs animations se produisent - l'une après l'autre - mais maintenant toutes les animations se produisent en même temps. Voici mon code - quelqu'un peut-il me dire où je vais mal?:

$(".button").click(function(){
  $("#header").animate({top: "-50"}, "slow")
  $("#something").animate({height: "hide"}, "slow")
  $("ul#menu").animate({top: "20", left: "0"}, "slow")
  $(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");
});
32
lnvrt

Vous pourriez faire un tas de rappels.

$(".button").click(function(){
    $("#header").animate({top: "-50"}, "slow", function() {
        $("#something").animate({height: "hide"}, "slow", function() {
            $("ul#menu").animate({top: "20", left: "0"}, "slow", function() {
                $(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");        
            });
        });
    });
});
24
jammus

La file d'attente ne fonctionne que si vous animez le même élément. Lord sait pourquoi ce qui précède a été voté mais cela ne fonctionnera pas.

Vous devrez utiliser le rappel d'animation. Vous pouvez passer une fonction comme dernier paramètre à la fonction d'animation et elle sera appelée une fois l'animation terminée. Cependant, si vous avez plusieurs animations imbriquées avec des rappels, le script deviendra assez illisible.

Je suggère le plugin suivant qui réécrit la fonction d'animation jQuery native et vous permet de spécifier un nom de file d'attente. Toutes les animations que vous ajoutez avec le même nom de file d'attente seront exécutées séquentiellement comme illustré ici .

Exemple de script

  $("#1").animate({marginTop: "100px"}, {duration: 100, queue: "global"});
  $("#2").animate({marginTop: "100px"}, {duration: 100, queue: "global"});
  $("#3").animate({marginTop: "100px"}, {duration: 100, queue: "global"});
29
redsquare

Je sais que c'est une vieille question, mais elle devrait être mise à jour avec une réponse pour les nouvelles versions de jQuery (1.5 et plus):

En utilisant le $.when fonction, vous pouvez écrire cette aide:

function queue(start) {
    var rest = [].splice.call(arguments, 1),
        promise = $.Deferred();

    if (start) {
        $.when(start()).then(function () {
            queue.apply(window, rest);
        });
    } else {
        promise.resolve();
    }
    return promise;
}

Ensuite, vous pouvez l'appeler comme ceci:

queue(function () {
    return $("#header").animate({top: "-50"}, "slow");
}, function () {
    return $("#something").animate({height: "hide"}, "slow");
}, function () {
    return $("ul#menu").animate({top: "20", left: "0"}, "slow");
}, function () {
    return $(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");        
});
26
Bill Barry

Une légère amélioration par rapport à la réponse de @ schmunk consiste à utiliser la file d'attente d'un objet jQuery en simple objet afin d'éviter tout conflit avec d'autres animations indépendantes:

$({})
    .queue(function (next) {
        Elm1.fadeOut('fast', next);
    })
    .queue(function (next) {
        Elm2.fadeIn('fast', next);
    })
    // ...

Une chose à garder à l'esprit est que, même si je n'ai jamais rencontré de problèmes pour ce faire, selon les documents l'utilisation des méthodes de file d'attente sur un wrapper d'objet simple n'est pas officiellement prise en charge.

Travailler avec des objets simples

À l'heure actuelle, les seules opérations prises en charge sur les objets JavaScript simples enveloppés dans jQuery sont: .data () ,. prop () ,. bind (), .unbind (), .trigger () et .triggerHandler ().

6
pgraham

Animer plusieurs balises séquentiellement

Vous pouvez tirer parti de la file d'attente d'animation intégrée de jQuery, si vous sélectionnez simplement une balise comme le corps pour faire la file d'attente globale:

// Convenience object to ease global animation queueing
$.globalQueue = {
    queue: function(anim) {
        $('body')
        .queue(function(dequeue) {
            anim()
            .queue(function(innerDequeue) {
                dequeue();
                innerDequeue();
            });
        });

        return this;
    }
};

// Animation that coordinates multiple tags
$(".button").click(function() {
    $.globalQueue
    .queue(function() {
        return $("#header").animate({top: "-50"}, "slow");
    }).queue(function() {
      return $("#something").animate({height: "hide"}, "slow");
    }).queue(function() {
        return $("ul#menu").animate({top: "20", left: "0"}, "slow");
    }).queue(function() {
        return $(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");
    });
});

http://jsfiddle.net/b9chris/wjpL31o0/

Alors, voici pourquoi cela fonctionne et ce qu'il fait:

  1. L'appel à $.globalQueue.queue() ne fait que mettre en file d'attente un appel à l'animation de votre balise, mais il la met en file d'attente sur la balise body.

  2. Lorsque jQuery frappe votre animation de balise dans la file d'attente du corps, l'animation de votre balise démarre, dans la file d'attente de votre balise - mais la façon dont le cadre d'animation jQuery fonctionne, tout rappel d'animation personnalisé provoque l'arrêt de la file d'attente d'animation d'une balise (le corps dans ce cas) , jusqu'à ce que l'animation personnalisée appelle la fonction passée dequeue(). Ainsi, même si les files d'attente de votre balise animée et de votre corps sont distinctes, la file d'attente de la balise de corps attend maintenant que sa dequeue() soit appelée. http://api.jquery.com/queue/#queue-queueName-callback

  3. Nous faisons simplement appel au dernier élément en file d'attente de la file d'attente de la balise pour continuer la file d'attente globale en appelant sa fonction dequeue() - c'est ce qui relie les files d'attente.

  4. Pour plus de commodité, la méthode globalQueue.queue Renvoie une référence this pour un chaînage facile.

setInterval

Par souci d'exhaustivité, il est facile d'atterrir ici simplement en cherchant une alternative à setInterval - c'est-à-dire que vous ne cherchez pas tant à coordonner des animations distinctes, qu'à les déclencher au fil du temps sans l'étrange montée en puissance votre animation causée par la façon dont les nouveaux navigateurs retarderont les files d'attente et les minuteries d'animation pour économiser le processeur.

Vous pouvez remplacer un appel à setInterval comme ceci:

setInterval(doAthing, 8000);

Avec ça:

/**
 * Alternative to window.setInterval(), that plays nicely with modern animation and CPU suspends
 */
$.setInterval = function (fn, interval) {
    var body = $('body');
    var queueInterval = function () {
        body
        .delay(interval)
        .queue(function(dequeue) {
            fn();
            queueInterval();
            dequeue();  // Required for the jQuery animation queue to work (tells it to continue animating)
        });
    };
    queueInterval();
};

$.setInterval(doAthing, 8000);

http://jsfiddle.net/b9chris/h156wgg6/

Et évitez ces explosions maladroites d'animation lorsqu'un onglet d'arrière-plan voit ses animations réactivées par le navigateur.

2
Chris Moschini

S'étendant sur la réponse de jammus, c'est peut-être un peu plus pratique pour de longues séquences d'animations. Envoyez une liste, animez chacun à son tour, en appelant récursivement à nouveau animate avec une liste réduite. Exécutez un rappel lorsque tout est terminé.

La liste ici est des éléments sélectionnés, mais il pourrait s'agir d'une liste d'objets plus complexes contenant différents paramètres d'animation par animation.

Voici un violon

$(document).ready(function () {
    animate([$('#one'), $('#two'), $('#three')], finished);
});

function finished() {
    console.log('Finished');
}

function animate(list, callback) {
    if (list.length === 0) {
        callback();
        return;
    }
    $el = list.shift();
    $el.animate({left: '+=200'}, 1000, function () {
        animate(list, callback);
    });
}
2
Sean

Vous pouvez également mettre vos effets dans la même file d'attente, c'est-à-dire la file d'attente de l'élément BODY.

$('.images IMG').ready(
   function(){
        $('BODY').queue(
            function(){
                $('.images').fadeTo('normal',1,function(){$('BODY').dequeue()});
            }
        );
    }
);

Assurez-vous d'appeler dequeue () dans le dernier rappel d'effet.

1
schmunk

Cela a déjà été bien répondu (je pense que la réponse de jammus est la meilleure) mais je pensais que je fournirais une autre option basée sur la façon dont je fais cela sur mon site Web, en utilisant la fonction delay() ...

  $(".button").click(function(){
     $("#header").animate({top: "-50"}, 1000)
     $("#something").delay(1000).animate({height: "hide"}, 1000)
     $("ul#menu").delay(2000).animate({top: "20", left: "0"}, 1000)
     $(".trigger").delay(3000).animate({height: "show", top: "110", left: "0"}, "slow");
});

(remplacez 1000 par la vitesse d'animation souhaitée. L'idée est que votre fonction de retard retarde de ce montant et accumule le retard dans l'animation de chaque élément, donc si vos animations étaient toutes les 500 millisecondes, vos valeurs de retard seraient 500, 1000, 1500)

edit: la vitesse "lente" de FYI jquery est également de 600 millisecondes. donc si vous souhaitez utiliser "slow" toujours dans vos animations, utilisez simplement ces valeurs lors de chaque appel ultérieur à la fonction de retard - 600, 1200, 1800

1
MrVimes

Je pensais à une solution de retour en arrière.

Vous pouvez peut-être définir que chaque objet ici a la même classe, par exemple .transparent

Ensuite, vous pouvez créer une fonction, disons startShowing, qui recherche le premier élément qui a le .transparent classe, l'animer, supprimer .transparent puis s'appelle.

Je ne peux pas assurer la séquence mais suit généralement l'ordre dans lequel le document a été écrit.

C'est une fonction que j'ai faite pour l'essayer

function startShowing(){
      $('.pattern-board.transparent:first').animate(
        { opacity: 1}, 
        1000,
        function(){
          $(this).removeClass('transparent');
          startShowing();
        }
      );
    }
0
Lomefin