Bilder mit hohem DPI-Wert für variable Pixeldichten

Boris Smus
Boris Smus

Veröffentlicht: 22. August 2012, zuletzt aktualisiert: 14. April 2025

Aufgrund der vielen Geräte auf dem Markt gibt es eine sehr große Bandbreite an Bildschirmpixeldichten. Anwendungsentwickler müssen eine Vielzahl von Pixeldichten unterstützen, was ziemlich schwierig sein kann. Im mobilen Web werden die Herausforderungen durch mehrere Faktoren verschärft:

  • Große Vielfalt an Geräten mit unterschiedlichen Formfaktoren.
  • Eingeschränkte Netzwerkbandbreite und Akkulaufzeit

Webentwickler sollten Bilder möglichst effizient bereitstellen und dabei die bestmögliche Qualität bieten. Hier finden Sie einige nützliche Methoden, die Sie jetzt und in Zukunft anwenden können.

Vermeiden Sie nach Möglichkeit Bilder.

Bevor Sie davon ausgehen, dass Sie ein Bild einfügen müssen, denken Sie daran, dass das Web zahlreiche leistungsstarke Technologien bietet, die weitgehend unabhängig von Auflösung und dpi sind. Insbesondere Text, SVG und ein Großteil von CSS funktionieren aufgrund der automatischen Pixel-Skalierung des Webs mit devicePixelRatio „einfach so“.

Allerdings lassen sich Rasterbilder nicht immer vermeiden. Möglicherweise erhalten Sie beispielsweise Assets, die sich nur schwer in reinen SVGs oder CSS-Dateien nachbilden lassen. Möglicherweise handelt es sich um ein Foto. Sie können ein Bild zwar automatisch in ein SVG konvertieren, aber die Vektorisierung von Fotos macht wenig Sinn, da vergrößerte Versionen in der Regel nicht gut aussehen.

Entwicklung der Pixeldichte

In den Anfangszeiten hatten Computerdisplays eine Pixeldichte von 72 oder 96 dpi (Dots per Inch).

Die Pixeldichte von Displays wurde nach und nach verbessert, was vor allem auf den Fortschritt bei Mobilgeräten zurückzuführen ist. Da Nutzer ihre Smartphones in der Regel näher an ihr Gesicht halten, sind die Pixel besser sichtbar. 2008 waren Smartphones mit 150 dpi der neue Standard. Die Displaydichte wurde weiter erhöht und die heutigen Smartphones haben Displays mit 300 dpi.

In der Praxis sollten Bilder mit niedriger Pixeldichte auf neuen Bildschirmen genauso aussehen wie auf alten. Im Vergleich zu den scharfen Bildern mit hoher Pixeldichte, die Nutzer gewohnt sind, wirken Bilder mit niedriger Pixeldichte jedoch irritierend und pixelig. Im Folgenden sehen Sie eine grobe Simulation, wie ein 1:1-Bild auf einem 2:1-Display aussieht. Im Gegensatz dazu sieht das 2-fache Bild ziemlich gut aus.

1 Pixel 2-fache Pixelanzahl
Baboon, einfache Pixeldichte Baboon mit doppelter Pixeldichte.
Baboons bei unterschiedlichen Pixeldichten.

Google Pixel im Web

Als das Web entwickelt wurde, hatten 99% der Bildschirme 96 dpi und es wurden nur wenige Vorkehrungen für Variationen getroffen. Da es eine große Vielfalt an Bildschirmgrößen und -dichten gibt, brauchen wir eine Standardmethode, mit der Bilder auf allen Geräten gut aussehen.

Die HTML-Spezifikation hat dieses Problem durch die Definition eines Referenzpixels gelöst, mit dem Hersteller die Größe eines CSS-Pixels bestimmen.

Anhand des Referenzpixels kann ein Hersteller die Größe des physischen Pixels des Geräts im Vergleich zum Standard- oder Idealpixel bestimmen. Dieses Verhältnis wird als Pixel-Verhältnis des Geräts bezeichnet.

Pixel-Verhältnis des Geräts berechnen

Angenommen, ein Smartphone hat ein Display mit einer physischen Pixelgröße von 180 Pixeln pro Zoll (ppi). Das Pixel-Verhältnis des Geräts wird in drei Schritten berechnet:

  1. Vergleichen Sie die tatsächliche Entfernung, in der das Gerät gehalten wird, mit der Entfernung für das Referenzpixel.

    Gemäß der Spezifikation sind bei 28 Zoll 96 Pixel pro Zoll ideal. Bei einem Smartphone befinden sich die Gesichter der Nutzer näher am Gerät als bei Laptops und Computern. Für die folgenden Gleichungen gehen wir von einer Entfernung von 46 Zentimetern aus.

  2. Multiplizieren Sie das Abstandsverhältnis mit der Standarddichte (96 ppi), um die ideale Pixeldichte für die angegebene Entfernung zu erhalten.

    idealPixelDensity = (28/18) * 96 = 150 Pixel pro Zoll (ungefähr)

  3. Das Pixel-Verhältnis des Geräts ergibt sich aus dem Verhältnis der physischen Pixeldichte zur idealen Pixeldichte.

    devicePixelRatio = 180/150 = 1,2

Ein Referenzwinkelpixel, das veranschaulicht, wie das Pixel-Verhältnis des Geräts berechnet wird.

Wenn ein Browser also wissen muss, wie er ein Bild entsprechend der idealen oder Standardauflösung auf die Bildschirmgröße skalieren muss, bezieht er sich auf das Pixelverhältnis des Geräts von 1,2. Das bedeutet, dass dieses Gerät für jedes ideale Pixel 1,2 physische Pixel hat. Die Formel für die Umwandlung zwischen idealen (gemäß der Webspezifikation) und physischen Pixeln (Punkte auf dem Bildschirm des Geräts) lautet:

physicalPixels = window.devicePixelRatio * idealPixels

Bisher haben Geräteanbieter devicePixelRatios (DPRs) in der Regel gerundet. Für iPhones und iPads von Apple wird ein DPR von 1 und für die Retina-Modelle von Apple ein DPR von 2 angegeben. Die CSS-Spezifikation empfiehlt Folgendes:

Die Pixeleinheit bezieht sich auf die ganze Anzahl der Gerätepixel, die dem Referenzpixel am besten entspricht.

Ein Grund dafür, dass runde Proportionen besser sein können, ist, dass sie zu weniger Subpixel-Artefakten führen können.

Die Realität der Gerätelandschaft ist jedoch viel vielfältiger und Android-Smartphones haben oft eine DPR von 1,5. Das Nexus 7-Tablet hat einen DPR von etwa 1,33, der durch eine ähnliche Berechnung wie im vorherigen Beispiel ermittelt wurde. In Zukunft werden weitere Geräte mit variablen DPRs eingeführt. Daher sollten Sie niemals davon ausgehen, dass Ihre Kunden eine ganzzahlige DPR haben.

HiDPI-Bildtechniken

Es gibt viele Methoden, um das Problem zu lösen, Bilder so schnell wie möglich in der bestmöglichen Qualität zu präsentieren. Sie lassen sich grob in zwei Kategorien unterteilen:

  1. Optimieren einzelner Bilder
  2. Auswahl zwischen mehreren Bildern optimieren

Ansätze mit einem einzelnen Bild: Verwenden Sie ein Bild, aber machen Sie etwas Cleveres damit. Diese Ansätze haben den Nachteil, dass Sie zwangsläufig Abstriche bei der Leistung machen müssen, da Sie auch auf älteren Geräten mit niedrigerer Auflösung HiDPI-Bilder herunterladen müssen. Hier sind einige Ansätze für den Fall mit einem einzelnen Bild:

  • Stark komprimiertes HiDPI-Bild
  • Super tolles Bildformat
  • Progressives Bildformat

Mehrere Bilder: Verwenden Sie mehrere Bilder, aber wählen Sie auf intelligente Weise aus, welches geladen werden soll. Bei diesen Ansätzen müssen Entwickler mehrere Versionen desselben Assets erstellen und dann eine Entscheidungstrategie entwickeln. So sehen die einzelnen Optionen aus:

  • JavaScript
  • Serverseitige Bereitstellung
  • CSS-Medienabfragen
  • Integrierte Browserfunktionen (image-set(), <img srcset>)

Stark komprimiertes HiDPI-Bild

Bilder machen bereits 60% der Bandbreite aus, die für den Download einer durchschnittlichen Website benötigt wird. Durch die Bereitstellung von HiDPI-Bildern für alle Kunden können wir diese Zahl erhöhen. Wie viel größer kann es werden?

Ich habe einige Tests durchgeführt, bei denen 1- und 2-fache Bildfragmente mit einer JPEG-Qualität von 90, 50 und 20 generiert wurden.

Sechs Versionen eines Bildes mit unterschiedlicher Komprimierung und Pixeldichte. Sechs Versionen eines Bildes mit unterschiedlicher Komprimierung und Pixeldichte. Sechs Versionen eines Bildes mit unterschiedlicher Komprimierung und Pixeldichte.

Diese kleine, unwissenschaftliche Stichprobe legt nahe, dass das Komprimieren großer Bilder ein gutes Verhältnis zwischen Qualität und Größe bietet. Meiner Meinung nach sehen stark komprimierte Bilder mit doppelter Auflösung sogar besser aus als unkomprimierte Bilder mit einfacher Auflösung.

Allerdings ist es schlechter, Geräten mit doppelter Auflösung Bilder mit geringer Qualität und starker Komprimierung zu senden, als ihnen Bilder mit höherer Qualität zu senden. Dieser Ansatz würde zu Strafen bei der Bildqualität führen. Wenn Sie quality: 90-Bilder mit quality: 20-Bildern vergleichen, ist das Bild weniger scharf und körniger. Artefakte mit quality:20 sind möglicherweise nicht akzeptabel, wenn hochwertige Bilder wichtig sind (z. B. in einer Fotobetrachter-App) oder für App-Entwickler, die keine Kompromisse eingehen möchten.

Dieser Vergleich wurde ausschließlich mit komprimierten JPEGs durchgeführt. Es ist wichtig zu beachten, dass es viele Kompromisse zwischen den weit verbreiteten Bildformaten (JPEG, PNG, GIF) gibt. Das bringt uns zu…

WebP: Tolles Bildformat

WebP ist ein ziemlich interessantes Bildformat, das sehr gut komprimiert und gleichzeitig eine hohe Bildtreue beibehält.

Eine Möglichkeit, die WebP-Unterstützung zu prüfen, ist die Verwendung von JavaScript. Laden Sie ein 1-Pixel-Bild mit data-uri, warten Sie, bis das Bild geladen wurde oder Fehlerereignisse ausgelöst wurden, und prüfen Sie dann, ob die Größe korrekt ist. Modernizr wird mit einem solchen Funktionserkennungs-Script geliefert, das mit Modernizr.webp verfügbar ist.

Besser ist es jedoch, dies direkt in CSS mit der image()-Funktion zu tun. Wenn Sie also ein WebP-Bild und einen JPEG-Fallback haben, können Sie Folgendes schreiben:

#pic {
  background: image("foo.webp", "foo.jpg");
}

Dieser Ansatz birgt einige Probleme. Erstens ist image() nicht weit verbreitet. Zweitens: Die WebP-Komprimierung ist zwar um ein Vielfaches besser als JPEG, aber es handelt sich immer noch um eine relativ geringe Verbesserung – etwa 30% kleiner als in dieser WebP-Galerie. WebP allein reicht also nicht aus, um das Problem mit hoher Auflösung zu lösen.

Progressive Bildformate

Progressive Bildformate wie JPEG 2000, Progressive JPEG, Progressive PNG und GIF haben den (etwas umstrittenen) Vorteil, dass das Bild schon angezeigt wird, bevor es vollständig geladen ist. Sie können einen gewissen Größenoverhead verursachen, obwohl es widersprüchliche Beweise dafür gibt. Jeff Atwood behauptete, dass der progressive Modus „die Größe von PNG-Bildern um etwa 20% und die Größe von JPEG- und GIF-Bildern um etwa 10% erhöht“. Stoyan Stefanov behauptet jedoch, dass der progressive Modus bei großen Dateien in den meisten Fällen effizienter ist.

Auf den ersten Blick klingen progressive Bilder sehr vielversprechend, wenn es darum geht, Bilder in der bestmöglichen Qualität so schnell wie möglich zu liefern. Der Browser kann das Herunterladen und Decodieren eines Bildes beenden, sobald er weiß, dass zusätzliche Daten die Bildqualität nicht verbessern (d. h. alle Verbesserungen der Wiedergabequalität liegen unter einem Pixel).

Verbindungen können zwar schnell beendet werden, aber oft ist es teuer, sie neu zu starten. Bei einer Website mit vielen Bildern ist es am effizientesten, eine einzelne HTTP-Verbindung aufrechtzuerhalten und so lange wie möglich wiederzuverwenden. Wenn die Verbindung vorzeitig beendet wird, weil ein Bild ausreichend heruntergeladen wurde, muss der Browser eine neue Verbindung herstellen. Das kann in Umgebungen mit niedriger Latenz sehr langsam sein.

Eine Lösung für dieses Problem ist die Verwendung der HTTP-Bereichsanfrage, mit der Browser einen Bereich von Byte angeben können, der abgerufen werden soll. Ein intelligenter Browser könnte eine HEAD-Anfrage senden, um den Header abzurufen, ihn zu verarbeiten, zu entscheiden, wie viel des Bildes tatsächlich benötigt wird, und ihn dann abzurufen. Leider wird der HTTP-Bereich auf Webservern nur schlecht unterstützt, was diesen Ansatz unpraktisch macht.

Eine offensichtliche Einschränkung dieses Ansatzes besteht darin, dass Sie nicht auswählen können, welches Bild geladen werden soll, sondern nur die verschiedenen Auflösungen desselben Bildes. Daher wird der Anwendungsfall Art Direction nicht abgedeckt.

Mit JavaScript festlegen, welches Bild geladen werden soll

Der erste und naheliegendste Ansatz, um zu entscheiden, welches Bild geladen werden soll, besteht darin, JavaScript im Client zu verwenden. So können Sie alles über Ihren User-Agent herausfinden und die richtige Entscheidung treffen. Mit window.devicePixelRatio können Sie das Pixelverhältnis des Geräts ermitteln, die Bildschirmbreite und -höhe abrufen und sogar mit navigator.connection ein wenig Netzwerkverbindungs-Sniffing betreiben oder eine gefälschte Anfrage senden, wie es die foresight.js-Bibliothek tut. Sobald Sie alle diese Informationen erfasst haben, können Sie entscheiden, welches Bild geladen werden soll.

Es gibt ungefähr eine Million JavaScript-Bibliotheken, die diese Technik verwenden. Leider sticht keine davon besonders hervor.

Ein großer Nachteil ist, dass das Laden von Bildern verzögert wird, bis der Vorschau-Parser fertig ist. Das bedeutet, dass Bilder erst nach dem Auslösen des Ereignisses pageload heruntergeladen werden. Weitere Informationen finden Sie im Artikel von Jason Grigsby.

Festlegen, welches Bild auf dem Server geladen werden soll

Sie können die Entscheidung auf die Serverseite verschieben, indem Sie benutzerdefinierte Anfragehandler für jedes Bild schreiben, das Sie bereitstellen. Ein solcher Handler würde anhand des User-Agents (der einzigen Information, die an den Server weitergegeben wird) nach Retina-Unterstützung suchen. Je nachdem, ob die serverseitige Logik HiDPI-Assets bereitstellen soll, wird das entsprechende Asset geladen, das gemäß einer bekannten Konvention benannt ist.

Leider enthält der User-Agent nicht unbedingt genügend Informationen, um zu entscheiden, ob ein Gerät Bilder in hoher oder niedriger Qualität erhalten soll. Außerdem sollten Sie jede Lösung vermeiden, bei der die User-Agent für Stilentscheidungen verwendet wird.

CSS-Medienabfragen verwenden

Da sie deklarativ sind, können Sie mit CSS-Medienabfragen Ihre Absicht formulieren und den Browser die richtige Entscheidung treffen lassen. Neben der gängigsten Verwendung von Medienabfragen, der Gerätegröße, können Sie auch devicePixelRatio abgleichen. Die zugehörige Medienabfrage ist „device-pixel-ratio“ und hat wie erwartet minimale und maximale Varianten.

Wenn Sie Bilder mit hoher Auflösung laden möchten und das Pixelverhältnis des Geräts einen Grenzwert überschreitet, haben Sie folgende Möglichkeiten:

#my-image { background: (low.png); }

@media only screen and (min-device-pixel-ratio: 1.5) {
  #my-image { background: (high.png); }
}

Mit den verschiedenen Anbieterpräfixen wird es etwas komplizierter, vor allem aufgrund der großen Unterschiede bei der Platzierung der Präfixe „min“ und „max“:

@media only screen and (min--moz-device-pixel-ratio: 1.5),
    (-o-min-device-pixel-ratio: 3/2),
    (-webkit-min-device-pixel-ratio: 1.5),
    (min-device-pixel-ratio: 1.5) {

  #my-image {
    background:url(high.png);
  }
}

Mit diesem Ansatz können Sie die Vorteile des Vorab-Parsings wieder nutzen, die mit der JavaScript-Lösung verloren gegangen sind. Außerdem haben Sie die Flexibilität, Ihre responsiven Wendepunkte auszuwählen (z. B. Bilder mit niedriger, mittlerer und hoher DPI), was beim serverseitigen Ansatz nicht möglich war.

Leider ist es immer noch etwas unhandlich und führt zu seltsam aussehendem CSS oder erfordert eine Vorverarbeitung. Außerdem ist dieser Ansatz auf CSS-Properties beschränkt. Es gibt also keine Möglichkeit, ein <img src> festzulegen, und alle Bilder müssen Elemente mit einem Hintergrund sein. Wenn Sie sich ausschließlich auf das Pixelverhältnis des Geräts verlassen, kann es vorkommen, dass Ihr Smartphone mit hoher Auflösung ein riesiges Bild-Asset im doppelten Format herunterlädt, während es mit einer EDGE-Verbindung verbunden ist. Das ist nicht optimal.

Da image-set() eine CSS-Funktion ist, wird das Problem für <img>-Tags nicht behoben. Geben Sie @srcset ein, um dieses Problem zu beheben. Im nächsten Abschnitt werden image-set und srcset genauer erläutert.

Browserfunktionen für die Unterstützung hoher Auflösungen

Wie Sie die Unterstützung hoher Auflösungen angehen, hängt letztendlich von Ihren spezifischen Anforderungen ab. Alle oben genannten Ansätze haben Nachteile.

Da image-set und srcset weithin unterstützt werden, sind sie die beste Lösung. Es gibt weitere Best Practices, die wir für ältere Browser anwenden können.

Worin unterscheiden sich diese beiden? image-set() ist eine CSS-Funktion, die als Wert für die CSS-Eigenschaft „background“ verwendet werden kann. srcset ist ein <img>-spezifisches Attribut mit ähnlicher Syntax. Mit beiden Tags können Sie Bilddeklarationen angeben. Mit dem srcset-Attribut können Sie jedoch auch konfigurieren, welches Bild basierend auf der Größe des Darstellungsbereichs geladen werden soll.

Best Practices für Bildsätze

Die image-set()-Syntax nimmt eine oder mehrere kommagetrennte Bilddeklarationen an, die aus einem URL-String oder einer url()-Funktion gefolgt von der entsprechenden Auflösung bestehen. Beispiel:

image-set(
  url("image1.jpg") 1x,
  url("image2.jpg") 2x
);

/* You can also include image-set without `url()` */
image-set(
  "image1.jpg" 1x,
  "image2.jpg" 2x
);

Dadurch wird dem Browser mitgeteilt, dass zwei Bilder zur Auswahl stehen. Ein Bild ist für 1:1-Displays und das andere für 2:1-Displays optimiert. Der Browser wählt dann anhand verschiedener Faktoren aus, welche Version geladen werden soll. Dazu kann auch die Netzwerkgeschwindigkeit gehören, wenn der Browser intelligent genug ist.

Der Browser lädt nicht nur das richtige Bild, sondern skaliert es auch entsprechend. Mit anderen Worten: Der Browser geht davon aus, dass zwei Bilder doppelt so groß sind wie ein einzelnes Bild. Daher wird das doppelte Bild um den Faktor 2 verkleinert, damit es auf der Seite dieselbe Größe hat.

Anstatt „1x“, „1,5x“ oder „Nx“ anzugeben, können Sie auch eine bestimmte Pixeldichte des Geräts in dpi angeben.

Wenn Sie Bedenken bezüglich älterer Browser haben, die das Attribut image-set nicht unterstützen, können Sie einen Fallback hinzufügen, damit immer ein Bild angezeigt wird. Beispiel:

/* Fallback support. */
background-image: url(icon1x.jpg);
background-image: image-set(
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);

image-set(
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);

In diesem Beispielcode wird das entsprechende Asset in Browsern geladen, die „image-set“ unterstützen. Andernfalls wird das 1x-Asset verwendet.

An dieser Stelle fragen Sie sich vielleicht, warum Sie nicht einfach eine Polyfill-Version (d. h. einen JavaScript-Shim) für image-set() erstellen und damit fertig sind. Es ist jedoch ziemlich schwierig, effiziente polyfills für CSS-Funktionen zu implementieren. Eine ausführliche Erklärung dazu finden Sie in dieser Diskussion zum www-Stil.

Bild-Srcset

Zusätzlich zu den Deklarationen, die image-set bereitstellt, nimmt das srcset-Element auch Werte für Breite und Höhe an, die der Größe des Darstellungsbereichs entsprechen. So wird versucht, die relevanteste Version zu senden.

<img alt="my awesome image"
  src="banner.jpeg"
  srcset="banner-HD.jpeg 2x, banner-phone.jpeg 640w, banner-phone-HD.jpeg 640w 2x">

In diesem Beispiel wird banner-phone.jpeg auf Geräten mit einer Ansichtsbreite von weniger als 640 Pixeln, banner-phone-HD.jpeg auf Geräten mit hoher DPI und kleinem Bildschirm, banner-HD.jpeg auf Geräten mit hoher DPI und einem Bildschirm mit mehr als 640 Pixeln und banner.jpeg auf allen anderen Geräten ausgeliefert.

„image-set“ für Bildelemente verwenden

Es kann verlockend sein, Ihre img-Elemente durch <div>s mit Hintergründen zu ersetzen und den Ansatz mit Bildsätzen zu verwenden. Das funktioniert, aber mit Einschränkungen. Der Nachteil dabei ist, dass das <img>-Tag einen langfristigen semantischen Wert hat. In der Praxis ist dies für die Barrierefreiheit und Webcrawler wichtig.

Sie können die CSS-Eigenschaft „content“ verwenden, die das Bild automatisch basierend auf devicePixelRation skaliert. Beispiel:

<div id="my-content-image"
  style="content: -webkit-image-set(
    url(icon1x.jpg) 1x,
    url(icon2x.jpg) 2x);">
</div>

Polyfill-srcset

Eine praktische Funktion von srcset ist, dass es einen natürlichen Fallback gibt. Wenn das srcset-Attribut nicht implementiert ist, verarbeiten alle Browser das src-Attribut. Da es sich nur um ein HTML-Attribut handelt, ist es auch möglich, Polyfills mit JavaScript zu erstellen.

Diese Polyfill-Funktion wird mit Unit-Tests geliefert, um sicherzustellen, dass sie möglichst nah an der Spezifikation liegt. Außerdem gibt es Prüfungen, die verhindern, dass der Polyfill Code ausführt, wenn srcset nativ implementiert ist.

Fazit

Die beste Lösung für Bilder mit hoher Auflösung sind SVGs und CSS. Dies ist jedoch nicht immer eine realistische Lösung, insbesondere bei Websites mit vielen Bildern.

Ansätze in JavaScript, CSS und serverseitigen Lösungen haben ihre Stärken und Schwächen. Am besten eignen sich image-set und srcset.

Zusammenfassend sind das meine Empfehlungen:

  • Verwenden Sie für Hintergrundbilder image-set mit den entsprechenden Fallbacks für Browser, die es nicht unterstützen.
  • Verwenden Sie für Inhaltsbilder eine srcset-Polyfill oder image-set (siehe oben).
  • Wenn Sie bereit sind, die Bildqualität zu opfern, können Sie stark komprimierte 2x-Bilder verwenden.