J'ai une boucle d'animation utilisée pour manipuler des lettres individuelles. Il est enveloppé dans une minuterie afin de créer un décalage retardé. Chaque lettre s'anime 100 ms plus tard que la précédente. J'ai besoin de comprendre comment je peux savoir quand l'animation complète est terminée, mais j'ai des problèmes en raison des différents types d'imbrication utilisés.

J'ai essayé quelques choses différentes, y compris en essayant de renvoyer une valeur de l'animation, puis la minuterie, puis la fonction $ .each, mais je suis sûr que cela est désactivé. Je pensais également que je pourrais être en mesure d'utiliser la promesse fournie par la fonction d'animation de jQuery, mais je ne sais pas exactement comment l'implémenter. Tout conseil ici serait apprécié:] merci

Voici mon code actuel:

var offset = 200;
//drop individual letters down out of view
    function dropLetters($letters){

        var len = $letters.length - 1;

        $letters.each(function(i){

            var $letter = $(this);

            setTimeout(function(){

                $letter.animate({ top: offset + 'px' }, 300, function(){
                    if( i >= len ){
                        return $(this).promise();
                    }
                });

            }, 100 * i );

        });

    }

Edit: Désolé, je me rends compte que j'ai omis la variable de décalage. J'ai ajouté ceci en arrière - il était juste réglé à une valeur de 200.

De plus, je me rends compte que cette question est similaire à une autre, mais elle semble également en différer. Les réponses fournies ici donnent quelques approches différentes qui ne sont pas présentes dans l'autre question.

2
sm1215 5 mars 2016 à 01:31

3 réponses

Vous pouvez faire une promesse à chaque appel setTimeout et vous n'avez pas besoin de vérifier par vous-même que toutes les opérations asynchrones sont terminées.

function dropLetters($letters){
  var promises = [];
  $letters.each(function(i){
    var $letter = $(this);
    promises.push(new Promise(function(resolve, reject) {
      setTimeout(function(){
        $letter.animate({ top: offset + 'px' }, 300, function(){
            resolve();
        });
      }, 100 * i );
    });               
  });
  return Promise.all(promises);
}

Notez que vous pourriez avoir besoin d'un polyfill Promise Voir http://caniuse.com/#feat=promises

L'un des avantages de ma réponse par rapport à ceux qui vérifient l'index est que vous devrez modifier le code à deux endroits si vous modifiez l'animation pour qu'elle soit en arrière. Voir ma version ci-dessous où les lettres volent en arrière (et en avant).

function dropLetters($letters, backwards) {
  var promises = [];
  $letters.each(function(i) {
    var $letter = $(this);
    promises.push(new Promise(function(resolve, reject) {
      setTimeout(function() {
        $letter.animate({
          top: '-100px'
        }, 300, function() {
          resolve();
        });
      }, 100 * (backwards ? $letters.length - i : i));
    }));
  });
  return Promise.all(promises);
}

dropLetters($('p')).then(function() {
  alert('finished')
});
  
dropLetters($('span'), true).then(function() {
  alert('finished')
});
p, span {
  position: relative;
  float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>

<hr style="clear: both"/>

<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
2
Juan Mendes 4 mars 2016 à 23:25

Vous pouvez utiliser $.Deferred(), .resolveWith()

function dropLetters($letters) {

  var len = $letters.length - 1;

  var dfd = $.Deferred();

  $letters.each(function(i) {

    var $letter = $(this);

    setTimeout(function() {

      $letter.animate({
        top: offset + 'px'
      }, 300, function() {
        if (i >= len) {
          dfd.resolveWith($(this));
        }
      });

    }, 100 * i);

  });

  return dfd.promise()

}
1
guest271314 4 mars 2016 à 22:38

Comme on l'a dit, vous pouvez utiliser $ .Deferred () `Juste pour illustrer, j'ai ajouté un exemple basé sur le code donné. ;-)

//drop individual letters down out of view
    function dropLetters($letters){
        var deferred = jQuery.Deferred();
        
        $letters.each(function(i,elem){
            var $letter = $(elem);

            var timer = setTimeout(function(){        

                $letter.animate({ top: $letter.offset().top-100 }, 300, function(f){
                   if(i+1>=$letters.length){// last letter was animated
                       deferred.resolve();
                   }
                });
              
            }, 300*i );

        });
        return deferred;

    }
dropLetters($('p')).then(function(){alert('finished')});
p{
  position:relative;
  float:left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p>
1
J.K.Lauren 4 mars 2016 à 23:00