DEV Community

Lito
Lito

Posted on

Redimensionado de imágenes según la resolución del cliente

Una vez tenemos claro como utilizar una herramienta como Packer para el redimensionado de imágenes en tiempo de ejecución:

Vamos a dar un paso más aplicando la redimensión según la resolución del dispositivo del cliente.

Esta mejora permitirá enviar fotos posiblemente mucho más reducidas en tamaño para optimizar sobre todo la navegación móvil en, como mínimo, cuatro puntos:

  • el cliente usa menos ancho de banda con lo cual la descarga será más rápida y la web se visualizará antes
  • el uso de CPU del dispositivo será menor al tener que renderizar imágenes más ligeras
  • evitamos guardar en la caché de disco del cliente imágenes a alta resolución
  • el feeling en el scroll mejorará cuanto más reducidas sean las imágenes

Lo primero es añadir un trozo de código javascript en el HTML:

(function() {
    function setCookie(value) {
        document.cookie = value
            + '; path=/'
            + '; max-age=' + (3600 * 24 * 30 * 12)
            + '; SameSite=Strict'
            + '; secure';
    }

    function setResolution() {
        var width = document.documentElement.clientWidth || window.innerWidth,
            height = document.documentElement.clientHeight || window.innerHeight,
            dpr = window.devicePixelRatio || 1;

        setCookie('resolution=' + width + ',' + height + ',' + dpr);
    }

    setResolution();

    window.addEventListener('resize', function() {
        setResolution();
    });
})();

Con esto ya tenemos una cookie que registra la resolución del dispositivo conectado.

Ahora vamos por la parte de backend.

Primero creamos un servicio que nos permita realizar las transformaciones desde las plantillas de manera sencilla:

<?php declare(strict_types=1);

namespace App\Services\Html;

class Html
{
    /**
     * @param string $image
     * @param string $options = ''
     *
     * @return string
     */
    public static function img(string $image, string $options = ''): string
    {
        return Image::transform($image, $options);
    }
}

Y lo añadimos como alias en app/config.php:

    'aliases' => [
        ...,
        'Html' => App\Services\Html\Html::class,
    ],

Ahora preparamos el servicio de procesado de imágenes:

<?php declare(strict_types=1);

namespace App\Services\Html;

use Packer;

class Image
{
    /**
     * @param string $image
     * @param string $options
     *
     * @return string
     */
    public static function transform(string $image, string $options): string
    {
        if ($options = static::options($options)) {
            return (string)Packer::img($image, $options);
        }

        return $image;
    }

    /**
     * @param string $options
     *
     * @return string
     */
    protected static function options(string $options): string
    {
        if (empty($resolution = $_COOKIE['resolution'])) {
            return $options;
        }

        list($widthMax, $heightMax, $dpr) = array_pad(array_map('intval', explode(',', $resolution, 3)), 3, 1);

        if (empty($widthMax) || empty($heightMax)) {
            return $options;
        }

        $options = $options ?: 'resize,0,0,center,middle';

        $widthMax *= ($dpr ?: 1);
        $heightMax *= ($dpr ?: 1);

        list($action, $width, $height, $horizontal, $vertical) = array_pad(explode(',', $options, 5), 5, null);

        if ($width > $widthMax) {
            $height = intval(($widthMax * $height) / $width);
            $width = $widthMax;
        } elseif ($height > $heightMax) {
            $width = intval(($heightMax * $width) / $height);
            $height = $heightMax;
        }

        return $action.','.$width
            .($height ? (','.$height) : '')
            .($horizontal ? (','.$horizontal) : '')
            .($vertical ? (','.$vertical) : '');
    }
}

Ahora sólo queda usarlo en las plantillas y comparar las imágenes generadas según el dispositivo con el que nos conectemos:

<img src="{{ Html::img('/storage/images/background.jpg') }}" />
<img src="{{ Html::img('/storage/images/slider.jpg', 'resize,1920') }}" />

Eso es todo amigos!

Top comments (0)