Der Zwiebellook: Bessere responsive Bilder mit mehreren Hintergründen

Avatar of Parker Bennett
Parker Bennett am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Der folgende Text ist ein Gastbeitrag von Parker Bennett. Parker hat bereits früher für CSS-Tricks geschrieben, in seinem Artikel Crop Top, der sich mit der Positionierung von flüssigen Bildern befasste. Dies ist eine großartige Fortsetzung dazu und präsentiert eine weitere Option im endlosen Sagen-Zyklus von responsiven Bildern. Es ist auch ein interessanter Kontrast zum gestrigen Beitrag über responsive Bilder – so kann man sehen, wie sehr sich die Ansätze zu diesem Problem unterscheiden können.

Wir wollen schnelle Seitenladezeiten. Wir wollen Retina-Bilder. Können wir alles haben?

Ein "lowsrc"-Fake – hochauflösende Farbmodifikationen, sobald Mobile-First geladen ist.

Wir wollen keine riesigen Bilder an Nutzer ausliefern, die sie nicht benötigen. Es gibt verschiedene Ansätze, dies zu vermeiden – darunter Scott Jehls picturefill, serverseitige Lösungen und Lazy-Loading-Techniken – aber die einfachste Methode ist vielleicht die Verwendung eines background-image und der Magie von CSS-Media-Queries. So erhalten Ihre glücklichen Retina-Nutzer die hochauflösenden @2x-Versionen, und der Rest von uns… nun, zumindest muss ich nicht auf den Download von Mammutdateien warten.

(Um dies unten zu veranschaulichen, habe ich mein Standardbild für "Mobile First" schwarzweiß, die mittelgroße Version sepia gehalten, und es gibt eine größere Farbversion, wenn Sie das Fenster breiter ziehen oder einen Bildschirm mit hoher DPI haben.)

Kleine schwarz-weiße Standardversion, wechselt zu Farbe, wenn breiter. Bevor Bilder gecached sind, gibt es ein kurzes Aufblitzen, während sie laden.
/* base background-image class */
.bg-image {
  width: 100%;
  /* default background-position */
  background-position: center center;
  /* lt ie 8 */
  -ms-background-position-x: center;
  -ms-background-position-y: center;
  /* scale proportionately */
  background-size: cover;
  /* IE8 workaround - http://louisremi.github.io/background-size-polyfill/ */
  -ms-behavior: url(/backgroundsize.min.htc); }

/* mobile-first default (b&w) */
.bg-image-sedona {
  background-image: url(img/photo-sedona_512x320.jpg);
  background-position: center 21%; }

/* example media queries (IE8 needs this:
   http://code.google.com/p/css3-mediaqueries-js) */
@media
  /* "mama-bear" - plus any-retina */
  only screen and min-width : 513px,
  only screen and (-webkit-min-device-pixel-ratio: 1.5),
  only screen and (        min-device-pixel-ratio: 1.5) {

    /* mid-size (sepia) */
    .bg-image-sedona {
      background-image: url(img/photo-sedona_1024x640.jpg); }
  }

@media
  /* "papa-bear" - plus larger retina */
  only screen and (min-width : 1025px),
  only screen and (min-device-width : 768px) and (-webkit-min-device-pixel-ratio: 1.5),
  only screen and (min-device-width : 768px) and (        min-device-pixel-ratio: 1.5) {

    /* high-res (color) */
    .bg-image-sedona {
      background-image: url(img/[email protected]); }
  }

Das div, das das background-image anzeigt, benötigt eine Höhe, die manuell gesetzt werden kann, oder, wie ich es hier getan habe, indem man ein transparentes "Proxy"-img einwickelt, das responsiv skaliert wird (mehr dazu hier).

Nun, wie Sie vielleicht bemerkt haben, kann es beim ersten Rendern eines großen Bildes auf einer Seite zu einer spürbaren Verzögerung kommen, während es lädt. Selbst kleinere Bilder können, bevor sie gecached sind, ein lästiges Aufblitzen anzeigen, während sie geladen oder ausgetauscht werden. Aber das können wir beheben…

CSS3 Mehrfach-Hintergründe: Wie sie sich stapeln

Auf CodePen bearbeiten

Neuere Browser erlauben uns, Hintergrundbilder zu stapeln, indem wir mehrere durch Kommas getrennte Werte deklarieren. So können wir das ursprüngliche, gecachte Bild anzeigen, während das Ersatzbild flüssig darüber geladen wird (beachten Sie die Stapelreihenfolge im Code unten).

Einzelner Hintergrund oben, mehrere Hintergründe unten.

Um dies in Aktion zu sehen, verkleinern Sie das Browserfenster und leeren Sie den Cache (wählen Sie "Browserdaten löschen" im Chrome-Menü oder "Caches leeren" im Safari-Entwicklermenü). Laden Sie die Seite nun neu. Scrollen Sie wieder nach unten und vergrößern Sie das Fenster, bis die Farbbilder darüber geladen werden. (Oder versuchen Sie dieses Pop-up-Fenster.)

Leider verstehen ältere Browser wie IE8* keine Mehrfach-Hintergrunddeklarationen und werfen die Hände hoch – sie zeigen nichts an (autsch!). Wir müssen also modernizr.js verwenden, um Feature-Detecting zu betreiben und eine Fallback-Lösung zu schaffen (wenn wir möchten, dass diese Browser etwas Größeres als die Mobile-First-Standardversion angezeigt bekommen).

/* .bg-image and .bg-image-sedona same as above.
   .multiplebgs class added by modernizer.js. */

@media
  /* "mama-bear" - plus any-retina */
  only screen and min-width : 513px,
  only screen and (-webkit-min-device-pixel-ratio: 1.5),
  only screen and (        min-device-pixel-ratio: 1.5) {

    /* no-multiplebgs - mid-size fallback (sepia) */
    .no-multiplebgs .bg-image-sedona,
    /* upscale to mid-size if no javascript */
    .no-js .bg-image-sedona {
      background-image: url(img/photo-sedona_1024x640.jpg); }

    .multiplebgs .bg-image-sedona {
      background-image:
        /* mid-size on top (sepia) */
        url(img/photo-sedona_1024x640.jpg),
        /* mobile-first default on bottom (b&w) */
        url(img/photo-sedona_512x320.jpg);
      }
  }

@media
  /* "papa-bear" - all three images */
  only screen and (min-width : 1025px) {

    /* no-multiplebgs fallback is above */

    .multiplebgs .bg-image-sedona {
      background-image:
        /* high-res on top (color) */
        url(img/[email protected]),
        /* mid-size in middle (sepia) */
        url(img/photo-sedona_1024x640.jpg),
        /* mobile-first default on bottom (b&w) */
        url(img/photo-sedona_512x320.jpg);
      }
  }

@media
  /* larger retina device - triggered immediately,
     so mid-size image not needed */

  only screen and (min-device-width : 768px) and
    (-webkit-min-device-pixel-ratio: 1.5),
  only screen and (min-device-width : 768px) and
    (        min-device-pixel-ratio: 1.5) {

    /* no-multiplebgs fallback is above */

    .multiplebgs .bg-image-sedona {
      background-image:
        /* high-res on top (color) */
        url(img/[email protected]),
        /* mobile-first default on bottom (b&w) */
        url(img/photo-sedona_512x640.jpg);
    }
  }

Standard vs. Progressive JPEGs

Bei JPEGs hängt die Art und Weise, wie ein Bild über einem anderen Bild in einem Mehrfachhintergrund gerendert wird, davon ab, wie es gespeichert wurde. Ein Standard-JPEG "malt" das Bild sequenziell, während es heruntergeladen wird. Progressive JPEGs "erscheinen" vollständig heruntergeladen. (Der Standardweg erscheint mir flüssiger.) Beachten Sie, dass Bildkompressoren wie ImageOptim standardmäßig auf progressiv (Jpegrescan ist aktiviert) eingestellt sind, da dies etwas Speicherplatz spart.

Natürlich wollen wir nicht, dass Nutzer unnötigerweise Bilder herunterladen oder unseren Wartungsaufwand übermäßig komplizieren. Daher ist es wichtig, dass wir unsere Breakpoints zurückhaltend gestalten und logisch durchdenken. Aber da wir das Austauschen von Bildern weniger auffällig machen können, eröffnen sich einige Möglichkeiten…

"lowsrc" vortäuschen

Auf CodePen bearbeiten

Früher, als das Internet noch mit Dampf betrieben wurde, war der Einwahlzugang so langsam, dass ein spezielles Attribut geschaffen wurde, damit die Nutzer *etwas* sahen, während es anderthalb Minuten dauerte, ihre animierten GIFs herunterzuladen: es hieß "lowsrc" und sah so aus: IMG SRC="big.gif" LOWSRC="small.gif".

Browser stellten die Unterstützung dafür in den späten 50er Jahren ein.

Aber etwas Ähnliches könnte jetzt nützlich sein, damit Nutzer *etwas* sehen, während es zweieinhalb Sekunden dauert, ihre hochauflösenden Retina-Bilder herunterzuladen. (Und vergessen Sie nicht, 4K kommt.)

Moderne Browser sind ziemlich schlau darin, Bilder sofort anzuzeigen, sobald sie abgerufen wurden. Indem wir kleinere, stärker komprimierte "lowsrc"-Bilder als Standard angeben und diese dann unter den @2x Retina-Bildern in unseren CSS-Media-Queries stapeln, werden die Dinge wahrscheinlich reaktionsschneller wirken. Wir können noch einen Schritt weiter gehen und jQuery verwenden…

Die Idee ist, das Austauschen von Bildern hinauszuzögern, bis die Seite vollständig mit unseren Standard-"lowsrc"-Bildern gerendert ist. Dann verwenden wir jQuery, um der Hauptklasse "bg-image" eine "hd"-Klasse hinzuzufügen, was unsere Media-Queries zum Austauschen der Bilder auslöst. Wir könnten auch hinauszögern und die hochauflösenden Bilder "lazy loaden", während wir zu ihnen scrollen, mit etwas wie dem jQuery Waypoints Plug-in.

/* .bg-image and .bg-image-sedona same as above
   .hd class added by jQuery after page loads
   (or perhaps "lazy loaded" as user scrolls) */

@media
  /* "mama-bear" - plus any-retina */
  only screen and (min-width : 513px),
  only screen and (-webkit-min-device-pixel-ratio: 1.5),
  only screen and (        min-device-pixel-ratio: 1.5) {

    /* no-multiplebgs - mid-size fallback */
    .no-multiplebgs .bg-image-sedona.hd,
    .no-js .bg-image-sedona {
      /* mid-size (sepia) */
      background-image: url(img/photo-sedona_1024x640.jpg); }

    .multiplebgs .bg-image-sedona.hd {
      background-image:
        /* mid-size on top (sepia) */
        url(img/photo-sedona_1024x640.jpg),
        /* mobile-first "lowsrc" on bottom (b&w) */
        url(img/photo-sedona_512x320.jpg); }
  }

@media
  /* "papa-bear" - size only */
  only screen and (min-width : 1025px) {

    /* no-multiplebgs fallback is above */

    .multiplebgs .bg-image-sedona.hd {
      background-image:
        /* high-res on top (color) */
        url(img/[email protected]),
        /* mid-size in middle (sepia) */
        url(img/photo-sedona_1024x640.jpg),
        /* mobile-first "lowsrc" on bottom (b&w) */
        url(img/photo-sedona_512x320.jpg); }
  }

@media
  /* larger retina device, triggered immediately,
     so mid-size image is not needed */
  only screen and (min-device-width : 768px) and
    (-webkit-min-device-pixel-ratio: 1.5),
  only screen and (min-device-width : 768px) and
    (        min-device-pixel-ratio: 1.5) {

    /* no-multiplebgs fallback is above */

    .multiplebgs .bg-image-sedona.hd {
      background-image:
        /* high-res on top (color) */
        url(img/[email protected]),
        /* mobile-first "lowsrc" on bottom (b&w) */
        url(img/photo-sedona_512x320.jpg); }
  }
/* waits until everything is loaded, not just DOM is ready */
$(window).load(function() {

  $('.bg-image').addClass('hd');

});

Sehen Sie diese "Faking lowsrc"-Demo in Aktion

Siehe ein Beispiel mit "Lazy Loading" in Aktion.

/* "lazy loads" when .bg-image appears in viewport -
   http://imakewebthings.com/jquery-waypoints/ */

$('.bg-image').waypoint(function(direction) {
  if (direction === 'down') {
    $(this).addClass('hd');
  }
}, { offset: 'bottom-in-view', triggerOnce: true });

/* other offsets: '100%' (image top at viewport bottom),
   '125%' (just beyond the viewport, about to scroll in) */

Zusammenfassend

Idealerweise würde ich mir wünschen, dass dies auf eine automatisiertere Weise funktioniert, wie picturefill.js, aber basierend auf einem Mobile-First img statt einem data-src-Attribut. Was denken Sie? Sie können sich den Quellcode für weitere Informationen ansehen, alle Demos auf CodePen ansehen oder die Beispieldateien hier herunterladen. Wenn Sie Fragen, Kommentare oder Korrekturen haben, schreiben Sie mir: parker@parkerbennett.com.


* IE8 unterstützt keine Mehrfach-Hintergründe, aber wenn Sie eine Breite und Höhe für Ihr Bild deklarieren können, könnten Sie etwas Ähnliches mit diesem Pseudo-Element-Ansatz von Nicolas Gallagher umsetzen.