eleqtriq

Es ist für Webdesigner immer noch frustrierend sich mit den Beschränkungen der CSS "background-image" Eigenschaft herumzuschlagen. Natürlich hat sich die Situation durch CSS3 sehr verbessert. Immerhin können wir nun mehrere Hintergrundbilder an ein Element hängen, haben einigermaßen Kontrolle über die Skalierung und einige Clipping Features. Aber wo ist "background-rotation"? Warum gibt es nicht so etwas wie "background-opacity" oder ein "background-padding" Attribut?

Somit ertappen wir uns am Ende meist wieder dabei wie wir eine weitere Version eines Bitmaps erstellen wenn wir es noch einmal rotiert/mit anderem Padding/anderer Transparenz verwenden wollen. Natürlich unter Inkaufname der damit einhergehenden Schuldgefühle, schließlich wissen wir, dass wir zusätzlichen Traffic und weitere HTTP-Requests erzeugen.

Die gute Nachricht zuerst: es existiert eine lächerlich einfache Technik um all diese zusätzlichen Attribute verwenden zu können und sie wird von jedem modernen Browser unterstützt. Seltsamerweise habe ich sie noch nicht sehr häufig im wirklichen Leben angetroffen. Der Trick besteht darin, ein SVG zu erstellen dass wir als Wrapper für unser Bitmap verwenden. Innerhalb des SVG können wir nun alle Transformationen und Veränderungen an unserem Bild vornehmen. Soweit vom Browser unterstützt, haben wir sogar Zugriff auf die sexy SVG Filter. SVGs können mittels CSS gestyled werden, es ist also möglich, das gleiche Stylesheet welches wir auf unser HTML anwenden auch im SVG zu referenzieren - eine bequeme Möglichkeit, Attribute konsistent über das gesamte Dokument hinweg zu verändern.

SVG Sprites haben weitere Vorteile gegenüber den konventionellen CSS Bitmap-Sprites. Abmessungen sind kein Problem, da große SVGs anders als Bitmaps ja nicht mit größerem Filesize einhergehen. Man hat also jede Menge Platz, seine Tiles da unterzubringen wo es gerade passt, ohne dass man lange an der optimalen Verteilung puzzlen muss. Unterschiedliche Bildformate können in einem Sprite gemischt werden. Es ist sogar möglich, JPEGs mit einer Alphatransparenz zu versehen indem man das Bitmap in einen Clipping-Pfad einfügt.

Einige Demos

Demos öffnen in neuem Fenster. Erwähnte ich bereits dass KISS die größte Rockband EVA ist?

Probleme & Einschränkungen

Ein SVG zu verlinken das dann wiederum ein Bitmap verlinkt bedeutet zusätzliche HTTP-Requests und wie wir alle wissen ist das BÖSE. Aus diesem Grund sollten wir Bilder nach Base64 encodieren und direkt in das SVG einbetten. Das SVG wiederum kann ebenfalls Base64 encoded und direkt ins CSS eingefügt werden.

Webkit ist der einzige Browser von dem ich weiss, dass er UTF8 in Stylesheets einbetten kann und somit kein Base64 encoden nötig ist (Linebreaks sollten selbstverständlich vermieden werden):

"background-image: url('data:image/svg+xml; charset=utf-8,<svg></svg>"

Der Einsatz von CSS Sprites hilft natürlich ebenfalls die Anzahl der Requests gering zu halten. In SVG besteht die Möglichkeit Elemente einmal zu definieren...

<defs> <image id="image" xlink:href="myimgage.png"/> </defs>

...um sie anschließend im gesamten Dokument weiterzuverwenden:

<use xlink:href="#image" transform="lustigetransformationhier"/>

Aber dies ist das Internet und wie Ihr Euch sicher schon gedacht habt, sind die Dinge nie so rosig wie sie zuerst scheinen. Es gibt da einen ganz bestimmten Browser, der nach wie vor Tricks und Hacks benötigt um so zu spielen wie die anderen Kinder. Und nein, ich rede nicht vom IE, der macht seine Sache ganz gut. In diesem Fall ist Webkit das schwarze Schaf. Webkit lädt keine xlink Referenzen zu externen Resourcen in SVGs, wenn diese als Hintergrundbilder verwendet werden. Externe CSS haben dann keinen Einfluß und Bilder tauchen nicht auf. Deswegen führt kein Weg daran vorbei, nur inline Stylesheets und Base64 encodierte Bilder zu verwenden, wenn man Webkit unterstützen will. Aber selbst wenn alle Bilder als Base64 enkodierte Datensätze in das SVG eingefügt werden, erscheinen Sie nur, wenn Webkit das SVG vorher einmal auf Dokumentenebene "gezeigt" wurde. Ansonsten erscheinen zwar die Vektorelemente des SVG im Hintergrund aber nicht die Bildelemente.

Es gibt einen schmutzigen Hack für dieses Problem, der den Anhängern guter Semantik die Zehnägel kringeln läßt: bevor das Dokument vom Browser gerendert wird, muss man das SVG einmal als Objekt in das Dokument einfügen, z.B: als 1x1px großes, unsichtbares SVG Object. Natürlich kann uns hier ein wenig Javascript gute Dienste leisten:

(function (svgpath){ if( /webkit/gi.test(navigator.userAgent.toLowerCase()) ){ var obj = document.createElement("object"); obj.setAttribute("type", "image/svg+xml"); obj.setAttribute("data", svgpath); obj.setAttribute("width", "1"); obj.setAttribute("height", "1"); obj.setAttribute("style", "width: 0px; height: 0px; position: absolute;visibility : hidden"); document.getElementsByTagName("html")[0].appendChild(obj); } })("../img/mySVG.svg");

Das SVG kann dann wieder entfernt werden, sobald das Dokument geladen wurde.

Kürzlich brauchte ich SVG Sprite Support in Webkit und schrieb diesen Hack. Es durchkämmt einen Stylesheet nach Referenzen zu SVG Hintergrundbildern und parst jedes gefundene SVG nach Stylesheets und Bildern. Anschließend werden die gefunden Referenzen durch inline Stylesheets und Base64 encodierte Bilder erstezt. Zu guter Letzt wird das neue SVG einmal kurz ins Dokument eingesetzt und nach 1 Sekunde wieder entfernt. Gleichzeitig werden die Referenzen im CSS durch UTF8 Versionen ersetzt.

Man kann dem Script beim Aufruf "true" als parameter übergeben um einen "viewSourceMode" zu aktivieren (default ist "false"). In diesem Fall wird die Source der generierten SVGs in Textfeldern angezeigt, um sie für die Weiterverarbeitung bequem zu kopieren.

Seid vorsichtig beim Einsatz, das ganze ist derzeit noch im alpha Stadium. Im Moment kann Geschwindigkeit ein Problem sein, wenn zu große und/oder zu viele Bilder verwendet werden. Beim Coden ist es aber eine nützliche Hilfe.

Trackback

3 Kommentare zu „Bessere CSS Sprites mit SVG“

  1. Kommentar by Mark Mo. 9. Jan 2012, 17:47

    Amazing! If I knew this before I wouldn’t have been littering my mobile webkit stylesheets with base64.

  2. Kommentar by netsi1964 Mo. 19. Mär 2012, 11:40

    Hi Mark,
    Brilliant! yet another an inspirational „eyeopener“ from you here at eleqtriq.com. Might we get (yet another) webapp for this purpose? :-)

    /Sten

  3. Pingback by Revision 65: Infinite Transition Delays, jQuery Hooks, ECMAScript 6 | Working Draft Di. 3. Apr 2012, 12:11

    […] unangenehm. Noch! Außerdem sprachen wir über das Kapseln von Bitmapgrafiken in SVG, wie von Dirk Weber beschrieben und über die Möglichekeit, eine background-position von unten rechts aus anzugeben (siehe Example […]