# jQuery, elegant auf Ende von mehreren Animationen (asynchron) warten



## jeipack (29. August 2013)

Hi
Eine Animation mit jQuery läuft ja asynchron:

```
$("#img1").animate({
		width: '225px',

	}, 500, function() {
		console.log("animate fertig");
});
console.log("nach animatecode");
```
"nach animatecode" wird vor "animate fertig" ausgegeben.
So weit so gut. Grundsätzlich kann man ja seine Funktion die man nach der Funktion ausführen will einfach als Parameter übergeben (Wie ich es hier auch gemacht habe: "function() { console.log("animate fertig"); }"

Wie aber führe ich eine Funktion aus die auf das Ende von mehreren unterschiedlichen Animationen warten soll?
Mein jetziger Stand ist:
Ich packe alle Animationen in ein Array, setze wenn die Animation gestartet wird sie im Array auf false und setze sie wenn sie fertig ist wieder auf true und rufe dann meine Funktion auf die zuerst das ganze Array durchgeht und den eigentlichen Inhalt nur ausführt wenn alle Einträge des Arrays true sind.
Und weil ich das mit Sprache nur schwer erklären kann hier ein fiddle:
http://fiddle.jshell.net/3sPSx/

Meine eigentliche Frage ist nun: Kann man das auch eleganter machen?


----------



## tombe (29. August 2013)

Du erstellst eine allgemein gültige Variable (z.B. done) mit dem Startwert 0. Wenn eine der Funktionen fertig ist, wird der Wert der Variable um 1 erhöht.

In der "Schlussfunktion" prüfst du nur ob diese Variable den entsprechenden Gesamtwert hat, dein Beispiel sieht dann so aus.


----------



## jeipack (29. August 2013)

Hmm auch ne gute Möglichkeit.
Ein bisschen ressourcenschonender als meine Lösung, aber grundsätzlich der gleiche Gedanke.

Hmm um es etwas dynamischer zu machen, was würdest du von dieser Idee halten:
Jedesmal wenn eine Animation gestartet wird done-- (cool wäre, wenn ich da den Prototypen von animate so erweitern könnte, dass er dies automatisch macht - weiss aber noch nicht genau wie ich da jQuery genau erweitere) und jedesmal wenn die Funktion animateDone() aufgerufen wird done++ und dann wenn  done==0 ist weiss ich dass gerade keine Animation am laufen ist.

Was ich auch noch gesehen habe, jQuery hat einen :animated Selector:
http://api.jquery.com/animated-selector/

Eventuell kann das auch ganz nützlich sein um es dynamischer zu machen.
Allerdings könnte man die Funktion nicht für andere asynchrone Dinge (zB Ajax Requests) benutzen.

Freue mich wenns noch weitere Vorschläge gibt


----------



## jeipack (31. August 2013)

Bis jetzt habe ich folgende Funktion:


```
startAnimate(groupid, obj, animateConfig, successfunction)
```
Als Parameter:
groupdid: int oder auch string um bestimmte Animationen zu groupieren
obj: ein jQuery Object auf das .animate() ausgeführt werden soll
animateConfig: Ein Object das genau so aufgebaut ist wie die Parameter die man .animate() übergeben würde
successfunction: Wird ausgeführt wenn alle Animationen der Gruppe beendet sind

```
/**
* groupid int to group the the animations
* obj jQuery Object
* animateConfig Object, the animateConfig like jQuery 
* successfunction (only the first function with a new groupid is taken)
* the objects
*/
function startAnimate(groupid, obj, animateConfig, successfunction) {
    
    //init storage
    if(typeof animateInfoStorage === 'undefined') {
        animateInfoStorage = [];
    }
    
    //init animateDone function
    if(typeof animateDone  === 'undefined') {
        console.log("new function");
        
        animateDone = function(groupid) {
            done=animateInfoStorage[groupid]['counter'];
            if(--done == 0) {
                animateInfoStorage[groupid].function(groupid);
                
            }
            animateInfoStorage[groupid]['counter']=done;
        }
        
    }
    
    
    
    if(groupid in animateInfoStorage) {
        animateInfoStorage[groupid]['counter'] +=1;
    } else {
        animateInfoStorage[groupid] = new Array();
        animateInfoStorage[groupid]['counter'] =1;
        animateInfoStorage[groupid]['function'] =successfunction;
    }
    var tempfunc =animateConfig[2];
    var newfunc= function() {
        tempfunc();
        animateDone(groupid);
    }
    animateConfig[2]=newfunc;
    
    obj.animate.apply(obj, animateConfig);
}
```


Das ganze live:

http://fiddle.jshell.net/3sPSx/7/


----------



## CPoly (31. August 2013)

Ein Wort: promises http://api.jquery.com/promise/


```
$('#div1, #div2, #div3').promise().done(function() {
    myconsole.log("all animations done");
});
```

http://fiddle.jshell.net/3sPSx/8/


----------



## jeipack (31. August 2013)

@CPoly: Kannte ich nocht gar nicht, vielen Dank dafür. 

Nichts desto trotz will ich euch doch *mein erstes jQuery Plugin* vorstellen  Auch wenn die Funktion .promise() ein sehr ähnliches Verhalten erzeugen kann, kann man mit meiner noch ein bisschen mehr. Man kann auf das selbe Objekt Animationen verschiedener Gruppen legen und kann somit auf das Ende aller Animationen einer Gruppe warten, nicht auf das Ende aller Animationen eines Objekts.

Wie man es benutzt:

```
$("div").groupedAnimate(
	1, //group
	[ //an object like the (easy) parameters for .animate
		{
			width: '275px',
		}, 700, function() {
			myconsole.log("animate 1 done");
		}
	],
	function() { //the success function for ALL animations in the group 1
        console.log("Group 1 DONE");
    }
);
```

Das Plugin ansich:

```
//-----------------------------------------
// jQuery Plugin groupedAnimate
//-----------------------------------------

(function($){
	$.fn.extend({
	    groupedAnimate:function(groupid, animateConfig, successfunction){
		return $(this).each(function(){
    
			//init storage
			if(typeof animateInfoStorage === 'undefined') {
				animateInfoStorage = [];
			}
			
			//init animateDone function
			if(typeof animateDone  === 'undefined') {
				animateDone = function(groupid) {
					if(--animateInfoStorage[groupid]['counter'] == 0) {
						animateInfoStorage[groupid].function(groupid);
                    }                   
				}
			}
			
			
			
			if(groupid in animateInfoStorage) {
				animateInfoStorage[groupid]['counter'] +=1;
			} else {
				animateInfoStorage[groupid] = new Array();
				animateInfoStorage[groupid]['counter'] =1;
				animateInfoStorage[groupid]['function'] =successfunction;
			}

			var tempfunc =animateConfig[2];
			var newfunc= function() {
				tempfunc();
				animateDone(groupid);
			}
			animateConfig[2]=newfunc;
			$(this).animate.apply($(this), animateConfig);
            //ugly workaround cause i dont know how to get a copy of the object for every each loop
            animateConfig[2]=tempfunc;
		});
    }
	});
 })(jQuery);
```

Und das ganze Live:
(Man sieht hier schön wie Gruppe 1 Animationen fertig sind, obwohl es auf dem #div1 noch eine Animation hat, mit .promise wäre das so nicht möglich)
http://jsfiddle.net/uHdzS/7/

Mann kann es auch verschachten  (siehe Gruppe 'X')
http://jsfiddle.net/uHdzS/8/


*ToDos:*

Das ganze funktioniert im Moment nur mit dem easy Aufruf von .animate(), der komplexere: .animate( properties, options ) funktioniert so noch nicht.
Im Moment ist es zwingend dass eine .done Funktion für jede Animation mitgegeben wird. Dies soll optimal werden.


----------

