protofunc()

jQuery´s step-Methode erweitern: synchrone Width-Animation

Tags: deutsch, javascript, jquery, tutorial

jQuery bietet viele Möglichkeiten der Erweiterung. Neben der relativ gut bekannten Möglichkeit das jQuery Prototype Objekt mit weiteren Methoden zu versorgen ($.fn ist ein alias für jQuery.prototype). Gibt es APIs für die Erweiterung von Events, Selektoren, easing, Ajax und der step-Methode. Letztere spielt -wie das easing – eine zentrale Rolle bei Animationen. Die bekannteste step-Erweiterung ist wohl das Color-Animation-Plugin, welches es erlaubt auch Farbwerte zu animieren.

Wir wollen eine sehr simple step-Erweiterung schreiben. Bei diesem Tutorial geht es weniger darum eine tolle Custom-Animation zu schreiben, sondern zu lernen, wie man schnell und einfach eine solche Erstellen könnte. Möchte man 2 oder mehrere unterschiedliche Elemente mit unterschiedlichen Werten synchron animieren, kann man in der Regel einfach diese Animationen gleichzeitig starten. Hierbei kommt in der Regel nichts 100%ig synchrones heraus, jedoch fällt dies dem User nicht auf.

In manchen Fällen benötigt man jedoch eine wirklich 100%ige Synchronisierung von Effekten. In diesen Fällen kann eine Erweiterung der step-Methode der mögliche Weg zum Ziel sein. Die step-Methode wird – wie der Name bereits aussagt – bei einer Animation ständig aufgerufen. Nachfolgendend findet ihr eine Demo.

Unsere Step-Methode werden wir syncWidth nennen, so dass sie bei einer Animierung der Eigenschaft syncWidth jedes mal aufgerufen wird:

$('div.box:first').animate({syncWidth: 400});

Damit unsere Erweiterung weiß, welche Elemente synchronisiert werden und wieviel Platz für diese Elemente zur Verfügung stehen soll, geben wir in den Optionen zwei weitere Eigenschaften an:

$('div.box:first')
	.animate({syncWidth: 400}, {syncElements: 'div.box', fullWidth: 800});

Das Grundgerüst unseres Step-Plugins sieht wie folgt aus:

$.fx.step.syncWidth = function(fx){
	console.log(fx);
};

Als 1. Parameter übergibt jQuery uns das fx-Objekt der jeweiligen Animation. Mit Firebug können wir die in diesem Objekt zur Verfügung stehenden Informationen anschauen. Der gesamte weitere Code findet innerhalb dieses Grundgerüst statt.

Am Anfang kümmern wir uns um die initialen Berechnungen, die nur einmal ganz am Anfang durchgeführt werden müssen. Damit wir auf diese Berechnungen auch bei den späteren Aufrufen zurückgreifen können, erweitern wir einfach das fx-Objekt.

//fx.state ist nur beim 1. Aufruf falsy
if (!fx.state) {
	var o = fx.options;
	//Startwerte der Animation berechnen
	fx.start = $(fx.elem).width();
	//Zugriff auf wichtige Eigenschaft verkürzen
	fx.fullWidth = o.fullWidth;

	//Array für die Startwerte der synchronisierten Elemente
	fx.syncStart = [];
	fx.syncElements = $(o.syncElements)
	// neues jQuery-Objekt mit allen synchronisieten Elementen, aber ohne das fx.elem, erstellen
		.map(function(i, elem){
			if(elem !== fx.elem){
				return elem;
			}
		})
		//Array mit Startwerten füllen
		.each(function(i){
			fx.syncStart.push($(this).width());
		});
	//Endwert für synchronisierte Elemente berechnen
	fx.syncEnd = (fx.fullWidth - fx.end) / fx.syncElements.length;
}

Nun kümmern wir uns letztlich darum die einzelnen CSS-Änderungen zu berechnen und bei den jeweiligen Elementen zu setzen.

//syncedWidth enthält die Gesamtbreite aller synchronisierten Elemente
var syncedWidth = 0;
fx.syncElements
	.each(function(i){
		//Berechnung der aktuellen Breite des synchronisierten Elements
		//fx.pos gibt die Position in der Animation zwischen 0 und 1 an
		//mit Math.round erreichen wir, daß nur Ganzzahlen herauskommen,
		//da einige Browser ein schlechtes Subpixel-Rendering haben
		var width = Math.round(fx.pos * (fx.syncEnd - fx.syncStart[i]) + fx.syncStart[i]);
		//addieren der Breite des einzelnen Elements
		syncedWidth += width;
		//Breite des synchronisierten Elements setzten
		this.style.width = width + fx.unit;
	});
//Breite des Hauptelements berechnen und setzten
fx.elem.style.width = fx.fullWidth - syncedWidth + fx.unit;

Das war´s dann schon. Hier nochmals abschließend die Demo. Mit etwas Kreativität (habe ich nicht) und noch mehr Mathematik (kann ich auch nicht) kann man noch einiges aus der step-Methode rausholen. Abschließend nochmal der gesamte Code:

$.fx.step.syncWidth = function(fx){
	if (!fx.state) {
		var o = fx.options;
		fx.start = $(fx.elem).width();
		fx.syncStart = [];
		fx.fullWidth = o.fullWidth;

		fx.syncElements = $(o.syncElements)
			.map(function(i, elem){
				if(elem !== fx.elem){
					return elem;
				}
			})
			.each(function(i){
				fx.syncStart.push($(this).width());
			});

		fx.syncEnd = (fx.fullWidth - fx.end) / fx.syncElements.length;
	}
	var syncedWidth = 0;
	fx.syncElements
		.each(function(i){
			var width = Math.round(fx.pos * (fx.syncEnd - fx.syncStart[i]) + fx.syncStart[i]);
			syncedWidth += width;
			this.style.width = width + fx.unit;
		});

	fx.elem.style.width = fx.fullWidth - syncedWidth + fx.unit;
};
Written January 25, 2009 by
protofunc