[ Impressum ]

Laden und Anzeigen von Rastergrafiken

www.Rozek.de > Zebra > Experiments > Image
Rastergrafiken sind ein wichtiger Bestandteil grafischer Benutzeroberflächen (besonders unter Zebra [1]) - deshalb soll an dieser Stelle der Umgang damit untersucht werden.

Laden von Rastergrafiken

Rastergrafiken werden unter Zebra mithilfe der Funktion zebra.ui.loadImage geladen:

var Image = zebra.ui.loadImage(ImageURL);
var Image = zebra.ui.loadImage(ImageURL, CallbackFunction);

Allerdings erfolgt der Ladevorgang asynchron, d.h., die Funktion loadImage kehrt zwar sofort zurück, das gewünschte Rasterbild ist aber trotzdem noch nicht verfügbar.

Aus diesem Grund kann loadImage eine "Callback"-Funktion mitgegeben werden, die aufgerufen wird, sobald das zu ladende Bild tatsächlich komplett verfügbar ist.

var Image = zebra.ui.loadImage(ImageURL, function (ImageURL, successful, Image) {
if (successful) {
sayln('image "' + ImageURL + '" has been successfully loaded');
} else {
sayln('image "' + ImageURL + '" could not be loaded');
};
});

Zwischen dem Aufruf von zebra.ui.loadImage und der Ausführung der Callback-Funktion kann durchaus Zeit vergehen - bis dahin ist das von loadImage gelieferte Objekt noch nicht wirklich benutzbar.

Die folgende kleine Zebra-Anwendung verdeutlicht diesen Aspekt (JSFiddle ist hierfür leider ungeeignet):


(den zugehörigen Quelltext können Sie hier herunterladen. Klicken Sie dazu mit der rechten Maustaste auf den Link und wählen Sie aus dem Kontext-Menü die Funktion "Speichern unter" - anderenfalls würde Ihr Web-Browser die Datei direkt auswerten)

Zu Beginn meldet das Image-Objekt noch eine Größe von 0x0 Pixeln - seine wirkliche Größe kennt es erst nach einem erfolgreichem Ladevorgang.

Interessant ist auch Folgendes: weisen Sie Ihren Browser an, die vorliegende Notiz nochmals zu laden - und werfen Sie einen Blick auf die Ausgabe, die obige Anwendung jetzt erzeugt: es kann gut sein, dass loadImage beim zweiten Mal auf Anhieb ein fertig geladenes Image zurückliefert und die Callback-Funktion wesentlich früher als zuvor aufruft.

Der Grund dafür liegt im Zwischenspeicher (Cache) Ihres Browsers: sofern ausreichend Speicher zur Verfügung steht (und die betreffende Ressource dies zulässt), versucht ein Browser, einmal geladene Web-Seiten und deren Inhalte zwischenzuspeichern, um die Antwortzeiten bei erneuten Anforderungen derselben Ressource zu optimieren und den erforderlichen Netzverkehr zu minimieren.

Falls Sie jetzt den Cache Ihres Browsers leeren und die vorliegende Notiz ein drittes Mal laden, sollten Sie ungefähr wieder die ursprünglichen Ausgaben erhalten.

Wie wir gleich sehen werden, hat die Asynchronität des Ladevorganges Auswirkungen auf die Anzeige von Rastergrafiken (und damit auf das komplette Programm)

Anzeige von Rastergrafiken: ImagePan

Im einfachsten Fall kann eine Rastergrafik mithilfe eines ImagePan angezeigt werden ("Pan" steht in dem Namen der Klasse vermutlich als Abkürzung für "Pane" oder "Panel"):

ZebraCanvas.root.properties({
layout: new CenteredLayout(),
kids: [
new ImagePan('favicon.png')
]
});

Sie können dem ImagePan-Konstruktor entweder die URL einer Rastergrafik oder gleich ein Image-Objekt mitgeben - ImagePan sorgt selbst dafür, dass das Bild geladen und anschließend angezeigt wird.

Die "preferredSize" eines ImagePan ist die "natürliche" Größe der zugehörigen Rastergrafik - sobald diese vollständig geladen ist: zuvor beansprucht ImagePan stattdessen eine Fläche von 10x10 Pixeln.

Durch explizite Zuweisung einer anderen Größe kann das anzuzeigende Bild jedoch auch skaliert und verzerrt werden:

ZebraCanvas.root.properties({
layout: new CenteredLayout(),
kids: [
new ImagePan('favicon.png').properties({
preferredSize: [16,32] // <= just as an example!
})
]
});

Die folgende kleine Zebra-Anwendung illustriert dies:
(den zugehörigen Quelltext können Sie hier herunterladen. Klicken Sie dazu mit der rechten Maustaste auf den Link und wählen Sie aus dem Kontext-Menü die Funktion "Speichern unter" - anderenfalls würde Ihr Web-Browser die Datei direkt auswerten)

Anzeige von Rastergrafiken: Picture

Mit ImagePan kann man Bilder nur in Gänze darstellen - möchte man lediglich einen Ausschnitt aus einer Rastergrafik zeigen, bietet sich stattdessen die Klasse zebra.ui.Picture an:

ZebraCanvas.root.properties({
layout: new CenteredLayout(),
kids: [
new Panel().properties({
background: new Picture(zebra.ui.loadImage(
'iWebKit/images/navleftblack.png'
), 0,0, 13,30)// displays 13x30 pixels of "navleftblack" starting at 0,0
})
]
});

Das Problem mit Picture: es kann nicht direkt verwendet, sondern muss z.B. einem zebra.ui.Panel als Background (oder einem zebra.ui.ImagePan als View) zugewiesen werden. Dieses Panel kann aber nicht wissen, wie groß das dem Konstruktor mitgegebene Image werden soll. Kennt man die Ausmaße des Image bereits im Voraus, kann man diese an das Panel weiterreichen:

ZebraCanvas.root.properties({
layout: new CenteredLayout(),
kids: [
new Panel().properties({
preferredSize: [13,30], // same size as below
background: new Picture(zebra.ui.loadImage(
'iWebKit/images/navleftblack.png'
), 0,0, 13,30)// displays 13x30 pixels of "navleftblack" starting at 0,0
})
]
});

Auf diese Weise (nämlich durch explizite Angabe der gewünschten Zielgröße) kann der Image-Ausschnitt auch skaliert oder verzerrt werden.

Anderenfalls muss man loadImage mit einer Callback-Funktion versehen, die nach erfolgtem Laden des Image dessen tatsächliche Größe an das Panel durchreicht.

Diese Callback-Funktion löst auch ein weiteres Problem: wird das Panel nämlich gezeichnet, bevor das Image geladen wurde, sieht man von dem Bild noch nichts. Und umgekehrt merkt das Panel nicht von sich aus, wann das Bild tatsächlich komplett verfügbar ist.

Auch hier hilft die Callback-Funktion von loadImage, indem sie ggfs. einen neuen Layout-Vorgang anstößt und/oder das erneute Zeichnen des Panel veranlasst:

var ImageView;
ZebraCanvas.root.properties({
layout: new CenteredLayout(),
kids: [
ImageView = new Panel().properties({
background: new Picture(zebra.ui.loadImage(
'http://snippetspace.com/iwebkit/demo/images/navright.png',
function (ImageURL, successful, actualImage) {
if (successful) {
ImageView.setPreferredSize(actualImage.width,actualImage.height);
ImageView.toPreferredSize();
ImageView.repaint();
};
}
))
})
]
});

Der zusätzliche Aufruf von toPreferredSize sorgt dafür, dass das Panel nicht in seiner ursprünglichen Größe verharrt, sondern tatsächlich die Zielgröße des Bildes annimmt - erst dann kann auch repaint zuverlässig funktionieren!

Anzeige von Rastergrafiken: Canvas.drawImage

Die volle Flexibilität erhält man, wenn man das Zeichnen eines Image-Objektes selbst übernimmt:

ZebraCanvas.root.properties({
layout: new CenteredLayout(),
kids: [
new zebra.ui.Panel(
'http://snippetspace.com/iwebkit/demo/images/navright.png',
[ // customize panel with own constructor and paint methods
function (ImageURL) {
this.$super();

var self = this;
this.ViewImage = zebra.ui.loadImage(
ImageURL,
function (ImageURL, successful, actualImage) {
if (successful) {
self.setPreferredSize(actualImage.width,actualImage.height);
self.toPreferredSize();
self.repaint();
};
}
)
},

function paint (Gfx) {
Gfx.drawImage(this.ViewImage, 0,0, this.width,this.height);
}
]
)
]
});

Kombiniert man das Zeichnen von Bild-Ausschnitten mit der gezielten Verzerrung der Darstellung, lässt sich das "Slicing" implementieren, welches häufig bei der Erstellung von Benutzeroberflächen zum Einsatz kommt:

ZebraCanvas.root.properties({
layout: new CenteredLayout(),
kids: [
new zebra.ui.Panel(
'http://snippetspace.com/iwebkit/demo/images/navleftblack.png', 13,4,
[ // customize panel with own constructor and paint methods
function (ImageURL, leftWidth, rightWidth) {
this.$super();

this.leftWidth = leftWidth;
this.rightWidth = rightWidth;


var self = this;
this.ViewImage = zebra.ui.loadImage(
ImageURL,
function (ImageURL, successful, actualImage) {
self.repaint();
}
)
},


function paint (Gfx) {
// this.$super(Gfx); // there is no super-method

if (!this.ViewImage || !this.ViewImage.width || !this.ViewImage.height) {
return; // ViewImage is missing or has not yet been loaded
};

Gfx.drawImage(
this.ViewImage,
0,0, this.leftWidth,this.ViewImage.height,
0,0, this.leftWidth,this.height
);

var innerWidth = this.ViewImage.width-this.leftWidth-this.rightWidth;
var drawWidth = this.width-this.leftWidth-this.rightWidth;
Gfx.drawImage(
this.ViewImage,
this.leftWidth,0, innerWidth,this.ViewImage.height,
this.leftWidth,0, drawWidth,this.height
);

Gfx.drawImage(
this.ViewImage,
this.ViewImage.width-this.rightWidth,0, this.rightWidth,this.ViewImage.height,
this.width-this.rightWidth,0, this.rightWidth,this.height
);
}
]
).properties({
preferredSize: [40,30]
})
]
});

Jeweils leftWidth bzw. rightWidth Pixel der verwendeten Rastergrafik werden ohne horizontale Skalierung gezeichnet - der Bereich dazwischen wird so gedehnt, dass er den verbleibenden Platz bis zur aktuellen Breite des Panel ausfüllt. Diese Aufgabe übernehmen die drei drawImage-Aufrufe innerhalb der paint-Methode.

Die folgende kleine Anwendung verdeutlicht dies: die beiden linken Anzeigen stellen die Grafiken in ihrer Originalgröße dar, rechts davon werden sie mit gestrecktem Mittelteil gezeigt:
(den zugehörigen Quelltext können Sie hier herunterladen. Klicken Sie dazu mit der rechten Maustaste auf den Link und wählen Sie aus dem Kontext-Menü die Funktion "Speichern unter" - anderenfalls würde Ihr Web-Browser die Datei direkt auswerten)

Die gezeigten Grafiken stammen übrigens aus iWebKit [2], einem kleinen Framework für HTML-Anwendungen im iOS-Stil.

Viel Spaß damit!

Literaturhinweise

[1]
Andrei Vishneuski
HTML5 Rich UI JavaScript Library
Zebra ist eine noch relativ neue JavaScript-Bibliothek für grafische Benutzeroberflächen in Web-Anwendungen. Diese Seite ist der primäre Anlaufpunkt, wenn Sie sich für Zebra interessieren.
[2]
Christopher Plieger
SnippetSpace | iWebKit: The free iPhone webapp and website framework
iWebKit ist ein kleines Framework für Web-Anwendungen im iOS-Stil. Für kostenlose Anwendungen ist iWebKit ebenfalls kostenlos, anderenfalls muss eine kommerzielle Lizenz erstanden werden.