Responsive Images mit Symfony, Twig und Imagine

Responsive Images sind noch ein relativ neues Thema und werden eher selten eingesetzt: Zu Unrecht! Die Ladezeit von Webseiten lässt sich durch den Einsatz von Responsive Images erheblich reduzieren: Anstatt ein 2600 x 600 Hintergrundbild an alle Browser auszuliefern, werden unterschiedliche Varianten einer Grafik angeboten. Der Browser sucht sich dann aus, welche Version er herunterlädt und anzeigt.  Dadurch kann vor allem auf mobilen Geräten viel Bandbreite eingespart werden und die Ladezeit erheblich reduziert werden.Ich empfehlen jeden den sehr ausführlichen Artikel von A List Apart zu lesen, um die Hintergründe zu verstehen.  Der einzige Nachteil: Man muss seine Grafiken in den entsprechenden Auflösungen zur Verfügung stellen.

Mit dem LiipImagineBundle und einem kleinen Twig Filter ist das allerdings sehr schnell getan. Das untenstehende Beispiel unterstützt nur eine Aspect Ratio, kann aber leicht erweitert werden.

<picture>
    {{ 'images/example.jpg' | responsive_image_source([750, 380, 504, 252], {'filterset': 'responsive_article_image'}) }}
    <img srcset="{{ 'images/example.jpg' | imagine_filter('responsive_article_image', {"thumbnail": {"size": [100, 100] }}) }}" alt="My Article" class="img-responsive">
</picture>

und die entsprechende Konfiguration des FilterSets:

liip_imagine:
    filter_sets:
        responsive_article_image:
            quality: 75
            filters:
                thumbnail: { size: [640, 384], mode: inset }

Die Twig Extension:

<?php

namespace AppBundle\Twig;

use Liip\ImagineBundle\Imagine\Cache\CacheManager;

/**
 * Generates a source tag to use inside an responsive picture
 */
class ResponsiveImageExtension extends \Twig_Extension
{
    /**
     * @var CacheManager $cacheManager
     */
    private $cacheManager;

    /**
     * @param $cacheManager
     */
    public function __construct($cacheManager)
    {
        $this->cacheManager = $cacheManager;
    }


    public function getFilters()
    {
        return array(
            new \Twig_SimpleFilter('responsive_image_source', array($this, 'responsiveImageSourceFilter'), array('is_safe' => array('html'))),
        );
    }

    /**
     * @param string $imageUrl
     * @param array  $widths
     * @param array  $config
     *
     * @return string
     */
    public function responsiveImageSourceFilter($imageUrl, $widths = [], $config = [])
    {
        if (isset($config['media'])) {
            $mediaAttr = sprintf('media="%s"', $config['media']);
        } else {
            $mediaAttr = '';
        }
        $filterSet = isset($config['filterset']) ? $config['filterset'] : 'responsive_image';

        $srcSets = [];
        foreach ($widths as $width) {
            $resizedImageUrl = $this->getImagineUrl($imageUrl, $filterSet, $width);
            $srcSets[] = $resizedImageUrl . " " . $width . "w";
        }
        $srcSets = implode(", ", $srcSets);

        $tag = sprintf('<source srcset="%s" %s />', $srcSets, $mediaAttr);

        return $tag;
    }

    /**
     * @return CacheManager
     */
    public function getCacheManager()
    {
        return $this->cacheManager;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'responsive_image';
    }

    /**
     * @param string $imageUrl
     * @param string $filter
     * @param int    $width
     *
     * @return mixed
     */
    private function getImagineUrl($imageUrl, $filter, $width)
    {
        return $this->cacheManager->getBrowserPath($imageUrl, $filter, ['thumbnail' => ['size' => [0 => $width]]]);
    }
}

und deren Service Definition:

<service id="leaphub.twig.responsive_image_extension" class="AppBundle\Twig\ResponsiveImageExtension">
    <argument type="service" id="liip_imagine.cache.manager"/>
    <tag name="twig.extension"/>
</service>