Interaktive SVG mit AngularJS – Teil 1

Bei der Entwicklung von mobilen Web-Anwendungen mit Responsive Design stellen flexible Bilder (flexible images) ein besonderes Problem dar.
SVG bieten eine gangbare Lösung: Sie sind deutlich kompakter als Bitmaps vergleichbarer Größe, und können über ihre DOM-API gesteuert werden.
AngularJS ist wiederum gut dafür geeignet, die Logik komplexer UI-Elemente in individuellen HTML-Direktiven zu kapseln.
Die Kombination der beiden erscheint attraktiv, allerdings gilt es dabei diverse Stolpersteine zu vermeiden.

Teil 1 dieses Artikels untersucht verschiedene Methoden, SVG als flexible Bilder in browser-übergreifender Weise zu verwenden.

Teil 2 beschreibt die Verwendung von AngularJS zur Steuerung von SVG-Bildern, um individuelle Kontroll- und Status-Elemente zu realisieren.

SVG in mobilen Web-Anwendungen

Bei der Entwicklung mobiler Web-Anwendungen ist Responsive Design eine attraktive Methode, um der Vielzahl möglicher Bildschirmgrößen, Auflösungen und Seitenverhältnisse Herr zu werden. Die verschiedenen Techniken leisten gute Arbeit darin, Inhalt und Layout einer Anwendung an jedes beliebige Gerät anzupassen.

Allerdings stellen Bilder im mobilen Kontext immer noch ein Problem dar, besonders bei der etwas unglücklichen Kombination eines hochauflösenden „Retina“-Displays mit einer langsamen Netzverbindung. Web-Anwendungen verwenden gewöhnlich Bitmap-Bilder (alias Raster-Bilder), deren Kodierung eine Menge Speicherplatz erfordert. Typischerweise übertrifft die Gesamtgröße und Übertragungszeit der Bilder bei weitem diejenige ihres zugehörigen HTML-Dokuments. Wenn die Web-Anwendung standardmäßig große Bitmaps verwendet, kann das eine inakzeptable Verzögerung beim Laden der Anwendung bewirken. Andererseits verschlechtert sich beim Hochskalieren kleinerer Bilder zwangsläufig die allgemeine Darstellungsqualität.

Flexible Bilder

Techniken für Flexible Bilder versuchen die passende Balance zu finden, immer genau die richtige Art von Bild für einen gegebenen Gerätekontext zu liefern. Dazu muss der Grafik-Designer üblicherweise mehrere Varianten eines Bildes vorbereiten, die jeweils auf eine spezifische Bildgröße und Auflösung angepasst sind. Der Anwendungsentwickler muss dann einen Mechanismus implementieren, um den Gerätekontext der Anwendung zu ermitteln und eine entsprechend geeignete Bild-Variante zu laden. Je nach eingesetzter Technik für flexible Bilder beinhaltet dies z.B. mehrere Media-Queries und umfangreiches Skripting im Browser, sowie zusätzliche Maßnahmen zur Datenbeschaffung der gewählten Variante und hochqualitative Bild-Skalierung auf dem Server.

Nichts davon ist besonders einfach, aber es wird noch schlimmer wenn es um die interaktiven Kontroll- und Status-Elemente einer Web-Anwendung geht: Jede Phase eines hover/press/release- oder ok/warning/alert-Zyklus erfordert eine zusätzliche Bitmap, die auf diese Weise vorbereitet und behandelt werden muss! Der nötige Aufwand erhöht zwangsläufig die Komplexität der Web-Anwendung und verschlechtert damit ihre Wartbarkeit. Gibt es denn dafür keine andere Lösung?

Scalable Vector Graphics

Hier kommen Scalable Vector Graphics ins Spiel. Anstatt jedes einzelne Pixel eines Bildes zu kodieren, enthalten sie vektorbasierte Grafikanweisungen, die der Bild-Renderer lokal ausführt. SVG wurden speziell für hochqualitative Skalierung entwickelt, so dass sie sich sehr einfach an die Anforderungen eines vorliegenden Geräte-Displays anpassen können. Obwohl sie auf XML basieren, können sie selbst komplexe Formen und Farbverläufe in bemerkenswert wenig Speicherplatz unterbringen – in der Regel um Größenordnungen geringer als bei Bitmaps mit vergleichbarer Bildfläche. Ferner können SVG mittels CSS stilistisch modifiziert und über die bekannte DOM-API gesteuert werden, genau wie HTML-Dokumente. Dadurch kann ein einzelnes SVG prinzipiell alle Phasen eines komplexen Kontroll- und Status-Elements beinhalten, wodurch sich die sonst erforderliche Notwendigkeit mehrerer Bild-Ressourcen vermeiden lässt. Na dann, wo ist der Haken?

Um es klarzustellen, die SVG-Spezifikation existiert bereits seit über zehn Jahren, und alle heutigen Web-Browser können SVG-Bilder darstellen – die meisten sogar schon seit drei Vorgänger-Versionen oder mehr. Wenn man aber tatsächlich einmal ein SVG-Bild von ihnen skaliert haben möchte, können sie sich erstaunlich merkwürdig verhalten.

Theorie

Es gibt mehrere Methoden zur Einbindung von SVG-Bildern in HTML-Dokumente. Dieser Artikel behandelt drei davon, die sich besonders gut für moderne Web-Anwendungen eignen:

  • <img>
  • <object>
  • beliebiges Element mit CSS background-image

Andere Methoden werden hier aus verschiedenen Gründen nicht berücksichtigt:

  • embed : ähnlich wie <object>, aber nicht standard-konform
  • <iframe> : ähnlich wie <object> bei geeigneter Konfiguration
  • <svg> : keine Wiederverwendung, kein separates Caching

Immer wenn die HTML-Engine eine solche Einbindung erkennt, handelt sie mit der SVG-Engine einen Viewport aus – ein rechteckiger Bereich der Seite, in dem das Bild angezeigt werden soll. Innerhalb des SVG-Dokuments spezifiziert das <svg>-Wurzelelement die dafür notwenigen Attribute:

width/height: Die bevorzugte Größe des Viewport, üblicherweise in px angegeben. Bei fehlender Angabe wird der Standardwert "100%" verwendet, d.h. der Viewport vergrößert sich so weit wie es der HTML-Renderer erlaubt (siehe unten).

viewBox: Definiert den sichtbaren Bereich des SVG-Bildes innerhalb seines internen Koordinatensystems. Bei Angabe von viewBox bildet der SVG-Renderer diesen Bereich auf die Fläche des ausgehandelten Viewports ab, wodurch das Bild je nach Bedarf skaliert wird. Bei fehlender Angabe zeichnet der SVG-Renderer das Bild so wie es ist.

preserveAspectRatio: Hat nur dann Auswirkungen, wenn viewBox ebenfalls angegeben ist. Beim Wert "none" streckt sich das Bild, bis es den Viewport vollständig ausfüllt. Bei anderen Werten beachtet die Skalierung die Seitenverhältnisse, die durch viewBox vorgegeben sind. Dabei kann das Bild nur so hoch bzw. breit werden wie die längere Site des Viewports, die restliche Fläche bleibt ungenutzt. preserveAspectRatio spezifiziert in diesem Fall auch die Platzierung des skalierten Bildes im Viewport: "xMinYMin" in der oberen linken Ecke, "xMaxYMax" in der unteren rechten Ecke, und so weiter. Bei fehlender Angabe wird der Standardwert "xMidYMid" verwendet, der das Bild in der Mitte platziert.

Obwohl das SVG-Dokument einen bevorzugten Viewport angeben darf, entscheidet letztendlich die HTML-Engine über dessen Größe. Wenn das Verknüpfungs-Element andere Werte für width/height angibt, sei es durch Attribute oder CSS-Regeln, dann haben diese Vorrang gegenüber den entsprechenden SVG-Werten. Wenn die Verknüpfung selbst keine solchen Beschränkungen aufweist, dafür aber das umgebende Eltern-Element, dann werden stattdessen diese verwendet, und so weiter entsprechend der Darstellungsregeln für HTML.

Zur Verwendung eines SVG-Bilds innerhalb eines flexibles Layouts kann der Entwickler all diese Parameter so einstellen, dass eines von drei möglichen Verhalten erzielt wird:

  • Skalierung zur Anpassung an den Viewport, wobei die Seitenverhältnisse beibehalten werden; wahlweise zentriert oder am Rand ausgerichtet
  • Streckung um den gesamten Viewport auszufüllen; typischerweise für Farbverläufe im Hintergrund verwendet
  • Feste Größe; gewöhnlich nur in statischen Container-Elementen verwendet

Als letzte Verfeinerung kann der Entwickler das Erscheinungsbild des SVG mittels CSS beeinflussen. Wie HTML unterstützt auch SVG sowohl interne wie externe Stylesheets. Ein interner Stylesheet wird im üblichen <style>-Element angegeben, typischerweise in einem <defs>-Abschnitt hinter dem <svg>-Wurzelelement. Ein externer Stylesheet wird durch die XML Processing Instruction <?xml-stylesheet?> referenziert, normalerweise direkt nach der initialen <?xml?>-Deklaration. Hierbei ist zu beachten, dass sich ein SVG-Stylesheet nicht auf das HTML-Dokument auswirkt, und auch nicht umgekehrt. Allerdings ist es durchaus möglich, denselben externen Stylesheet sowohl im HTML- als auch SVG-Dokument einzubinden, damit alle Stil-Informationen an einem zentralen Ort vorliegen.

Praxis

Leider ist die Realität selten so schön wie die Theorie behauptet. In der Praxis liefern nur bestimmte Kombinationen von Verknüpfungsmethode, Element-Styling und <svg>-Attributen das gewünschte flexible Darstellungsverhalten. Die Ergebnisse anderer Kombinationen dagegen reichen vom kleineren Ärgernis bis zur absoluten Katastrophe.

Folgende Rezepte sind mit der aktuellen Browser-Generation getestet: Firefox 26, Chrome 32, Safari 6 und Internet Explorer 11 auf Desktop-Systemen, sowie iOS Safari 6 und Android Chrome 32 auf Mobilgeräten. Bei früheren Versionen können die Ergebnisse abweichen.

Hinweis: Wenn das Rezept angibt, width/height bei <svg> wegzulassen, können diese Attribute alternativ auch explizit auf "100%" gesetzt werden. Ebenso kann anstelle eines weggelassenen preserveAspectRatio explizit "xMidYMid" angegeben werden, oder was auch immer am Besten zur entsprechenden Layout-Situation passt.

<img>

Die Einbindung eines SVG via <img> ist wahrscheinlich die verlässlichste Methode, da es hierbei genauso behandelt wird wie jedes andere Bildformat. Es besteht sogar eine gewisse Chance, dass auch ältere Browser dies durch Plug-Ins unterstützen.

Skalierung:

  • Flexible width/height für das <img>-Element vorgeben
  • width/height beim <svg>-Element weglassen
  • viewBox angeben um Skalierung zu erlauben
  • preserveAspectRatio weglassen

Streckung:

  • Flexible width/height für das <img>-Element vorgeben
  • width/height beim <svg>-Element weglassen
  • viewBox angeben um Skalierung zu erlauben
  • preserveAspectRatio auf "none" setzen

Einige Browser akzeptieren eventuell eine alternative Methode, wobei width/height zusammen mit preserveAspectRatio="none" beim <svg>-Element angegeben werden. Allerdings ist obiges Rezept näher am Standard und funktioniert daher höchstwahrscheinlich auch in Zukunft noch.

Feste Größe:

  • width/height beim <img>-Element weglassen
  • width/height in px für das <svg>-Element angeben
  • viewBox weglassen, wird hier nicht gebraucht
  • preserveAspectRatio weglassen

Da das <img>-Element keine Größe für den Viewport vorschreibt, stellt der Browser es einfach entsprechend der bevorzugten Größe dar, wie sie die Attribute des <svg>-Elements angeben. Eine viewBox und preserveAspectRatio sollten eigentlich keine Auswirkungen haben, da sie lediglich eine Abbildung 1:1 durchführen würden. Allerdings kann ihr Vorhandensein einige SVG-Renderer verwirren. Ähnlich verhält es sich, wenn <img> die gleichen Werte für width/height wie <svg> angibt. Einige Browser wenden in diesem Fall eine fehlerhafte Skalierung an, daher ist es sicherer diese Angaben wegzulassen.

<object>

Diese Methode verwendet das <object>-Element, welches schon in HTML4 eingeführt wurde, und sollte daher fast so verlässlich sein wie die <img>-Methode. Das Attribut type="image/svg+xml" sollte der Standard-Konformität halber immer angegeben werden. Falls es notwendig sein sollte, kann als Ausweichlösung statt <object> auch ein geeignet konfiguriertes <iframe> oder sogar das nicht standardisierte ⟨embed⟩-Element verwendet werden.

Skalierung:

  • Flexible width/height für das <object>-Element vorgeben
  • width/height beim <svg>-Element weglassen
  • viewBox angeben um Skalierung zu erlauben
  • preserveAspectRatio weglassen

Streckung:

  • Flexible width/height für das <object>-Element vorgeben
  • width/height beim <svg>-Element weglassen
  • viewBox angeben um Skalierung zu erlauben
  • preserveAspectRatio auf "none" setzen

Feste Größe:

  • Feste width/height in px für das <object>-Element vorgeben
  • Die gleichen width/height in px beim <svg>-Element angeben
  • viewBox weglassen, wird hier nicht gebraucht
  • preserveAspectRatio weglassen

Die meisten Browser funktionieren auch ohne width/height-Attribute für das <object>-Element, aber es ist empfehlenswert sie sicherheitshalber trotzdem anzugeben.

background-image

Bei dieser Methode wird eine individuelle CSS-Klasse definiert, die das SVG als background-image-Attribut angibt. Ferner muss background-repeat auf "no-repeat" gesetzt sein, damit nur ein einzelnes Bild angezeigt wird. Diese Klasse wird dann einem steuernden <div> zugewiesen, wobei width/height wie üblich den Viewport vorgeben. Alternativ kann die Klasse auch mit einem <span>-Element verwendet werden, oder was auch immer am Besten zur Layout-Situation passt.

Das flexibles Verhalten beruht bei dieser Methode auf der CSS3-Eigenschaft background-size. Sie verfügt über eine klar definierte Semantik, so dass diese Methode weniger problematisch sein sollte als andere Varianten der Einbindung. Allerdings funktioniert sie wahrscheinlich in älteren Browsern nicht.

Skalierung:

  • Flexible width/height für das steuernde Element vorgeben
  • CSS background-size auf "contain" setzen
  • CSS background-position auf "center" setzen
  • width/height beim <svg>-Element weglassen
  • viewBox angeben um Skalierung zu erlauben
  • preserveAspectRatio weglassen

Zur Ausrichtung des Bildes im Viewport können zwei Werte als background-position angegeben werden, z.B. "top left", "bottom right", und ähnliches mehr. Es darf hierbei keinesfalls eine andere preserveAspectRatio als der Standardwert "xMidYMid" verwendet werden, sonst verwirrt dies bekannterweise mehrere Browser.

Streckung:

  • Flexible width/height für das steuernde Element vorgeben
  • CSS background-size auf "100% 100%" setzen
  • width/height beim <svg>-Element weglassen
  • viewBox angeben um Skalierung zu erlauben
  • preserveAspectRatio auf "none" setzen

Feste Größe:

  • Feste width/height in px für das steuernde Element vorgeben
  • width/height beim <svg>-Element weglassen
  • viewBox weglassen um Skalierung zu verhindern
  • preserveAspectRatio weglassen

Obwohl all diese Rezepte flexible Bilder fabrizieren können, garantiert lediglich die <object>-Methode einen Zugriff auf das SVG-DOM, was in Teil 2 dieses Artikels benötigt wird. Einige Browser ermöglichen den DOM-Zugriff eventuell auch für <img>, aber die meisten verbergen hierbei das Innenleben von dem, was ja eigentlich nur ein gewöhnliches Bild sein sollte.

Ferner ist <object> die einzige Methode, die zuverlässig externe Stylesheets in SVG unterstützt. Bei <img> oder background-image lösen die meisten Browser solche Abhängigkeiten scheinbar nicht auf, wobei IE 11 eine bemerkenswerte Ausnahme darstellt. Stilistisch modifizierte SVG-Elemente greifen in dem Fall auf ihre Standardwerte zurück, und erscheinen zum Beispiel mit einfacher schwarzer Füllfarbe. Interne SVG-Stylesheets funktionieren aber in allen Fällen wie erwartet.

Ballast

Beim Exportieren oder Konvertieren von SVG-Bildern ist es unter Umständen schwer, exakt die Kombination von Attributen zu erhalten, wie sie die obigen Rezepte erfordern. Allerdings basieren SVG ja auf XML, so dass sie mittels eines gewöhnlichen Text-Editors leicht repariert werden können. Nur immer schön das Ergebnis der Änderung kontrollieren!

Das ist übrigens auch eine gute Gelegenheit, einigen unnützen Ballast zu entfernen, den Export- und Konvertierungs-Werkzeuge notorisch gerne in ihren erzeugten Inhalten hinterlassen. Entsprechend könnte ein SVG-Dokument folgendermaßen beginnen:

<?xml ... ?>
<!-- Generator: ... -->
<!DOCTYPE svg ... >
<svg version="1.1"
  id="Layer_1"
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  x="0px" y="0px"
  width="156px" height="117.064px"
  viewBox="0 0 156 117.064"
  preserveAspectRatio="xMidYMid"
  enable-background="new 0 0 156 117.064"
  xml:space="preserve" >

Die markierten Teile sind größtenteils harmlos, außer das sie die Dateigröße ein wenig aufblähen. Allerdings können einige davon in ungünstigen Fällen auch schon mal einen SVG-Renderer verwirren. Daher ist es allgemein besser diese Teile zu entfernen – bis auf die seltenen Fälle wo ihre Anwesenheit tatsächlich einem beabsichtigten Zweck dient.

  • id : Nur benötigt um das SVG als Ganzes zu steuern, allerdings funktioniert rootElement für den DOM-Zugriff genauso gut.
  • xmlns:xlink u.ä. : Nur benötigt wenn das SVG tatsächlich Hyperlinks, eingebettete Bitmaps oder etwas anderes enthält was diesen XML-Namespace benutzt.
  • x/y : Diese Attribute dienen zur Platzierung von intern geschachtelten SVG, und ergeben daher beim Wurzelelement überhaupt keinen Sinn.
  • enable-background : Benötigt für einige sehr spezielle Filter-Effekte, die aber in gegenwärtigen Browsern nicht besonders gut unterstützt werden. Normalerweise verschwendet diese Option nur zusätzlich Arbeitsspeicher.
  • xml:space="preserve" : Nur erforderlich wenn das SVG eingebettete HTML-Texte enthält, und selbst dann höchstens in ganz besonderen Ausnahmefällen.

Fazit

Auch wenn SVG-Unterstützung in aktuellen Browsern immer noch ein wenig hakelig sein kann, bietet sie dennoch eine praktikable Alternative zu Bitmaps wenn es darum geht, flexible Bilder für ein Responsive Design zu realisieren. SVG eignen sich besonders gut für stilisierte Inhalte wie Logos, Hintergründe und Icons, wobei ihre deutlich geringere Speichergröße dabei hilft die Ladezeiten einer Anwendung zu verbessern. Schließlich sind sie durch die Möglichkeit von CSS-Anpassung und DOM-Zugriff ideale Kandidaten für interaktive Kontroll- und Status-Elemente. Teil 2 des Artikels nimmt diesen Aspekt näher unter die Lupe.

Ein Gedanke zu “Interaktive SVG mit AngularJS – Teil 1

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s