Bilder mit hohem DPI-Wert für variable Pixeldichten

Boris Smus
Boris Smus

Eines der Merkmale der heutigen komplexen Gerätelandschaft ist, dass es eine sehr große Bandbreite an Bildschirmpixeldichten gibt. Einige Geräte haben sehr hochauflösende Displays, während andere hinterherhinken. Anwendungsentwickler müssen eine Reihe 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

Bei Bildern geht es den Web-App-Entwicklern darum, Bilder in der bestmöglichen Qualität möglichst effizient bereitzustellen. In diesem Artikel werden einige nützliche Methoden beschrieben, die Sie heute und in naher Zukunft anwenden können.

Vermeiden Sie nach Möglichkeit Bilder.

Bevor Sie diese Büchse der Pandora öffnen, denken Sie daran, dass das Web viele 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 im Web (über devicePixelRatio) „einfach so“.

Allerdings lassen sich Rasterbilder nicht immer vermeiden. Möglicherweise erhalten Sie Assets, die sich nur schwer in reinem SVG/CSS replizieren lassen, oder Sie haben es mit einem Foto zu tun. Sie können das Bild zwar automatisch in SVG konvertieren, aber eine vektorisierte Fotografie macht wenig Sinn, da vergrößerte Versionen in der Regel nicht gut aussehen.

Hintergrund

Eine kurze Geschichte der Displaydichte

Anfangs hatten Computerdisplays eine Pixeldichte von 72 oder 96 dpi (Punkte pro Zoll).

Die Pixeldichte von Displays wurde nach und nach verbessert, was vor allem auf den mobilen Einsatz zurückzuführen ist, bei dem Nutzer ihre Smartphones in der Regel näher an ihr Gesicht halten, wodurch Pixel besser sichtbar sind. 2008 waren Smartphones mit 150 dpi der neue Standard. Der Trend zu erhöhter Bildschirmdichte setzte sich fort und die heutigen neuen Smartphones haben 300-dpi-Displays (mit der Marke "Retina" von Apple).

Der heilige Gral ist natürlich ein Display, bei dem Pixel vollständig unsichtbar sind. Beim Formfaktor für Smartphones könnte die aktuelle Generation von Retina-/HiDPI-Displays an diesem Idealwert liegen. Neue Hardware- und Wearables-Klassen wie Project Glass werden jedoch wahrscheinlich weiterhin die Pixeldichte erhöhen.

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. Unten sehen Sie eine grobe Simulation, wie ein 1:1-Bild auf einem 2:1-Display aussehen würde. Im Gegensatz dazu sieht das 2-fache Bild recht gut aus.

Pavian 1x
Baboon 2x
Baboons! bei unterschiedlichen Pixeldichten.

Pixel im Web

Als das Web entwickelt wurde, hatten 99 % der Bildschirme 96 dpi (oder gaben vor, 96 dpi zu haben) und es wurden nur wenige Vorkehrungen für Abweichungen in dieser Hinsicht getroffen. Aufgrund der großen Vielfalt an Bildschirmgrößen und -dichten benötigten wir eine Standardmethode, mit der Bilder auf einer Vielzahl von Bildschirmdichten und -abmessungen gut aussehen.

In der HTML-Spezifikation wurde dieses Problem vor Kurzem durch die Definition eines Referenzpixels angegangen, 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 Verhältnis zum Standard- oder idealen Pixel ermitteln. Dieses Verhältnis wird als Geräte-Pixel-Verhältnis bezeichnet.

Pixelverhä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 den tatsächlichen Abstand, in dem das Gerät gehalten wird, mit dem Abstand für das Referenzpixel.

    Laut Spezifikation beträgt der Idealwert bei 28 Zoll 96 Pixel pro Zoll. Da es sich jedoch um ein Smartphone handelt, halten Nutzer das Gerät näher an ihr Gesicht als einen Laptop. Nehmen wir als Beispiel einen Abstand von 18 Zoll.

  2. Multiplizieren Sie das Entfernungsverhä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. Berechnen Sie das Verhältnis der physischen Pixeldichte zur idealen Pixeldichte, um das Gerätepixelverhältnis zu erhalten.

    devicePixelRatio = 180/150 = 1,2

So wird „devicePixelRatio“ berechnet.
Diagramm mit einem Referenzwinkelpixel, das die Berechnung von „devicePixelRatio“ veranschaulicht

Wenn ein Browser also wissen muss, wie ein Bild so skaliert werden muss, dass es der idealen oder Standardauflösung des Bildschirms entspricht, greift er auf das Pixelverhältnis des Geräts von 1,2 zurück. Das bedeutet, dass dieses Gerät für jedes ideale Pixel 1,2 physische Pixel hat. Die Formel, um zwischen idealen (wie in der Webspezifikation definiert) und physischen Pixeln (Punkten auf dem Gerätebildschirm) zu ermitteln, lautet wie folgt:

physicalPixels = window.devicePixelRatio * idealPixels

Bisher haben Geräteanbieter devicePixelRatios (DPRs) in der Regel gerundet. Das iPhone und iPad von Apple berichten von der nordkoreanischen DPR 1 und die Retina-Äquivalente 2. In der CSS-Spezifikation wird empfohlen,

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

Ein Grund, warum runde Verhältnisse 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 oben ermittelt wurde. In Zukunft werden weitere Geräte mit variablen DPRs eingeführt. Daher sollten Sie niemals davon ausgehen, dass Ihre Kunden Ganzzahl-DPRs haben.

Übersicht über HiDPI-Bildtechniken

Es gibt viele Techniken, 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 und
  2. Optimierung der Auswahl zwischen mehreren Bildern

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 niedrigerem DPI HiDPI-Bilder herunterladen. Hier sind einige Ansätze für den Fall mit einem einzelnen Bild:

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

Mehrere Bilder: Verwenden Sie mehrere Bilder, wählen Sie jedoch clever aus, welche geladen werden sollen. 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 Clients möchten wir diese Zahl erhöhen. Wie viel größer wird es?

Ich habe einige Tests durchgeführt, bei denen 1x- und 2x-Bildfragmente in JPEG-Qualität bei 90, 50 und 20 generiert wurden. Hier ist das Shell-Script, das ich mit ImageMagick verwendet habe, um sie zu generieren:

Kacheln – Beispiel 1. Kacheln-Beispiel 2. Beispiel für Kacheln 3
Beispiele von Bildern mit unterschiedlichen Komprimierungen und Pixeldichten

Bei dieser kleinen, unwissenschaftlichen Stichprobennahme scheint es, dass die Komprimierung großer Bilder einen guten Kompromiss zwischen Qualität und Größe darstellt. Meiner Meinung nach sehen stark komprimierte Bilder mit doppelter Auflösung besser aus als unkomprimierte Bilder mit einfacher Auflösung.

Natürlich ist es schlechter, wenn Sie Geräten mit doppelter Auflösung Bilder mit doppelter Auflösung und geringer Qualität präsentieren, als wenn Sie Bilder mit höherer Qualität präsentieren. Der oben beschriebene Ansatz führt zu Qualitätseinbußen. Wenn Sie „Qualität: 90 Bilder“ mit „Qualität: 20 Bilder“ vergleichen, ist das Bild bei 90 Bildern weniger scharf und körniger. Diese Artefakte sind möglicherweise nicht akzeptabel, wenn Bilder hoher Qualität entscheidend sind (z. B. in einer Fotobetrachter-App) oder für App-Entwickler, die keine Kompromisse eingehen möchten.

Der obige Vergleich wurde ausschließlich mit komprimierten JPEGs durchgeführt. Es gibt viele Vor- und Nachteile zwischen den weit verbreiteten Bildformaten (JPEG, PNG, GIF). Das bringt uns zu...

Ein tolles Bildformat

WebP ist ein ziemlich interessantes Bildformat, das sehr gut komprimiert und gleichzeitig eine hohe Bildtreue beibehält. Natürlich ist sie noch nicht überall implementiert.

Eine Möglichkeit, die WebP-Unterstützung zu prüfen, ist JavaScript. Sie laden ein 1-Pixel-Bild über die Data-URI, warten, bis das Bild geladen wurde oder ein Fehlerereignis ausgelöst wurde, und prüfen dann, ob die Größe korrekt ist. Modernizr wird mit einem entsprechenden Skript zur Funktionserkennung ausgeliefert, das über 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 bieten den (etwas umstrittenen) Vorteil, dass das Bild in ihrer Position erscheint, bevor es vollständig geladen ist. Sie können zwar etwas mehr Speicherplatz belegen, aber es gibt widersprüchliche Beweise dafür. 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 lassen sich zwar leicht beenden, 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 Image ausreichend heruntergeladen wurde, muss der Browser eine neue Verbindung herstellen. Dies 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.

Ein offensichtlicher Nachteil dieses Ansatzes besteht darin, dass Sie nicht auswählen können, welches Bild geladen werden soll, sondern nur unterschiedliche Grenzen desselben Bildes. Daher wird der Anwendungsfall Art Direction nicht abgedeckt.

Mit JavaScript entscheiden, welches Bild geladen werden soll

Der erste und naheliegendste Ansatz bei der Entscheidung, welches Bild geladen werden soll, ist die Verwendung von JavaScript im Client. So kannst du alles über deinen User-Agent herausfinden und das Richtige tun. Sie können das Pixelverhältnis des Geräts über window.devicePixelRatio ermitteln, die Bildschirmbreite und -höhe abrufen und sogar über navigator.connection oder durch Auslösen einer gefälschten Anfrage ein wenig Netzwerkverbindungs-Sniffing durchführen, 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 etwa eine Million JavaScript-Bibliotheken, die in etwa die oben genannte Funktion umsetzen. Leider ist keine von ihnen besonders herausragend.

Ein großer Nachteil bei diesem Ansatz besteht darin, dass bei Verwendung von JavaScript das Laden von Bildern verzögert wird, bis der Look-Ahead-Parser beendet ist. Das bedeutet, dass Bilder erst nach dem Auslösen des Ereignisses pageload heruntergeladen werden. Mehr dazu in Jason Grigsbys Artikel.

Entscheiden, welches Image auf den Server geladen werden soll

Sie können die Entscheidung auf der Serverseite verschieben, indem Sie für jedes bereitgestellte Image benutzerdefinierte Anfrage-Handler schreiben. Ein solcher Handler würde anhand des User-Agents (der einzigen Information, die an den Server weitergeleitet wird) nach Retina-Unterstützung suchen. Je nachdem, ob die serverseitige Logik HiDPI-Assets bereitstellen möchte, laden Sie das entsprechende Asset, das nach einer bekannten Konvention benannt ist.

Leider liefert der User-Agent nicht unbedingt genügend Informationen, um zu entscheiden, ob ein Gerät Bilder von hoher oder niedriger Qualität empfangen soll. Selbstverständlich ist alles, was mit dem User-Agent zu tun hat, ein Hack und sollte möglichst vermieden werden.

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 die zugehörigen Mindest- und Höchstvarianten. 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); }
}

Durch die Kombination aller Anbieterpräfixe wird es etwas komplizierter, insbesondere wegen der abgefahrenen 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 JS-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). Das war beim serverseitigen Ansatz nicht möglich.

Leider ist es immer noch etwas unhandlich und führt zu einem seltsam aussehenden CSS-Code (oder erfordert eine Vorverarbeitung). Dieser Ansatz beschränkt sich außerdem auf CSS-Eigenschaften, sodass es keine Möglichkeit gibt, eine <img src> festzulegen. Außerdem müssen alle Bilder 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 bei einer EDGE-Verbindung ein riesiges Bild-Asset mit doppelter Größe herunterlädt. Das ist nicht besonders nutzerfreundlich.

Neue Browserfunktionen verwenden

In letzter Zeit wurde viel über die Unterstützung von Webplattformen für das Problem mit Bildern mit hoher Auflösung diskutiert. Apple ist kürzlich in den Bereich eingestiegen und hat die CSS-Funktion image-set() zu WebKit hinzugefügt. Daher wird es sowohl von Safari als auch von Chrome unterstützt. Da es sich bei image-set() um eine CSS-Funktion handelt, wird das Problem für <img>-Tags nicht behoben. Geben Sie @srcset ein. Damit wird das Problem behoben, aber zum Zeitpunkt der Veröffentlichung dieses Artikels hat es (noch) keine Referenzimplementierungen. Im nächsten Abschnitt werden image-set und srcset genauer erläutert.

Browserfunktionen für High DPI-Unterstützung

Welchen Ansatz Sie wählen, hängt letztendlich von Ihren spezifischen Anforderungen ab. Beachten Sie jedoch, dass alle oben genannten Ansätze Nachteile haben. Sobald image-set und srcset jedoch weithin unterstützt werden, sind sie die geeigneten Lösungen für dieses Problem. Lassen Sie uns vorerst über einige Best Practices sprechen, die uns dieser idealen Zukunft so nahe wie möglich bringen können.

Erstens: Wie 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 Attribut, das für <img>-Elemente spezifisch ist und eine ähnliche Syntax hat. 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 CSS-Funktion image-set() ist mit dem Präfix -webkit-image-set() verfügbar. Die Syntax ist ziemlich einfach: Sie setzen sich aus einer oder mehreren kommagetrennten Bilddeklarationen zusammen, die aus einem URL-String oder einer url()-Funktion gefolgt von der zugehörigen Auflösung bestehen. Beispiel:

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

Dem Browser wird damit mitgeteilt, dass zwei Bilder zur Auswahl stehen. Eines ist für 1x-Displays optimiert, das andere für 2x-Displays. 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 (derzeit nicht implementiert).

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.

Das funktioniert gut, außer in Browsern, die das Attribut image-set nicht unterstützen. In diesen Browsern wird kein Bild angezeigt. Das ist eindeutig schlecht. Sie müssen also ein Fallback (oder eine Reihe von Fallbacks) verwenden, um dieses Problem zu beheben:

background-image: url(icon1x.jpg);
background-image: -webkit-image-set(
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);
/* This will be useful if image-set gets into the platform, unprefixed.
    Also include other prefixed versions of this */
background-image: image-set(
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);

Dadurch wird das entsprechende Asset in Browsern geladen, die „image-set“ unterstützen, andernfalls wird auf das 1x-Asset zurückgegriffen. Der offensichtliche Nachteil ist, dass die Browserunterstützung für image-set() zwar gering ist, die meisten User-Agents aber das 1x-Asset erhalten.

In dieser Demo wird die image-set() verwendet, um das richtige Bild zu laden. Wenn diese CSS-Funktion nicht unterstützt wird, wird auf das 1x-Asset zurückgegriffen.

An dieser Stelle fragen Sie sich vielleicht, warum nicht einfach nur Polyfill (d. h. einen JavaScript-Shim für) image-set() erstellen und dies der Fall sein könnte. 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

Hier ein Beispiel für srcset:

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

Wie Sie sehen, nimmt das srcset-Element neben den x-Deklarationen, die image-set bereitstellt, auch die Werte „w“ und „h“ an, die der Größe des Darstellungsbereichs entsprechen. So wird versucht, die relevanteste Version zu senden. Mit dem obigen Beispiel wird „banner-phone.jpeg“ auf Geräten mit einer Ansichtsbreite von weniger als 640 Pixeln, „banner-phone-HD.jpeg“ auf Geräten mit kleinem Bildschirm und hoher Auflösung, „banner-HD.jpeg“ auf Geräten mit hoher Auflösung und einem Bildschirm mit mehr als 640 Pixeln und „banner.jpeg“ auf allen anderen Geräten ausgeliefert.

„Bildsatz“ für Bildelemente verwenden

Da das Attribut „srcset“ in den meisten Browsern nicht implementiert ist, kann es verlockend sein, Ihre img-Elemente durch <div>-Elemente mit Hintergründen zu ersetzen und den Ansatz „Image-Set“ zu verwenden. Das funktioniert, aber mit Einschränkungen. Der Nachteil ist hier, dass das <img>-Tag einen langfristigen semantischen Wert hat. In der Praxis ist dies vor allem aus Gründen der Barrierefreiheit und für Webcrawler wichtig.

Wenn Sie -webkit-image-set verwenden, sollten Sie die CSS-Eigenschaft „background“ nicht verwenden. Der Nachteil dieses Ansatzes besteht darin, dass Sie die Bildgröße angeben müssen, die bei einem anderen als 1:1-Bild unbekannt ist. Stattdessen können Sie die CSS-Eigenschaft „content“ so verwenden:

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

Dadurch wird das Bild automatisch anhand von „devicePixelRatio“ skaliert. In diesem Beispiel wird die oben beschriebene Methode mit einem zusätzlichen Fallback auf url() für Browser veranschaulicht, die image-set nicht unterstützen.

Polyfilling 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 außerdem möglich, Polyfills mit JavaScript zu erstellen.

Dieser Polyfill enthält Einheitentests, mit denen geprüft wird, ob er der Spezifikation möglichst nahe kommt. Außerdem gibt es Prüfungen, die verhindern, dass der Polyfill Code ausführt, wenn srcset nativ implementiert ist.

Hier ist eine Demo der polyfill in Aktion.

Fazit

Es gibt keine Zauberlösung für das Problem mit Bildern mit hoher Auflösung.

Die einfachste Lösung besteht darin, Bilder vollständig zu vermeiden und stattdessen SVG und CSS zu verwenden. Das ist jedoch nicht immer realistisch, insbesondere wenn Sie hochwertige Bilder auf Ihrer Website haben.

Ansätze in JS, CSS und auf der Serverseite haben ihre Stärken und Schwächen. Am vielversprechendsten ist jedoch die Nutzung neuer Browserfunktionen. Auch wenn die Browserunterstützung für image-set und srcset noch nicht vollständig ist, gibt es bereits vernünftige Fallback-Lösungen.

Hier eine Zusammenfassung meiner 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.