Responsive Images mit pimcore

Lukas [php, Pimcore, Tricks und Tools]

Seit etwas über einem Jahr ist Responsive Webdesign Standard. Die Webseite passt sich der Bildschirmgrösse an und kann somit auf dem Desktop, einem Tablet und auch auf dem Smartphone optimal gelesen werden. Das bedeutet auch, dass die Bilder in unterschiedlichen Grössen verwendet werden müssen. Das geht ohne grossen Aufwand, wenn man die Möglichkeiten von JavaScript und pimcore kombiniert einsetzt. Der Vorteil davon: Eine möglichst optimale Ladezeit dank optimierten Bildern.

Bisher haben wir responsive Bilder z.B. für flächendekende Hintergrundbilder eingesetzt. Dazu gab es jeweils um die 3 Varianten des Bildes und je nach aktueller Bildschirmbreite wurde das passende dazu eingesetzt. Das benötigte für jeden Einsatz etwas Aufwand.

Ersetze Bilder durch Responsive Images

Bisher haben wir Bilder in pimcore mit der $this->image()->getThumbnail() Methode eingesetzt. So wurde jeweils je nach Seitenkonfiguration ein passendes Thumbnail des eingesetzten Bilder generiert. Das passt nun leider nicht immer. Je nach Bildschirmgrösse kann das zwischen 200 und 1200 Pixeln liegen. Deshalb ersetzen wir jeden Bildeinsatz mit einer Methode, die uns automatisch den grundlegenden HTML Code generiert:

public static function getImageLoaderTag(Document_Tag_Image $image) {
    //sammle alle nötigen Variabeln
    $asset = $image->getImage();
    $width = $asset->getWidth();
    $height = $asset->getHeight();

    $id = $asset->getId();
    $filename = $asset->getFilename();
    $parts = explode(".", $filename);
    $extension = array_pop($parts);
    $title = join(".", $parts);
    if ($asset->hasProperty("title")) {
        $title = $asset->getProperty("title");
    }

    //erstellen des html codes
    $doc = new DOMDocument();
    $imageLoader = $doc->createElement("div");
    $noscript = $doc->createElement("noscript");
    $img = $doc->createElement("img");

    //das imageloader element bekommt alle wichtigen variabeln als data- attribute
    $imageLoader->setAttribute("class", "imageloader");
    $imageLoader->setAttribute("data-width", $width);
    $imageLoader->setAttribute("data-height", $height);
    $imageLoader->setAttribute("data-assetid", $id);
    $imageLoader->setAttribute("data-extension", $extension);
    $imageLoader->setAttribute("data-title", $title);
    $imageLoader->setAttribute("style", "background-image:url(/plugins/nambu/static/img/loader.gif);background-repeat:no-repeat;background-position:center");

    //erstelle ein img-tag mit einem standardbild, welcher in ein noscript-tag gepackt wird.
    $img->setAttribute("src", '/website/var/tmp/thumb_'.$id.'__imageloader-1200.'.$extension.'');
    $img->setAttribute("style", "width:100%");
    $img->setAttribute("alt", $title);
    $img->setAttribute("title", $title);
    $noscript->appendChild($img);

    $imageLoader->appendChild($noscript);

    $doc->appendChild($imageLoader);

    echo $doc->saveHTML();
}

So sieht ein Beispielcode aus, welcher hier generiert wird:

<div style="background-image: url('/plugins/nambu/static/img/loader.gif'); background-repeat: no-repeat; background-position: center center;" data-title="home" data-extension="jpg" data-assetid="26" data-height="1146" data-width="1667" class="imageloader">
  <noscript>
    <img src="/website/var/tmp/thumb_26__imageloader-1200.jpg" style="width: 100%;">
  </noscript>
</div>

Sollte JavaScript deaktiviert sein, wird das grösst mögliche Bild angezeigt. Das ist zwar nicht optimiert, aber immerhin kommt was erwartet wird. Sobald JavaScript aktiviert ist, wird zuerst mal ein div mit dem Loader als Hintergrundbild angezeigt. Damit noch was passiert ist noch ein wenig Code nötig.

Der JavaScript Part der Responsive Images

Das Script setzt jQuery voraus. Es sucht sich alle Elemente mit der imageloader Klasse. Als erstes holt es sich die aktuelle Breite des divs und berechnet anhand des Seitenverhältnisses des Originalbildes die Höhe (Haben wir alles per data-attribut mitgegeben). Das sort dafür, dass der tatsächlich benötigte Platz für das Bild bereits reserviert wird, ohne dass das Bild selbst schon vorhanden ist. Dies verhindert lästiges flackern, bedingt durch neu erscheinende Bilder die plötzlich höher sind als der Platzhalter. Anhand der Breite wird die korrekte Bild-URL generiert und zusammen mit einem img-Tag ins vorhandene div eingesetzt. Sobald geladen wird die Höhe zurückgesetzt. Dies damit sich die Höhe beim Verändern des Browserfensters im bleibenden Seitenverhältnis anpasst.

$(document).ready(function() {
    $(".imageloader").each(function() {
        var imageloader = $(this);
        var width = imageloader.width();
        var assetid = imageloader.data("assetid");
        var extension = imageloader.data("extension");
        var datawidth = imageloader.data("width");
        var dataheight = imageloader.data("height");
       
        var thumbname = getThumbnailname(width);
        var dynamicHeight = (width/datawidth)*dataheight;
       
        imageloader.css("height", dynamicHeight);
       
        var img = $('<img />');
        img.attr("src", "/website/var/tmp/thumb_" + assetid + "__" + thumbname + "." + extension);
        img.css("width", "100%");
        img.load(function() {
            imageloader.css("height", "");
        });
        imageloader.html(img);
    });
});

function getThumbnailname(width) {
    var width = parseInt(width);
    var sizes = [ 100, 200, 300, 400, 500, 600, 700, 800, 1000, 1100, 1200 ];
   
    var usedWidth = sizes[sizes.length-1];

    $.each(sizes, function(index, value) {
        if (width < value) {
            usedWidth = value;
            return false;
        }
    });
    return "imageloader-" + usedWidth;
}

Automatisches generieren der Responsive Images

Jetzt kommt noch ein wenig pimcore zum Einsatz. Seit der Version 1.4.10 generiert es beliebige Bilder anhand einer vorgegebenen URL. Das hat zur Folge, dass die Bilder nicht im Voraus für alle möglichen Grössen generiert werden müssen. Diese werden sobald benötigt generiert und abgespeichert. Wir brauchen uns um das also nicht zu kümmern. Dies geschieht anhand einer vordefinierten URL nach dem Schema:
/website/var/tmp/thumb_assetid__Thumbnailname.jpg

Damit es nun auch die verschiedenen Thumbnail-Konfigurationen gibt, installiert das nambu Plugin bei der Installation die nötigen Thumbnails automatisch mit:

$imageLoaderWidths = array(100,200,300,400,500,600,700,800,1000,1100,1200);

foreach ($imageLoaderWidths as $width) {
    $name = "imageloader-".$width;

    $config = new Asset_Image_Thumbnail_Config();
    $config->setName($name);
    $config->setItems(array(
            array("method" => "scaleByWidth",
                    "arguments" => array("width" => $width))
    ));
    $config->save();
}

zurück