HTML Bilder in Responsive Websites

veröf­fent­licht am 02 December 2015

Einige Ansätze Bilder in Webseiten zu verwal­ten, die sich an die Bildschirm­größe anpassen müssen.

Jeder, der schon einmal eine Webseite für mobile Geräte erstellt hat, kennt das Problem. Bilder sollen überall gut aussehen und man will nicht mehr Bandbreite verwenden als unbedingt notwen­dig. Die umset­zung dafür war früher ziemlich einfach: man schaut sich das Layout der Seite an, misst wie groß ein Bild in Pixeln sein muss und benutzt eine der vielen Möglich­keiten das Bild auf exakt die Größe zu skalieren.

Ein Bild mit genauer Größe

Das genaue anpassen der Bilder funktio­niert auch heute noch recht gut und benötigt so gut wie keinen Aufwand. Im einfachsten Fall strengen wir unser bevor­zugtes Grafi­k-­Pro­gramm an das Bild zu skalieren.

Es wäre aber langweilig wenn es nicht Probleme gäbe:

Dadurch, dass es die einfachste imple­men­tie­rung ist, werden wir vermut­lich für immer diese Imple­men­tie­rung sehen. Das heißt aber nicht, dass es nicht bessere gibt.

Mehrere Bilder mit unterschiedlichen Größen

Ok, die Anzahl an Möglich­keiten ist überwäl­ti­gend. Ich habe einen älteren (2012) Artikel von CSS-Tricks gefunden der auf viele Wege eingeht. Es gibt auch eine schöne Tabelle mit vielen Techniken die man verwenden kann um die richtigen Bilder aus zu liefern.

Diese Hack-­Lö­sungen inter­es­sieren mich aller­dings alle nicht! Ich möchte ein zukunfts­si­cheres Markup haben das am besten ohne JavaS­cript funktio­niert. Tja, um meinem Mund zu stopfen bekam ich gleich 3 native Wege dies um zu setzen.

Das srcset Attribut mit x Angabe

<img srcset="image.jpg 1x, image-2.jpg 2x, image-3.jpg 3x"
     alt="Dieses Bild ist immer scharf"
     src="image.jpg">

Diese Syntax erklärt sich von selbst. Wenn der Browser es unter­stützt guckt er sich das srcset Attribut an und wählt anhand der devicePixelRatio welches Bild er lädt. Wenn der Browser es nicht unter­stützt ignoriert er das srcset und lädt einfach das Bild in der src.

Aller­dings bleiben viele der negativen Punkte bestehen:

Grund­sätz­lich haben wir also einen Negati­v-­Punkt ins Positive gebracht (HiDPI), aber auch einen ins Negative (Verwal­tung/­Meh­rere Bilder). Spoiler: Alle Methoden brauchen mehrere Bilder, daher werden wir diesen Punkt nicht mehr los.

Ich erwähne diesen Weg zuerst, da er den größten Browser-Support hat (ios 8+). Aller­dings ist dies fast irrele­vant, den die erwei­terte w Version hat nur minimal schlech­teren Support (ios 9+).

Noch etwas: Ich würde das srcset Attribut immer zuerst dekla­rie­ren, damit der pre-loader dieses garan­tiert zuerst sieht. Ich weiß nicht wie gut die heutigen pre-loader sind, aber ich hoffe das ich damit einen unnötigen Anfragen sparen kann. Ob es einen Unter­schied macht hab ich aller­dings nicht getes­tet. Sollte jemand mehr wissen: gerne in die Kommentare.

Das srcset Attribut mit w Angabe

Dieses Version hat nur minimal schlech­teren Browser-Support als die x Version. Da jeder, der einen älteren Browser benutzt, die Seite dennoch problemlos sehen kann denke ich nicht, dass das ein Problem ist.

<img srcset="banner-720.jpg 720w, banner-1080.jpg 1080w"
     sizes="100vw"
     alt="Dieses Bild wird perfekt ausgewählt"
     src="banner-720.jpg">

In dieser Version definieren wir, wie breit das Bild in Pixel ist. Der Browser wählt sich dann das Beste aus. Es gibt momentan keine Möglich­keit die Auswahl anhand der Höhe zu treffen.

Der Browser hat nur ein Problem mit diesem Weg. Er muss wissen wie groß das Bild im Layout sein wird. Da aber der Styles­heet meist nicht sofort verfügbar ist, müsste der Browser mit dem laden von dem Bild warten bis er alle CSS-Da­teien geladen hat. Daher sieht der Standard ein weites Attribut vor. sizes definiert wie breit das Bild sein wird. Das heißt: wir müssen leider unser Layout zu einem Teil im Markup verewi­gen. Bei dem Banner-­Bei­spiel ist dies noch recht einfach, aber es kann schnell kompli­ziert werden. Für diesem Blog benutze ich momentan für die Thumb­nails diese Syntax:

<img sizes="(min-width: 48em) 9rem, 3rem">

Damit sag ich dem Browser, dass die Thumb­nails 9rem breit sind solang die Media-Query (min-­width: 48rem) zutrifft. Das Beispiel kann aber beliebig kompli­ziert werden. Hier ein paar Beispiel für Bilder in Bootstrap:

<!-- Bootstrap 3 mit Container-Breite -->
<img sizes="(min-width: 1200px) 1170px, (min-width: 992px) 970px, (min-width: 768px) 750px, 100vw">
<!-- Bootstrap 3 in einer Spalte -->
<img sizes="(min-width: 1200px) 1140px, (min-width: 992px) 940px, (min-width: 768px) 720px, calc(100vw - 30px)">
<!-- Bootstrap 4 mit Container-Breite -->
<img sizes="(min-width: 75em) 72.25rem, (min-width: 62em) 60rem, (min-width: 48em) 45rem, (min-width: 34em) 34rem, 100vw">
<!-- Bootstrap 4 in einer Spalte -->
<img sizes="(min-width: 75em) 70.375rem, (min-width: 62em) 58.125rem, (min-width: 48em) 43.125rem, (min-width: 34em) 32.125rem, calc(100vw - 1.875rem)">
<!-- kleine warnung: ich hab die Angaben nicht getestet -->

Wie man sieht ist es möglich calc() zu verwen­den, aber das macht die Angaben leider nur noch Aufwen­di­ger. Wenn wir nun eine Größe in CSS ändern müssen wir auch unser Markup ändern…. unschön.

Aber lohnt es sich?

Somit haben wir nun fast alle negativen Punkte, die für den Nutzer relevant sind, ins Positive gebracht. Also nun das Zähne-Knir­schen für die Entwickler:

Dies ist somit, sofern man die Zeit im Projekt hat die Bilder zu organi­sie­ren, die beste Möglich­keit ein Bild in der best mögli­chen Größe zu laden. Das einzige, was es nicht löst, ist der austausch von dem Bild unter bestimmten Situa­tio­nen. Ein kleines Bild sollte uu. einen anderen Ausschnitt zeigen im gegen­satz zu einem, das auf dem Desktop in Volbild angezeigt wird. Da wir aber auf modernen Smart­phones zum teil eine höhere Auflö­sung haben als auf einem Desktop können wir die Breite vom Bild nicht verwenden um zwischen verschie­denen Ausschnitten zu wählen.

Das <picture> Element

Dieses Element kann nun auch das Problem des Bildin­haltes lösen. Ich werfe einfach direkt ein Beispiel rein:

<picture>
    <source srcset="desktop-thumbnail-144.jpg 144w, desktop-thumbnail-288.jpg 288w"
            sizes="9rem"
            media="(min-width: 62em)">
    <source srcset="tablet-thumbnail-128.jpg 128w, tablet-thumbnail-256.jpg 256w"
            sizes="8rem"
            media="(min-width: 48em)">
    <img src="dektop-thumbnail-144.jpg"
         srcset="mobile-thumbnail-48.jpg 48w, mobile-thumbnail-96.jpg 96w"
         sizes="3rem"
         alt="Wunderschönes Thumbnail">
</picture>

Es ist möglich jeder einzelnen &lt;source&gt; eine eigene Media-Query zu geben um zu definieren wann diese zu trifft. Der Browser geht die sources in der Defini­ti­ons­-Rei­hen­folge durch und nimmt sich die Erste die zutrifft, daher fange ich mit dem Größten an. Damit erspare ich mit die merkwür­digen (max-­width: 61.9em) Abfragen um die Bildaus­wahl in beide Richtungen zu limitie­ren. In meinen Tests scheint das &lt;img&gt;-Element auch als source ohne Media-­Ab­frage zu gelten. Das ist auch deshalb inter­essant, da das &lt;picture&gt;-Element einen noch schlech­teren Browser-Support hat als das srcset-Attribut.

Diese Variante erlaubt es nun für ein kleines Layout ein anderes Bild zu verwen­den, welches zum Beispiel einen kleineren Ausschnitt zeigt. Natür­lich sind auch Media-Que­ries wie print möglich. Es ist aller­dings nicht möglich das Bild in bestimmten Layouts überhaupt nicht zu laden. Eine Möglich­keit dafür wäre der klassi­sche trans­pa­rente Gif-Platzhalter.

Das &lt;picture&gt; Element hat noch einen weiteren theore­ti­schen Vorteil. Ähnlich wie bei dem &lt;video&gt; Element kann jeder &lt;source&gt; ein type="image/jpeg" gegeben werden. Der Browser lädt dann diese nur, wenn er diesen Typ unter­stützt. Mein erster Gedanke ging an SVG, doch jeder Browser, der &lt;picture&gt; unter­stützt, sollte SVG bereits unter­stütz­ten. Das Einzige was mir noch einfallen würde ist WebP, welches derzeit nur von Blink, also Chrome und Opera (und mobile Varian­ten) unter­stützt wird, aber das Format ist den Aufwand einer weiteren Kodie­rung nicht wert… außer vielleicht wenn dringend Bandbreite gespart werden muss und eine stärkere JPEG-­Kom­pres­sion nicht in Frage kommt.

Schlusswort

Mehrere Bilder in verschie­denen Größen an zu bieten ist eine super Sache um die bestmög­liche Erfah­rung auf allen Geräten zu bieten und da alle Varianten (die ich gezeigt hab) Rückwärts­-­Kom­pa­tibel sind spricht nichts dagegen diese sofort ein zu setzen. Wenn voller Browser-­Sup­port zwingend nötig ist, kann auch mit einem Polyfill wie Picturefill nachge­holfen werden.

Aller­dings gibt es den Nachteile der kompli­zier­teren Konfi­gu­ra­tion und bei sehr vielen Bildern ist die Dateigröße auf dem Server auch nicht zu vernach­läs­si­gen. In meinen Tests ist es häufig schlauer das Bild in einer höheren Auflö­sung aus zu liefern und dafür eine niedri­gere Jpeg-Qua­lität zu verwenden (zB. 65% anstatt 85%). Die Dateien sind dann meistens nicht viel größer als die low-DPI Variante und auf einem high-DPI Gerät ist der Unter­schied kaum sicht­bar. Es sieht aller­dings immer noch deutlich besser aus, als ein low-DPI Bild auf einem high-DPI Gerät.

Wo es aller­dings viel Sinn macht ist bei Bannern oder anderen Bildern, die ihre Größe anhand des Viewports ändern. Dort spielt häufig nicht nur die Daten­größe eine Rolle, sondern auch die Skalie­rung vom Endge­rät. Der Safari zum Beispiel erzeugt sehr schnell hässliche Treppen bei einer starken Skalie­rung und der Chrome erzeugt immer sehr verwa­schene Bilder. Dort macht ein srcset sehr viel Sinn.

Und zuletzt ist für Art-Di­rec­tion ein &lt;picture&gt;-Element sehr viel Wert. Aller­dings kann in einigen Fällen auch eine CSS-Lö­sung mit Media-­Ab­fragen sehr gut funktio­nie­ren. So eine Lösung benötigt dann auch keinen Polyfill.