Dynamisches Zuschneiden von Bildern in WordPress

Mich haben schon seit langem die relativ statischen Bildgrößen in WordPress gestört. Zwar kann man schon seit Version 2.9.0 über die Funktion add_image_size eigene Größen definieren, leider ist aber auch dies recht statisch, da die Größen ja vordefiniert werden müssen. Mit WordPress 4.4 kamen Responsive Images in den Core, das Problem der vordefinierten Größen bleibt jedoch bestehen. Die ganze Denkweise hinter diesem Vorgehen ist auf Geräte(größen) – also adaptive Design/Development – ausgerichtet, anstatt sich geräteagnostisch nur für den Viewport zu interessieren (responsive Design). Ich will mich aber gar nicht darum kümmern mit welchem Gerät ein User meine Seite besucht, sondern mich interessiert welche Inhalte ich in welcher Größe darstellen kann.

Aus diesem Grund habe ich schon seit Langem mit Hilfe des Resizing Skripts Aqua Resizer Bilder aus der WordPress Mediathek dynamisch zugeschnitten: Über window.devicePixelRatio kann man die Pixeldichte des Displays auslesen und somit die exakte Pixelgröße eines Bildes bestimmen. Diese wird dann per AJAX an das php Skript geschickt welches entweder die schon vorhandene Datei vom Server zurückliefert oder das Bild eben zuschneidet und dies dann an den Browser ausliefert. Da man das Verhalten des Browsers wann dieser Bilder lädt nicht bzw. nicht zuverlässig beeinflussen kann, sollte man das src-Attribut des img-Tags mit einem winzigen Platzhalter – klassischerweise ein 1x1px transparentes gif – ersetzen und die eigentliche Bildquelle in ein data-*-Attribut auslagern, bspw. data-src. Für den Fall, dass ein Besucher kein JavaScript ausführen möchte bietet es sich an, das Original-Bild noch mit einem noscript-Tag zu umgeben. Der img-Tag sieht dann also in etwa so aus:

<img src="spacer.gif" data-src="https://example.com/wp-content/uploads/my-image.jpg" class="my-image-class" />
<noscript>
    <img src="https://example.com/wp-content/uploads/my-image.jpg" />
</noscript>

Soweit die Lazy-Load Grundlagen… im vorliegenden Fall geht es aber nicht um auf ein scroll-Event lauschendes lazy-loading – wobei sich das natürlich anbietet – zumal die Vorarbeiten schon gemacht sind. Sondern wir wollen dem Benutzer das Bild in exakt der benötigten Größe ausliefern. Mit folgendem jQuery Code werden die Bilder ersetzt (Hinweis: Um die Lesbarkeit zu erhöhen und das Beispiel so knapp und präzise wie möglich zu halten, verzichte ich auf Sicherheitschecks wie nonce oder AJAX Referer):

jQuery(document).ready(function($) {
    $('img').each(function() {
        // get the image ratio
        var ratio = new Image();
        ratio.src = $(this).data('src');
        
        var targetEl = $(this),
            ajaxData = {
                action: 'resize_my_image', 
                width: $(this).width(), 
                height: $(this).width() * (ratio.height / ratio.width),
                source: $(this).data('src')
            };

        $.getJSON(ajaxurl, ajaxData, function(response) {
            targetEl.attr('src', response.src);
        });
    });
});

und das dazugehörige php-Skript sieht in etwa so aus:

<?php

// include aq resizer script
require_once __DIR__ . '/path/to/aq_resizer.php';

// register the ajax handler
add_action('wp_ajax_resize_my_image', 'resizeImage');
add_action('wp_ajax_nopriv_resize_my_image', 'resizeImage');

function resizeImage()
{
    // explicit declaration of variables
    $width = (int) $_GET['width'];
    $height = (int) $_GET['height'];
    $url = esc_url($_GET['source']);
    
    // call the resize script
    $img = aq_resize($url, $width, $height);
    
    // echo the response
    echo json_encode(['src' => $img]);
    wp_die();
}

Leider ist der AQ Resizer auch schon etwas in die Jahre gekommen. Daher habe ich meine Methode noch weiterentwickelt und werde sie hoffentlich bald hier vorstellen. Schreibt mich bei Fragen oder Verbesserungen zum Code einfach auf twitter an oder forked meine Gists.