Nun wird das bisherige Script aufgebohrt und mit einigen Extras versehen. Dazu gehört zuallererst die Möglichkeit, die Bilder in einer zufälligen Reihenfolge anzeigen zu lassen. Außerdem soll ein Fader auch manuell startbar sein, ohne dass er automatisch nach dem Laden der Seite anläuft. Ebenso soll er auch anhaltbar sein, damit man ihn auf Knopfdruck stoppen kann, und er soll eine Einzelschritt-Funktion (natürlich vorwärts und rückwärts) besitzen. Und selbstverständlich sollte er so robust geschrieben sein, dass eine "Fehlbedienung" ausgeschlossen ist.
Um diese gewünschten Fähigkeiten umsetzen zu können, müssen dem Fader zunächst verschiedene weitere Eigenschaften verliehen werden, auf die man dann im init-Aufruf Bezug nehmen kann, und die diese zusätzlichen Möglichkeiten steuern. Einstellungen, die im init-Aufruf nicht enthalten sind, werden durch Voreinstellungen ersetzt, die im Framework-Objekt abgespeichert werden. Hier die komplette Übersicht aller benötigten Eigenschaften dieses Komfort-Faders:
| Eigenschaft | Inhalt | Bedeutung |
|---|---|---|
viewTime |
Ganzzahl | Zeit bis zum Überblenden in ein neues Bild (in Millisekunden) |
fadeStep |
Dezimalzahl | Größe des Prozent-Schritts beim Überblenden |
autostart |
true/false |
sofortiges Starten des Faders nach dem Laden der Seite |
random |
true/false |
zufällige Reihenfolge der Bilder |
Diese Werte können in einem init-Aufruf über das zu übergebende Objekt für jeden Fader individuell definiert werden.
| Eigenschaft | Inhalt | Bedeutung |
|---|---|---|
dir |
String | Richtung für die Reihenfolge der Bilder. "backwards" führt zu einer umgekehrten Reihenfolge. |
counter |
Zahl | Zähler, der auf die Nummer des aktuellen Indexes in der Playlist zeigt. |
playList |
Array | Liste mit den (eventuell vermischten) Indizes der Bilder |
fading |
true/false |
Wird von fade geändert: Während einer Überblendung ist dieser Wert true. |
playList stehen die (eventuell vermischten) Indizes der Bilder aus dem internen Array this.images. Auf diese Weise kann sowohl eine reguläre, als auch eine zufällige Reihenfolge relativ einfach gehandhabt werden. Die Eigenschaft counter wurde sowieso schon in vorherigen Versionen des Fader-Objektes benötigt. Hier zeigt sie nun nicht mehr direkt auf die Bild-Elemente selbst, sondern auf einen Index in der Playlist, der jeweils für eines der Bild-Elemente steht.
Die Reihenfolge in der Playlist kann über die Eigenschaft dir umgekehrt abgespielt werden. Der Fader zeigt die Bilder dann "rückwärts" an. Um das zu erreichen muss in dir die Zeichenkette backwards stehen.
Während des Überblendens sollen Störungen unmöglich gemacht werden. Dafür existiert jetzt die Eigenschaft fading, die ein Weiterschalten in der Playlist verhindert.
Um eine Playlist zu erstellen, bedarf es einer entsprechenden Methode. Auch zum manuellen Starten und Anhalten des Faders werden weitere Methoden benötigt. Hier eine Übersicht aller Methoden eines Komfort-Faders:
| Methode | Aufgabe |
|---|---|
createPlayList() |
erstellt ein neues Array anhand der Menge der in images abgelegten Bildreferenzen und mischt die Nummern, wenn in random der Wert true steht |
start() |
startet den Fader, setzt stopped logischerweise auf false und ruft next auf |
stop() |
setzt stopped auf true |
next(single, backwards) |
zählt counter um eins hoch oder runter (je nach dir) - wenn Playlist zu Ende ist, wird counter zurückgesetzt und eine neue Playlist mittels createPlayList erstellt - wird für single als ersten Parameter true übermittelt (für Einzelschritt-Modus), wird stopped auf true gesetzt - wird für backwards als zweiter Parameter ein String übermittelt, so wird er in dir gespeichert - zuletzt wird fade aufgerufen |
fade() |
führt eine Überblendung zweier Bilder durch - benutzt counter, um das entsprechende Bilderpaar zu ermitteln. |
Im Wesentlichen übernehmen die Methoden fade und next die Animation. Die Methoden start und stop sind lediglich dazu da, eine Animation anzuhalten oder wieder zu starten. Dabei muss next auf die Eigenschaft stopped Rücksicht nehmen, da sonst der Fader nicht angehalten werden kann. Ebenso muss next auf fading Rücksicht nehmen, damit eine gerade ablaufende Animation nicht gestört wird. Die Methode fade wiederum soll nicht auf stopped Rücksicht nehmen, da sonst der Fader mitten in einer Überblendung angehalten würde, was nicht unbedingt einen Sinn hat.
Die Methode createPlayList ist im Grunde eine Helferfunktion, die sowohl bei der Erstellung eines Faders, als auch von der Methode next aufgerufen wird, um eine neue Playlist zu erstellen.
In diesem Kapitel wird der JavaScript-Code der Methoden genauer besprochen, bevor alles am Ende wieder zu einem neuen FaderFramework zusammengesetzt wird.
stopthis.stop = function () {
this.stopped = true;
};
Mehr ist in dieser Methode nicht notwendig.
startthis.start = function () {
this.stopped = false;
this.next();
};
Wie bereits besprochen ruft diese Funktion next auf, um einen Bilderwechsel zu provozieren. Damit ein Bilderwechsel auch manuell ausgelöst werden kann, ist der "Umweg" über start → next notwendig.
createPlayListthis.createPlayList = function () {
var i, r;
this.playList = new Array();
if (this.random) {
// zufällige Reihenfolge
while (this.playList.length < this.images.length) {
vorhanden = false;
r = Math.floor(Math.random() * (this.images.length));
for (i = 0; i < this.playList.length; i++) {
if (r == this.playList[i]) {
vorhanden = true;
}
}
if (!vorhanden) {
this.playList[this.playList.length] = r;
}
}
} else {
// geordnete Reihenfolge
for (i = 0; i < this.images.length; i++) {
this.playList[i] = i;
}
}
};
Für die Playlist wird ein leeres Array angelegt. Eine eventuell vorhandene Playlist wird dadurch gelöscht. Danach verzweigt sich die Funktion, je nach der Einstellung random entweder in eine zufällige Reihenfolge (wenn random den Wert true enthält) oder in eine geordnete Reihenfolge. Im Zweig für eine geordnete Reihenfolge wird lediglich die Variablen i auf die benötigte Anzahl der Bilder hochgezählt, im Zweig für eine zufällige Reihenfolge ist der Vorgang etwas komplexer.
Die while-Schleife wird so lange ausgeführt, bis für alle Bilder eine Nummer gefunden wurde. In der Variablen vorhanden wird angenommen, dass die neu ermittelte Zufallszahl noch nicht in der Playlist vorhanden ist, daher erhält sie den Wert false. Anschließend wird in der Variablen r eine Zufallszahl ermittelt. Diese Zufallszahl wird in der folgenden for-Schleife mit allen bereits eingetragenen Zahlen abgeglichen, ob sie nicht bereits vergeben wurde. Der Wert von vorhanden wird bei Bedarf auf true gesetzt. Dieser Wert entscheidet dann im darauffolgenden if-Zweig, ob die Zufallszahl in this.playList eingetragen werden kann.
Was Zufallszahlen angeht, so muss man wissen, dass die Funktion Math.random zufällige Werte zwischen 0.0 und 1.0 zurückgibt. Wie man diesen Zufallswert am Besten auf eine Skala von Null bis x auffächert, beschreibt der Artikel
Gleichverteilte Zufallszahlen erzeugen von Mathias Schäfer.
nextthis.next = function (single, dir) {
if (single) {
this.stopped = true;
}
if (typeof dir == "string") {
this.dir = dir;
}
if ((this.stopped && !single) || this.fading) {
return;
}
if (this.dir != "backwards") {
this.counter = (this.counter < this.playList.length -1) ? this.counter +1 : 0;
if (this.counter == 0) {
this.createPlayList();
}
} else {
this.counter = (this.counter > 0) ? this.counter -1 : this.playList.length -1;
if (this.counter == this.playList.length -1) {
this.createPlayList();
}
}
this.element.appendChild(this.images[this.playList[this.counter]]);
this.images[this.playList[this.counter]].className = "next";
this.fade();
};
Die Methode next nimmt einen Parameter namens single entgegen, der entweder als true oder als false interpretiert wird. Er dient dazu, die Animation auf "manuelle Einzelbilder" umzuschalten. Sollte kein Parameter single übergeben worden sein, dann ist das für diese Funktion dasselbe, als hätte er den Wert false gehabt.
Die Methode next nimmt noch einen zweiten Parameter namens backwards entgegen, der nur dann in this.dir abgespeichert wird, wenn es sich dabei um eine Zeichenkette handelt.
In der Methode wird allerdings nur dann eine Weiterschaltung in der Playlist vorgenommen, wenn entweder der Fader nicht mittels der Eigenschaft stopped angehalten wurde, oder wenn der Parameter single auf true gesetzt ist. Im letzteren Falle wird der Fader auf jeden Fall angehalten, da single für "Einzelschritt" steht. Und ein Einzelschritt kann nur dann ein Einzelschritt sein, wenn es danach keine weiteren automatischen Bilderwechsel gibt. Auf jeden Fall aber wird genau kein Bilderwechsel durchgeführt, solange die Eigenschaft fading noch nicht wieder auf false gesetzt wurde, um Störungen in der gerade laufenden Animation zu vermeiden.
Je nachdem, ob in der Fader-Eigenschaft dir der Wert "backwards" enthalten ist, wird counter entweder hoch- oder heruntergezählt. Je nach Bedarf wird dann counter wieder zurückgesetzt und eine neue Playlist erstellt.
fadethis.fade = function (step) {
var fader = this, imgs = this.element.getElementsByTagName("img");
this.fading = true;
step = step || 0;
imgs[1].style.opacity = step/100;
imgs[1].style.filter = "alpha(opacity=" + step + ")"; // IE?
step += this.fadeStep;
if (step <= 100) {
window.setTimeout(function () { fader.fade(step); }, 1);
} else {
this.fading = false;
window.setTimeout(function () { fader.next(); }, this.viewTime);
}
};
fade führt wie bereits in den vorhergehenden Fader-Versionen einen Animationsschritt beim Überblenden aus. Dazu nimmt die Methode einen Parameter entgegen, der den aktuellen Prozentwert im Überblendungsvorgang darstellt. Wird kein Wert übergeben, interpretiert fade das als 0.0.
Unabhängig davon, warum fade aufgerufen wurde, setzt die Methode sofort die Eigenschaft fading auf true, da fade ja nur aufgerufen worden sein konnte, um eine Animation einen Schritt weiter zu führen, eventuell sogar von Anfang an. Aus diesem Grund sollen keine Weiterschaltungen in der Playlist möglich sein, damit die gegenwärtige Animation nicht gestört wird.
Wie schon in den Vorgängerversionen verändert fade den opacity-Wert des zweiten Bild-Elements im Fader-Element für den Überblendungsvorgang, um die Deckkraft schrittweise zu erhöhen.
Um das richtige Bildelement als neues Bild ins Fader-Element einzuhängen, wird mit Hilfe des counter in der playList das anzuzeigende Bild-Element adressiert.
Wenn der Überblendungsvorgang abgeschlossen ist, also die volle Deckkraft erreicht wurde, wird im zweiten Bild der Klassenname "next" durch einen Leerstring ersetzt, um die Zugehörigkeit zu dieser Klasse aufzuheben. Das Bild muss nun nicht mehr absolut positioniert werden, da es das einzige Bild-Element werden wird. Das erste Bild wird entfernt, da es nun nicht mehr angezeigt wird. Danach setzt fade den Wert in fading wieder zurück auf false und ruft über einen Timeout die Methode next auf. Für die Dauer dieser Pause wird Wert in der Eigenschaft viewTime benutzt, der als zweiter Parameter an window.setTimeout übergeben wird.
Da einige Werte in diesem Fader-Script nicht mehr fest im Script-Code eingetragen werden, sondern über "Variablen" (genauer Eigenschaften des Objekts) festgelegt werden, kann in jedem Fader z.B. eine individuelle Betrachtungszeit für ein Bild eingestellt werden.
Um das FaderFramework so komfortabel wie möglich zu gestalten, müssen natürlich alle Parameter mit Voreinstellungen versehen werden, falls im init-Aufruf nicht alle explizit aufgeführt und mit einem Wert versehen werden. Um nun aber einen bequemen Zugriff auf die Voreinstellungen zu ermöglichen, sollten diese nicht im Konstruktor, sondern im FaderFramework selbst stehen. Der Konstruktor muss dann jeweils die Werte für die Voreinstellungen aus diesem Objekt beziehen.
Das bedeutet, dass der Anfang des Codes von sowohl dem Konstruktor, als auch dem Framework-Objekt modifiziert werden muss. Bis auf die "lebenswichtigen" Angaben zu id und images können Voreinstellungen definiert werden, zu den beiden genannten Parametern hingegen selbstverständlich nicht. Damit sieht der Code des Framework-Objektes am Anfang so aus:
var FaderFramework = {
// Einstellungen
className: "fader",
// Voreinstellungen für einen Fader
viewTime: 5000,
fadeStep: 0.5,
random: false,
autostart: true,
// automatische Einstellungen
baseURL: "",
oldWinOnLoad: false,
inits: new Array(),
faders: new Object(),
start: function () {
...
},
...
};
Die Einstellung className bezeichnet die Klasse, die das <span>-Element verliehen bekommt, welches die Slideshow für einen Fader enthält. Diese Einstellung korrespondiert mit den CSS-Regeln in der zugehörigen CSS-Datei.
Die folgenden Parameter können in einem init-Aufruf explizit für jeden Fader individuell eingestellt werden. Was sie im Einzelnen bewirken, wurde bereits
behandelt.
Die darauffolgenden Parameter richtet das Framework automatisch ein. Sie haben für den einzelnen Fader zunächst keine Bedeutung.
Fader: function (einstellungen) {
if (!einstellungen.id || !document.getElementById(einstellungen.id)
|| FaderFramework.faders[einstellungen.id]
|| einstellungen.images.length < 2) {
return false;
}
this.id = einstellungen.id;
this.images = new Array();
this.random = (typeof(einstellungen.random) != "undefined") ?
einstellungen.random :
FaderFramework.random;
this.autostart = (typeof(einstellungen.autostart) != "undefined") ?
einstellungen.autostart :
FaderFramework.autostart;
this.viewTime = einstellungen.viewTime || FaderFramework.viewTime;
this.fadeStep = einstellungen.fadeStep || FaderFramework.fadeStep;
this.stopped = false;
this.playList = new Array();
this.counter = 0;
...
}
Die einzelnen Parameter werden mit this.<Parameter> wie in einer simplen Variablendeklaration zugewiesen. Zu jedem in FaderFramework voreingestellten Parameter wird überprüft, ob im übermittelten Einstellungen-Objekt ein Wert eingetragen ist, oder ob der in FaderFramework voreingestellte Wert genommen werden soll. Dabei wird immer wieder "FaderFramework" ausnotiert, da ja das Schlüsselwort this nicht auf das Framework-Objekt, sondern auf das neu zu erstellende Fader-Objekt verweist.
Manche Einstellungen enthalten Boolesche Werte (also entweder true oder false). Wenn nun der voreingestellte Wert false ist, dann könnte man einfach auf if (einstellungen.<Parameter>) prüfen, da ja eine Abweichung von der Voreinstellung immer true sein muss, welche in die if-Verzweigung führt und für den Parameter den Wert true einträgt. Ist dagegen überhaupt keine Angabe gemacht worden, oder soll false eingetragen werden, so entspricht das der Voreinstellung. Dieses Vorgehen versagt aber in dem Moment, wo die Voreinstellung true lautet. Daher muss man solche Default-Werte anders abfragen.
Die eben vorgestellte Prüfung führt bei true als Defaultwert in die Irre! Wenn für einen Parameter explizit false eingetragen werden soll, dann ergibt if (einstellungen.<Parameter>) eben false, was aber dazu führt, dass eben nicht in die if-Verzweigung gegangen wird, sondern dass der voreingestellte Wert genommen wird, welcher entgegen der Absicht true lautet! Daher muss bei Booleschen Defaultwerten immer explizit auf das Vorhandensein der entsprechenden Eigenschaft geprüft werden, und das geht mit dem typeof-Operator. Die Prüfung muss korrekterweise lauten: if (typeof(einstellungen.<Parameter>) != "undefined")! Bei den Parametern autostart und random kann man das sehen.
Bei Voreinstellungen mit anderen Werten schreibt man: this.<Parameter> = einstellungen.<Parameter> || FaderFramework.<Parameter>;. Das mag verwirrend aussehen, ist aber eine simple Oder-Verknüpfung. Bei einer Oder-Verknüpfung ist das Ergebnis entweder der erste Vergleichswert (wenn er nicht als false interpretiert werden muss), oder eben der zweite Vergleichswert, oder eben false. Daher führt die eben genannte Schreibweise zu entweder dem übermittelten Parameter, oder (wenn er fehlt ist das wie false) der Voreinstellung.
Alle Code-Beispiele zusammengenommen ergeben dieses Script:
fader-framework.js (10 KB), welches auch für die
Demonstration am Anfang dieses Artikels benutzt wurde. Auf der folgenden Beispielseite können an einem solchen Komfort-Fader diverse Methoden ausprobiert, und diverse Parameter live verändert werden.
Anzeigebeispiel: So sieht's aus
Das folgende Beispiel zeigt, wie eine Bildergalerie in eine Slideshow verwandelt werden könnte. Je nach Dokumentstruktur ist ein anderes JavaScript nötig, um den Umbau vorzunehmen. Den Rest erledigt das Fader-Framework.
Anzeigebeispiel: So sieht's aus
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
<title>Komfort-Fader in einer Bildergalerie</title>
<script type="text/javascript" src="fader-framework/fader-framework.js"></script>
<script type="text/javascript">
// Slideshow einrichten
function slideShow () {
var galerie, i, images, img, p, test;
test = document.getElementsByTagName("*");
for (i = 0; i < test.length; i++) {
if (test[i] && test[i].className && test[i].className == "bildergalerie") {
galerie = test[i];
}
}
images = new Array();
test = galerie.getElementsByTagName("a");
for (j = 0; j < test.length; j++) {
images[images.length] = test[j].href;
}
p = document.createElement("p");
img = document.createElement("img");
img.src = images[0];
img.alt = "Bild";
img.id = "meineSlideshow";
p.appendChild(img);
galerie.parentNode.replaceChild(p, galerie);
FaderFramework.init({ id: img.id, images: images, fadeStep: 2, viewTime: 1000 });
}
</script>
<style type="text/css">
.bildergalerie { margin: 0; padding: 0; list-style: none; }
.bildergalerie li { display: inline; margin: 0; padding: 10px; }
</style>
</head>
<body>
<h1>Komfort-Fader in einer Bildergalerie</h1>
<ul class="bildergalerie">
<li><a href="images/berge1.jpg"><img src="images/tn_berge1.jpg" alt="In den Bergen" /></a></li>
<li><a href="images/berge2.jpg"><img src="images/tn_berge2.jpg" alt="In den Bergen" /></a></li>
<li><a href="images/berge3.jpg"><img src="images/tn_berge3.jpg" alt="In den Bergen" /></a></li>
<li><a href="images/berge4.jpg"><img src="images/tn_berge4.jpg" alt="In den Bergen" /></a></li>
<li><a href="images/berge5.jpg"><img src="images/tn_berge5.jpg" alt="In den Bergen" /></a></li>
<li><a href="images/berge6.jpg"><img src="images/tn_berge6.jpg" alt="In den Bergen" /></a></li>
</ul>
<p><a href="#" onclick="slideShow(); this.onclick = function () { return false; }; return false;">Slideshow</a></p>
</body>
</html>
Durch Anklicken des Links "Slideshow" wird über den onclick-Attribut notierten Funktionsaufruf die Funktion slideshow gestartet. Diese Funktion ermittelt alle HTML-Elemente des Dokuments, indem sie in getElementsByTagName als Parameter den Stern übermittelt, der wie ein Joker zu verstehen ist. Danach prüft sie in einer for-Schleife jedes ermittelte Element, ob es zur Klasse "bildergalerie" gehört. Wenn ja, wird es in der Variablen galerie abgelegt.
Anschließend werden alle <a>-Elemente ermittelt und ihre Verweisziele in der Array-Variablen images gespeichert.
Die originale Bildergalerie wird durch einen Textabsatz ersetzt, in den das erste Bild als Startbild eingefügt wird. Es erhält in diesem Beispiel die ID "meineSlideshow", über die es später im Fader referenziert wird.
Zuletzt wird FaderFramework.init aufgerufen, der ein Objekt übergeben wird, das unter anderem die ID des Startbildes, und eine Liste aller Slideshow-Bilder enthält. Den Rest übernimmt dann das Fader-Script.
Das obige Beispiel ist auf die Dokumentstruktur der Beispielseite zugeschnitten. Eventuell muss in anderen Bildergalerien anders vorgegangen werden, um eine Galerie durch eine Slideshow zu ersetzen. Außerdem wird im obigen Code nicht überprüft, ob eine Galerie auch erfolgreich gefunden wurde. An der Stelle, an der mit test die Nachfahrenelemente (Vollbild-Links) von galerie ermittelt werden sollen, könnte ja sein, dass zuvor kein Element mit passender Klasse gefunden worden war. Hier würde dann der Browser das Script mit einer Fehlermeldung beenden.
Im obigen Beispiel wird außerdem die Variablen galerie bei mehreren gefundenen Elementen einfach mit dem zuletzt gefundenen überschrieben. Hier könnte man das Script dahingehend erweitern, dass galerie als Array angelegt wird, durch welches dann mit den folgenden Aktionen hindurch iteriert werden könnte. Für das vorliegende Beispiel war dies jedoch nicht erforderlich.
© 2008
Impressum, für diese Seite:
Felix.Riesterer@gmx.net