El problema de las imágenes flotadas que acompañan al texto

Uso de Developer Tools de Chrome para ver medidas de un elemento HTML

El peso de las imágenes es un factor crítico en la optimización de la velocidad de carga. Lo óptimo sería no utilizar imágenes, pero esto no es una situación realista. Un texto podría describir mejor lo que pretende exponer si acompañamos alguna imagen cercana al texto, si es posible incluso flotándola en el texto. La pregunta clave es cuál debe ser el ancho y el alto de la imagen para obtener la mejor presentación en la mayor parte de dispositivos.

Supongamos que voy a usar una imagen fotográfica de 2560×1536 píxeles. Es un JPG de 462452 bytes. Estos son demasiados bytes para la web. Hay que recordar la limitación que impone la latencia web. Hemos de lograr que las imágenes pesen 14000 bytes o menos. Por otro lado tenemos la limitación del ancho del dispositivo, el llamado viewport. Si queremos que esa imagen se muestre completa en un dispositivo pequeño de 320 píxes de ancho no debería ser más ancha. Esto está bien porque así la imagen tendría un archivo de menor tamaño. Llevemos esto a cabo con un ejemplo.

Para el siguiente ejemplo hay un contenedor con borde verde donde voy a ubicar la imagen flotada en el texto. Consultamos con una herramienta de desarrollo de navegadores cuál es el ancho disponible si ajustamos el viewport (la ventana del navegador) a 320 píxeles. Resulta que disponemos de 275 píxeles, tal como se observa en la captura de pantalla de la imagen que encabeza este tema. Reduciremos entonces nuestra imagen original a unos conservadores 270 píxeles, de tal forma que manteniendo la proporción original tendremos una imagen reducida de 270×162 píxeles con un tamaño de archivo de 12921 bytes. Esto se consigue con alguna herramienta editora de imágenes y probando varias calidades. En este caso he usado una calidad de 0.7 que permite reducir el tamaño del archivo manteniendo una calidad aceptable.

Ejemplo: Flotando imágenes con el texto

Planta de parchita o maracuyá con flores

Lorem ipsum ad his scripta blandit partiendo, eum fastidii accumsan euripidis in, eum liber hendrerit an. Qui ut wisi vocibus suscipiantur, quo dicit ridens inciderint id. Quo mundi lobortis reformidans eu, legimus senserit definiebas an eos. Eu sit tincidunt incorrupte definitionem, vis mutat affert percipit cu, eirmod consectetuer signiferumque eu per. In usu latine equidem dolores. Quo no falli viris intellegam, ut fugit veritus placerat per. Blandit incorrupte quaerendum in quo, nibh impedit id vis, vel no nullam semper audiam. Ei populo graeci consulatu mei, has ea stet modus phaedrum. Inani oblique ne has, duo et veritus detraxit. Tota ludus oratio ea mel, offendit persequeris ei vim.

La imagen aparece flotada a la izquierda con el texto rodeándola por la derecha. Al menos para anchos de ventana superiores a 32em, que con fuente de 16px son 512px. Esto lo configuro con el CSS siguiente usando una consulta de medios:

<style>
    /* Esto está en el archivo base.css */
    *[class^=flota-izda] {
        float: left;
        margin: 0 0.3em 0.3em 0;
    }
    @media screen and (max-width: 32em) {
        #contenido img[class^=flota-] {
            margin-top: 0.6em;
            float: none; 
            display: block; 
        }
        ...
    }
</style>

................

<section><div class="ejemplo-linea"><h4>Ejemplo...</h4>
    <img src="ejemplos/parchita-reducida-270x162-07.jpg" 
    class="flota-izda"
    width="270" height="162"
    alt="Planta de parchita o maracuyá con flores" />
    <p>Lorem ipsum...
    </p>
    <div class="borra-flotaciones"></div>
</div></section>

En esa consulta de medios estableciendo max-width: 32em hacemos que para anchos de 512 píxeles o menos se borre la flotación. Pues en dispositivos estrechos quedaría poco espacio para que el texto envuelva la imagen por la derecha. La imagen pasará de inline (su valor por defecto) a block, forzando a presentar el texto en una nueva línea.

Esa es la solución que vengo utilizando en este sitio. Es aceptable en aquellos casos donde la reducción del tamaño de la imagen no afecta a lo que queramos presentar en ella. O bien que con esos píxeles de ancho tenemos suficiente para la presentación. Pero si no podemos reducir tanto entonces tenemos que buscar otra solución.

Supongamos que tenemos que presentar la imagen en un tamaño no menor de 512×307 píxeles con un archivo de 40848 bytes. Aunque no en este caso, en otros podría suceder que una reducción mayor hiciera inapreciable ciertos detalles de la imagen.

Ejemplo: Tratando con imágenes más anchas

Planta de parchita o maracuyá con flores

Lorem ipsum ad his scripta blandit partiendo, eum fastidii accumsan euripidis in, eum liber hendrerit an. Qui ut wisi vocibus suscipiantur, quo dicit ridens inciderint id. Quo mundi lobortis reformidans eu, legimus senserit definiebas an eos. Eu sit tincidunt incorrupte definitionem, vis mutat affert percipit cu, eirmod consectetuer signiferumque eu per. In usu latine equidem dolores. Quo no falli viris intellegam, ut fugit veritus placerat per. Blandit incorrupte quaerendum in quo, nibh impedit id vis, vel no nullam semper audiam. Ei populo graeci consulatu mei, has ea stet modus phaedrum. Inani oblique ne has, duo et veritus detraxit. Tota ludus oratio ea mel, offendit persequeris ei vim.

Ahora tenemos que olvidar las flotaciones pues el ancho de la imagen es mayor que el de muchos dispositivos móviles en orientación portrait. Para albergar imágenes grandes las incluyo en un <div class="conten-width"> que limita su ancho al del contenedor padre:

<style>
    /* Esto está en el archivo base.css */
    .conten-width {
        width: 100%;
        overflow: auto;
        text-align: center;
    }
</style>

................

<section><div class="ejemplo-linea"><h4>Ejemplo...</h4>
    <div class="conten-width"><img 
    src="ejemplos/parchita-reducida-512x307-07.jpg"
    width="512" height="307"
    alt="Planta de parchita o maracuyá con flores" /></div>
    <p>Lorem ipsum...
    </p>
</div></section>

Para anchos de dispositivos inferiores al de la imagen ésta no superará el ancho del viewport ocultando por lo derecha el exceso que sobresale y mostrando la barra de scroll horizontal. Para cualquier ancho de dispositivo la imagen se centra en pantalla y el texto se presenta en una nueva línea.

Selección basada en viewport para imágenes flotadas

Los ejemplos anteriores usan consultas de medios para restructurar los elementos en torno a la imagen. Ésta no cambia de tamaño y, por supuesto, no se ajusta a las diferentes resoluciones (DPR) de los dispositivos. Los nuevos atributos srcset y sizes del elemento <img> nos permiten unificar el resultado de los dos ejemplos del apartado anterior más la adaptación de DPR.

Ejemplo: Usando sizes con consultas de medios

Planta de parchita o maracuyá con flores

Lorem ipsum ad his scripta blandit partiendo, eum fastidii accumsan euripidis in, eum liber hendrerit an. Qui ut wisi vocibus suscipiantur, quo dicit ridens inciderint id. Quo mundi lobortis reformidans eu, legimus senserit definiebas an eos. Eu sit tincidunt incorrupte definitionem, vis mutat affert percipit cu, eirmod consectetuer signiferumque eu per. In usu latine equidem dolores. Quo no falli viris intellegam, ut fugit veritus placerat per. Blandit incorrupte quaerendum in quo, nibh impedit id vis, vel no nullam semper audiam. Ei populo graeci consulatu mei, has ea stet modus phaedrum. Inani oblique ne has, duo et veritus detraxit. Tota ludus oratio ea mel, offendit persequeris ei vim.

Este ejemplo de soporte con los atributos srcset y sizes se ejecuta correctamente en Chrome 41 y Opera 28. Si redimensionamos la ventana se volverá a ejecutar la consulta de medios declarada en sizes. Firefox 36 también ejecuta la consulta al recargar la página, pero no cuando redimensionamos la ventana. IE11 no soporta estos nuevos atributos.

A continuación puede ver el HTML y CSS necesario para este ejemplo, estando el CSS ubicado en la cabecera de esta página:

<style>
    /* Ejemplo con una imagen que flota a izquierda con pantallas entre 
       540px y 800px. Para menores de 540 y mayores 800 se centra. */
    /* Este id es para un DIV que contiene todo el ejemplo */
    #ejemplo2 {
        /* Esto es para los que no soporten srcset con objeto de que la 
           imagen no supere el ancho del viewport, pues usará src que 
           tiene un único ejemplar de 400x240 */
        overflow: hidden;
    }
    /* Esta clase es para un DIV en cuyo interior está el IMG */
    .flotaresp {
        display: block;
        text-align: center;
    }
    @media screen and (min-width: 541px) and (max-width: 800px) {
        .flotaresp {
            display: inline-block;
            float: left;
            margin: 0 0.2em 0.2em 0;
        }
        .flotaresp + p {
            margin-top: 0;
        }
    }
</style>

...............

<section><div class="ejemplo-linea" id="ejemplo2"><h4>Ejemplo...</h4>
    <div class="flotaresp"><img src="ejemplos/parchita-400x240.jpg"
        alt="Planta de parchita o maracuyá con flores"
        sizes="(max-width: 540px) 82vw, (max-width: 800px) 50vw, 
              calc(690px)"
        srcset="ejemplos/parchita-400x240.jpg 400w,
                ejemplos/parchita-500x300.jpg 500w,
                ejemplos/parchita-600x360.jpg 600w,
                ejemplos/parchita-700x420.jpg 700w,
                ejemplos/parchita-800x480.jpg 800w,
                ejemplos/parchita-900x540.jpg 900w,
                ejemplos/parchita-1000x600.jpg 1000w" 
    /></div><p>Lorem ipsum...
    </p>
    <div class="borra-flotaciones"></div>
</div></section>

Hay un contenedor <div class="flotaresp"> en cuyo interior ubicamos el elemento <img>. El contendor DIV se configura con display block (su valor por defecto) y centrando su contenido para anchos de viewport menores de 541 píxeles y mayores de 800 píxeles. En el caso de que esté dentro de ese rango, pasamos el contenedor DIV a inline-block y lo flotamos a la izquierda. Con este CSS y la consulta de medios del atributo sizes pretendemos alcanzar los siguientes objetivos:

  1. Para dispositivos estrechos (ancho ≤ 540px) la imagen no será flotada y ocupará una gran parte del ancho disponible, especificando 82vw en el atributo sizes. Esto equivale a decir que el ancho de la imagen será un 82% del ancho del viewport. Vea que 320×82% = 262px asegurándonos que la imagen no superará los 270px para dispositivos de 320px, alojándose con holgura en su contenedor. No podemos flotar porque el espacio libre para el texto sería insuficiente si deseamos mantener un tamaño mínimo de imagen en torno a los 270px.
  2. Para dispositivos intermedios (540px < ancho ≤ 800px) flotamos la imagen a la izquierda. Son tamaños adecuados para hacer este efecto con imágenes de tamaño itermedio. Le damos un 50vw con lo que aseguramos la mitad de ancho para la imagen flotada y la otra mitad para el texto a su derecha.
  3. Para dispositivos grandes (ancho > 800px) usaremos imágenes grandes sin flotar. No podemos seguir incrementando el tamaño de la imagen flotada porque para dispositivos muy anchos requerirá de una imagen excesivamente grande. Tampoco conviene mantener una imagen pequeña flotada y unas líneas muy largas de texto a la derecha. Así que congelamos el ancho en sizes al valor calc(690px) con una imagen sin flotar. Este valor, que permanecerá invariable, proviene de otro mecanismo que tengo en este sitio y que limita el ancho disponible (max-width) de todo el contenido de la página en 700px. Su razón es mantener líneas de texto en torno a 80 caracteres (ver Ancho máximo para una página web).

Usando el emulador de dispositivos que viene con el Developer Tools de Chrome capturamos algunas pantallas:

SIZES con consulta de medios
Uso del emulador de móviles del Developer Tools de Chrome para practicar con srcset sizesImagen no disponible
Para un ancho de ventana igual o inferior a 540 píxeles la imagen aparece centrada en una línea y el texto se separa a continuación.

El atributo sizes permite especificar consultas de medios y además el atributo srcset nos ajustará el DPR tal como vimos en el primer tema acerca de la selección basada en DPR pero ahora usando el descriptor nw en lugar de nx. Las siguientes capturas de pantalla muestran varios DPR para un mismo ancho de viewport:

SIZES con consulta de medios
Uso del emulador de móviles del Developer Tools de Chrome para practicar con srcset sizesImagen no disponible
Con un viewport de 320 y 1 DPR, el navegador selecciona la imagen más pequeña de 400×240 píxeles.

Consideración con el atributo sizes

En el ejemplo hemos cubierto el rango de ejemplares de la imagen desde 400 a 1000 píxeles, en tramos de 100 píxeles. Estos son sus tamaños de archivo:

ArchivoTamaño KB
parchita-400×240.jpg18.4
parchita-500×300.jpg28.0
parchita-600×360.jpg38.6
parchita-700×420.jpg51.2
parchita-800×480.jpg64.9
parchita-900×540.jpg69.8
parchita-1000×600.jpg77.3

Las imágenes se obtuvieron cambiándoles el tamaño al JPG original de 2560×1536 píxeles. Se redujeron con una calidad JPG de 0.6 para evitar un tamaño excesivo, incluso las dos últimas tiene calidad algo inferior. Este ejemplo puede resultar eficaz pero conlleva un trabajo de preparación de imágenes algo engorroso. Quizás es suficiente con generarlas en tramos de 200 píxeles, pero a diferencia de los ejemplos en páginas anteriores, ahora el navegador debe seleccionar la imagen teniendo en cuenta el DPR y las medidas que resultan de la consulta de medios del atributo sizes. Por lo tanto para obtener el mayor beneficio es mejor ofrecer un mayor número de ejemplares.

Aunque al elemento <img> hemos de ponerle el atributo src para aquellos navegadores que no soporten srcset, no es posible especificar los atributos width y height porque el navegador ignoraría los tamaños que se declaran en sizes. La extensión PageSpeed nos expone esto como un punto de mejora. El asunto es que el navegador podría dejar un cuadro en blanco con las dimensiones de la imagen y seguir con el renderizado del resto del documento. Cuando la imagen se descargue entonces no tendrá que rehacer el layout para toda la página sino solo repintar lo que falte.

Si no aportamos los atributos width y height podríamos considerar si el navegador puede obtenerlas a través del atributo sizes. Supongamos que en el ejemplo anterior la consulta de medios da un resultado de 82vw. Como el navegador sabe cuál es el ancho del viewport, determinará que la imagen, sea cual sea, debe ser pintada con un 82% de esa medida. Por lo tanto puede reservar un espacio de ese ancho, ¿pero qué altura le reservará?.

Para probar esto he construido dos páginas de ejemplos. Un ejemplo con srcset y otro ejemplo sólo con src. Les he pasado el Timeline del Developer Tools de Chrome para ver el comportamiento de los procesos Layout y Paint.

He tomado unas capturas de pantalla de estos Timeline, imágenes de gran tamaño por si desea observarlas en una ventana aparte:

En la siguiente serie de imágenes se explica como se comporta cada ejemplo:

Layout+Paint con src y srcset
Recursos de un ejemplo con IMG SRCImagen no disponible
Ejemplo con src especificando width y height. Se observa un único proceso de Layout y dos Paint.

En la primera captura vemos la lista de procesos que realiza el navegador para cargar la página del ejemplo con sólo src especificando los atributos width y height para la imagen. Finaliza la carga del documento src.html y lo parsea, tras lo cual hace el Layout o esquema para ubicar los elementos. Luego realiza el primer pintado incluso antes de recibir el archivo de la imagen parchita-400x240.png.

Si seleccionamos el primer Paint veremos cuanto lleva pintado hasta el momento, lo que se expone en la segunda captura. Se observa que el navegador dejó un recuadro con las dimensiones width y height especificadas en el HTML del elemento img. Cuando ya se recibe el archivo de imagen procederá a pintar la imagen (tercera captura) pero no necesitará rehacer el layout.

Sin embargo para el segundo ejemplo con srcset donde no se declaran atributos width y height, observaremos dos procesos de Layout (cuarta captura). En el primer layout/pintado el navegador no deja ningún hueco para la imagen. Cuando la descargue se verá obligado a rehacer el Layout.

Por ahora no sé hasta que punto el uso de srcset sin especificar medidas de la imagen puede perjudicar la velocidad de carga de una página. Dos Layouts pueden parecer insignificantes en ese ejemplo. Pero en una página más compleja podría resultar en una carga apreciable. Habrá que seguir probando todo esto.