template

Index :: Javascript :: Kachel-Scrolling

Bei der Präsentation großformatiger Grafiken ist es oft lästig, dass der Seitenaufbau ins Stocken gerät, weil die Image-Ladezeiten relativ hoch sind. Dieser Verzögerungseffekt tritt besonders bei flächigen Objekten wie Lageplänen, Landkarten, detaillierten Grundrissen oder komplexen Struktogrammen auf, deren Ausmaß u.U. sogar das des Bildschirms übersteigen, wodurch zudem ein Scrollen unerlässlich ist.

In solchen Fällen wird die Grafik oft in einen DIV-Container gesetzt, der eine festgelegte Größe sowie die OVERFLOW-Eigenschaft hidden oder scroll besitzt. Dadurch wird zumindest gewährleistet, dass das Layout im Rahmen bleibt, d.h. der umliegende Text bleibt sichtbar, und nur der Inhalt des Anzeigebereichs wird (per ScrollBars oder entsprechenden Bedienungselementen) gescrollt.

Um nun noch das Problem der hohen Ladezeit zu vermeiden, gibt es die Möglichkeit der Kachelung: Das Originalbild wird in kleinere Rechtecke unterteilt und auf dem Server abgelegt. Die so entstandenen Teilbilder (Kacheln oder Tiles) werden dann vom Client nach Bedarf geladen, je nachdem welcher Bildbereich gerade angezeigt werden muss.

Im DIV (fett gestrichelt) ist immer nur
ein Teil der Kacheln sichtbar, aus denen
sich das Gesamtbild zusammensetzt.


Vorteile

Das Verfahren der Kachelung hat entscheidende Vorteile:

Essentielle Fragen

Für die Realisierung eines gekachelten Bildausschnitts müssen bereits im Vorfeld diverse Fragen geklärt werden:

Prinzip des Scroll-Vorgangs

Im Folgenden wird der Scroll-Vorgang anhand einer Kachelreihe A-B-C-D-E erläutert.

Hier sehen wir eine Zeile von 4 Images, die einen zusammenhängenden Bildbereich A-B-C-D repräsentieren (der fett gestrichelte Rahmen stellt den Sichtbereich des DIV-Containers dar):
Momentan sind zwar nur 3 Kacheln sichtbar (A, B und ein Teil von C), aber gemäß obiger Überlegung (siehe: Essentielle Fragen :: Wieviele Kacheln werden clientseitig maximal angezeigt?) können hier bis zu 4 Kacheln gleichzeitig in Erscheinung treten. Deshalb wird zunächst einmal strikt diese Anzahl an Grafiken (vor)geladen.

Wenn der Benutzer nach rechts scrollt, müssen die Images nach links verschoben werden. Dadurch rückt jetzt auch das 4. Image mit Kachel D in den sichtbaren Bereich:

Durch einen weiteren Scroll-Schritt wird Image 1 (Kachel A) links aus dem Sichtbereich herausgeschoben. Es wandert nun ganz nach rechts, wo es die dort zu platzierende Kachel E aufnimmt:

Wie man erkennt, wird bei einem solchen Übergang nicht die ganze Image-Reihe zurückgesetzt (wodurch alle sichtbaren Images neue Grafiken laden müssten - ein Flackern wäre die Folge), sondern lediglich das herausfallende Image neu positioniert und - ausserhalb des Sichtbereichs - mit einer neuen Grafik versorgt.
Mithilfe dieses Rotationsverfahrens wird vermieden, dass alle Images gleichzeitig Position und Grafik ändern, was durch das asynchrone, zeitversetzte Laden ein irritierendes Ruckeln hervorrufen würde.

Am obigen Beispiel wird klar, dass ein Scrollen nach rechts überdies vorausschauend ist. Dort steht schon die Kachel E bereit, um bei einem Folgeschritt in Erscheinung zu treten.
Allerdings ist es völlig offen, ob der Benutzer in diese Richtung weiterscrollt - er könnte genausogut kehrt machen und wieder nach links zurückscrollen, d.h. die Images müssten nach rechts verschoben werden:

Hier sehen wir, dass ein Scrollen nach links nicht vorausschauend ist. Dort steht kein Image bereit, um die erwartete Kachel A in Erscheinung treten zu lassen.
Image 1 muss also wieder Kachel A laden und nach links wandern:

Theoretisch dürfte man die Image-Reihe erst dann neu positionieren, wenn Image 1 die Kachel A geladen hat - bis dahin enthält das Image ja immer noch die Grafik von Kachel E (deren - wenn auch kurzzeitiges - Erscheinen am linken Rand einen befremdlichen optischen Effekt hätte).
Da wir nicht auf das Laden der neuen Kachel warten wollen, die Methoden hierzu sind ohnehin browserspezifisch und nicht immer verlässlich, könnten wir zuerst einmal die störende Grafik E aus dem Image verschwinden lassen, bevor wir die Positionierung durchführen bzw. die neue Grafik zuweisen.

Der naheliegendste und auch schnellste Weg, nämlich die src-Eigenschaft des Images zu leeren (IMG.src='';) führt bei den meisten Browsern zur Anzeige ihres BildNichtVorhanden-Symbols. Wer damit leben kann, sollte nicht vergessen, die Image-Attribute width und height auf Kachelmaße zu setzen. Empfehlenswert ist auch eine zur Gesamtgrafik passende, neutrale Hintergrundfarbe, beispielweise ein Mittelgrau: IMG.style.background='#C0C0C0';
So sieht ein leeres Image (hier: 120 x 96 Pixel) in diesem Browser aus:
Verbreitet ist auch der Einsatz unaufdringlicher Dummy-Kacheln in Form einer unifarbenen oder hinlänglich neutralen Fläche - hier ein Exemplar von 120 x 96 Pixeln mit einem seamless Muster:
Dummy-Kacheln erfüllen noch einem anderen Zweck: bei manchen Grafiken sind möglicherweise nicht alle Bildabschnitte erfasst, d.h. es fehlen Kacheln - entweder, weil sie noch nicht hergestellt/bearbeitet wurden, oder weil sie mangels Informationsgehalt erst gar nicht vorgesehen sind. Diese Situation entsteht z.B. bei gerasterten Landkarten, deren Inhalt sich auf ein unregelmäßig geformtes Gebiet beschränkt. Anstelle der ausgelassenen Kacheln können dann Platzhalter verwendet werden, z.B.:
Bei Online-Karten mit Zoomfunktion und der Anzeige von Satellitenbildern kommt es häufig vor, dass in den Ebenen mit hohem Zoom-Faktor gewisse Bereiche - etwa ländliche Gebiete oder Meeresoberflächen - nicht abgebildet wurden. Hier besitzen die Platzhalter einen entsprechenden Hinweis wie 'Dieser Ausschnitt steht in der aktuellen Zoom-Stufe nicht zur Verfügung'.

Fazit
Natürlich kann man das vorausschauende Scrollen in allen Bewegungsrichtungen realisieren, es müssen nur zusätzliche Images mit den entsprechenden Kacheln vorgeladen unter dem DIV-Rand bereitstehen. Bei größeren Scrollschritten bedarf es evtl. sogar mehrerer Schichten solcher Images.
Meistens verzichtet man jedoch auf diese Mehrarbeit, weil die Ladezeiten der eher kleinen Kacheln (gerade wenn sie aus dem Cache geholt werden) so kurz ist, dass etwaige zur Überbrückung eingeblendete Dummy-Bilder kaum noch als störend empfunden werden.
I.d.R. wird - gerade bei Spielen - sogar auf die Zwischenanzeige von Dummy-Bildern verzichtet. Wenn die Gesamtmenge der Kacheltypen nicht allzu groß ist, wird ohnehin oft durch einen Preload dafür gesorgt, dass sich die Grafiken (evtl. hidden) entweder im Dokument oder zumindest im Cache befinden, wodurch das Auswechseln von Bildern so schnell abläuft, dass quasi keine wahrnehmbaren Flacker-Effekte auftreten.

Aufbau im HTML-Code

Im HTML-Code benötigen wir den DIV-Container mit festgelegter Größe. Seine OVERFLOW-Eigenschaft ist hidden, damit keine Kacheln über seinen Rand hinausragen; seine POSITION ist relative, damit sich die absolute-Positionierung seines Inhalts auf den DIV selbst bezieht.
Im DIV liegen ausserdem noch die absolute positionierten Images, um die Kachel-Grafiken aufzunehmen. Die Images besitzen jeweils IDs, um sie von Javascript aus ansprechen zu können. Beim Laden der Seite soll schon etwas zu sehen sein; deshalb sind entsprechende Bilder in der src-Eigenschaft angegeben:
<div style='WIDTH:200;HEIGHT:200;POSITION:relative;OVERFLOW:hidden;'>

<img id='it0' src='img/tile0.jpg' style='POSITION:absolute;LEFT:0;TOP:0;'>
<img id='it1' src='img/tile1.jpg' style='POSITION:absolute;LEFT:70;TOP:0;'>
<img id='it2' src='img/tile2.jpg' style='POSITION:absolute;LEFT:140;TOP:0;'>
<img id='it3' src='img/tile3.jpg' style='POSITION:absolute;LEFT:210;TOP:0;'>

</div>
Theoretisch ließen sich die Images auch per DOM erstellen. Ihre explizite Erzeugung ist spätestens dann erforderlich, wenn die Größe des DIV-Containers dynamisch ist (z.B. weil er sich der Fenstergröße anpasst): die Anzahl benötigter Images wäre zur Entwurfszeit nicht bekannt - sie würde erst zum Zeitpunkt der DIV-Anpassung ermittelt werden.

Beim Ändern des Bildausschnitts werden neue Kachelgrafiken in die entsprechenden Images geladen, z.B.:
IMG.src = 'img/tile27.jpg';
Um ein Scrollen zu bewirken, sind den Image-Attributen left und top neue Werte (Offsets im DIV) zuzuweisen, z.B.:
IMG.style.left = -12;
IMG.style.top = 20;

Beispiel: Weltkarte/Zeitzonen (Autoscroll)

Als Paradebeispiel für horizontales Endlos-Scrolling ist die Weltkarte zu nennen. Hier rotieren nicht nur die Images, sondern auch das Basisbild als Ganzes, vergleichbar mit einer sich drehenden Litfaßsäule.
Im vorliegenden Fall ist die Grafik in 3 Kacheln unterteilt, die von 3 Images aufgenommen werden. Alle Kacheln sind sozusagen von Anfang an untergebracht, so dass während eines Scrollings keinerlei src-Austausch erforderlich wird. Der Mechanismus beschränkt sich auf das Positionieren der Images.

 BreiteHöhe
Basisbildgröße in Pixeln576340
Kachelgröße in Pixeln192340
Kacheln im Basisbild31
Max. Angezeigte Kacheln31

Das Verschieben der Images wird mittels Timer animiert (4 Pixel / 50 ms bzw. 100 Pixel/Sek).

Demo
Durch Niederdrücken der Maus auf einer der seitlichen Flächen wird das automatische Scrolling aktiviert. Es endet, wenn man die Maus loslässt.


Quellcode
Das onmousedown-Event der seitlichen Schaltflächen (Tabellenzellen) ruft die Funktion wk_Mov() auf, wobei als Parameter die Scroll-Richtung (-1=links, 1=rechts) übergeben wird.

In wk_Mov() wird das Vorzeichen der Schrittweite wk_d angepasst und direkt die Timer-Funktion wk_Tick() aufgerufen.

In wk_Tick() wird zu wk_x, dem positiven Offset [0...575] ins Gesamtbild, die Schrittweite addiert.
Nachdem wk_x geclippt ist, ergibt sich durch Umkehrung (575 - wk_x) der effektive Offset für die Images, die sich ja in die entgegengesetzte Richtung bewegen müssen.
Dann werden die drei Images neu positioniert: die Berechnung ihrer neuen Koordinaten [-192...384] besteht in der Addition des effektiven Offsets auf ihre Grundstellung, gefolgt von einem Clipping.
Falls das Scrolling noch aktiv ist (zu erkennen an der Variablen Tim, die als Flag dient), wird schließlich der Timeout neu gesetzt, damit wk_Tick() nach 50 ms wieder aufgerufen wird.

Das document.onmouseup-Event wird dazu benutzt, den Timer wieder abzuschalten.
Javascript im Header
function $(id) { return document.getElementById(id); } var wk_x=136,wk_d,Tim; function wk_Tick() { var o=575-(wk_x=(wk_x+576+wk_d)%576); $('iwk0').style.left=((o+192)%576)-192; $('iwk1').style.left=((o+384)%576)-192; $('iwk2').style.left=o-192; if (Tim) Tim=window.setTimeout('wk_Tick()',50); return false; } function wk_Mov(d) { wk_d=d*4; Tim=true; wk_Tick(); } document.onmouseup=function(){ if (Tim) { window.clearTimeout(Tim); Tim=0; } }
Weiter unten, im BODY, die Tabelle mit den Bedienungselementen und dem Anzeigebereich:
<table align='center' bgcolor='#A0A0A0' cellspacing='1' cellpadding='0'><tr bgcolor='#D0D0D0'>
Linke Schaltfläche
<td style='PADDING:0 8;CURSOR:pointer;' onMouseDown='return wk_Mov(-1);'><img src='img/Le.gif'></td>
Zelle mit DIV-Container
<td><div style='WIDTH:340;HEIGHT:340;POSITION:relative;OVERFLOW:hidden;'> <img id='iwk0' src='img/tzone0.gif' style='POSITION:absolute;LEFT:-136;TOP:0;'> <img id='iwk1' src='img/tzone1.gif' style='POSITION:absolute;LEFT:56;TOP:0;'> <img id='iwk2' src='img/tzone2.gif' style='POSITION:absolute;LEFT:248;TOP:0;'></div></td>
Rechte Schaltfläche
<td style='PADDING:0 8;CURSOR:pointer;' onMouseDown='return wk_Mov(1);'><img src='img/Ri.gif'></td> </tr></table>
Die initiale Position der Kacheln (siehe Variable wk_x und style-Angaben der Images) wurde übrigens so gewählt, dass Europa etwa in der Mitte des Anzeigebereichs liegt.

Beispiel: Zeichnung (Dragging) mit Navigations-Thumbnail

Ein typischer Fall für großformatige Grafiken sind fotografierte bzw. eingescannte Kunstwerke wie Zeichnungen oder Gemälde, bei denen auch die Details gut erkennbar sein sollen.

In diesem Beispiel handelt es sich um den Stich Der Lehrer (nach einem Werk von Pieter Bruegel, ca. 15251569). Das Ausgangsbild lag als monochromes GIF (1890 x 1526 Pixel) vor und wurde in 7 x 7 Kacheln unterteilt.
Zur Herstellung eines Thumbnails, welches der Navigation dient, wurde das Original um den Faktor 7 herunterskaliert und in ein JPG (Qualität: 40%) gewandelt.

 BreiteHöhe
Basisbildgröße in Pixeln18901526
Kachelgröße in Pixeln270218
Kacheln im Basisbild77
Max. Angezeigte Kacheln##

Die Dateinamen der Kacheln setzen sich zusammen aus dem String 'breu', gefolgt von den beiden Ziffern der X- und Y-Koordinate, z.B.: breu21.gif.

Für den blauen Navigationsrahmen wird eine Grafik (transparentes GIF, 52 x 52 Pixel, monochrom) verwendet. Sie ist einfacher zu handhaben als ein entsprechendes DIV-Element. Ohnehin ist der Rahmen relativ klein und benötigt weder Farb- noch Größenänderung.

Als funktionale Hilfestellung kommen verschiedene Mauszeiger zu Einsatz (siehe auch Anhang :: Mauszeiger/Cursor):
- Auf dem Thumbnail: eine Lupe, um anzudeuten, dass der Ausschnitt/Rahmen direkt versetzt werden kann.
- Auf dem Navigationsrahmen: eine kleine Verschiebe-Hand, um anzudeuten, dass der Rahmen verschoben werden kann.
- Auf dem Bildausschnitt: eine große Verschiebe-Hand, um anzudeuten, dass der Ausschnitt verschoben werden kann.

Demo
Der blaue Rahmen im Thumbnail zeigt den aktuellen Ausschnitt an und ist verschiebbar.
Ein Mausklick ins Thumbnail positioniert den Rahmen auf die gewünschte Stelle.
Das Originalbild im Ausschnittsfenster ist verschiebbar.
Beim Anklicken der Erläuterungen wird der Ausschnitt entsprechend positioniert.

Der Lehrer
 Pieter Bruegel

Esel :: Der lernende Esel bezieht sich auf das lateinische Sprichwort am unteren Bildrand: PARISIOS STOLIDUM SI QUIS TRANSMITTAT ASELLUM. SI HIC EST ASINUS NON ERIT ILLIC EQUUS. (Darunter steht die niederländische, freie Übersetzung: Selbst wenn man einen Esel auf die Schule schickt, bleibt er ein Esel und kommt nicht als Pferd zurück)
Person im Fenster :: Parodistische Anspielung auf das geheimnisvolle Gesicht in diversen Gemälden Hieronimus Boschs.
Reisig-Rute :: Als schlagendes Argument für unverständige Schüler muss die Rute immer griffbereit sein.
Brotkasten :: Der aus dem Behälter ragende Teller und die Wurst an der Schnur weisen auf eine Futterkiste hin.
Kauernde Gestalten :: Parodistische Anspielung auf die grotesken Figuren in Hieronimus Boschs Gemälden.
Pillermann :: Ein Schüler, der mit natürlicher Anmut seine Blöße zur Schau stellt.
Schriftzug 'Bruegel Inventor' :: Hinweis auf Pieter Bruegel (den Älteren) als Urheber der Vorlage zu diesem Stich.
Schriftzug 'COCK EX 1557' :: Hinweis auf den niederländischen Verleger Hieronymus Jérôme Cock (ca. 1510-1570), der Bruegels Zeichnungen als Stichreproduktionen veröffentlichte.

Index :: Javascript


template