CSS avanzado

1.10. Rollovers y sprites

Según varios estudios realizados por Yahoo!, hasta el 80% de la mejora en el rendimiento de la descarga de páginas web depende de la parte del cliente. En el artículo Performance Research, Part 1: What the 80/20 Rule Tells Us about Reducing HTTP Requests Yahoo! explica que generar dinámicamente el código HTML de la página y servirla ocupa el 20% del tiempo total de descarga de la página. El 80% del tiempo restante los navegadores descargan las imágenes, archivos JavaScript, hojas de estilos y cualquier otro tipo de archivo enlazado.

Además, en la mayoría de páginas web normales, la mayor parte de ese 80% del tiempo se dedica a la descarga de las imágenes. Por tanto, aunque los mayores esfuerzos siempre se centran en reducir el tiempo de generación dinámica de las páginas, se consigue más y con menos esfuerzo mejorando la descarga de las imágenes.

La idea para mejorar el rendimiento de una página que descarga por ejemplo 15 imágenes consiste en crear una única imagen grande que incluya las 15 imágenes individuales y utilizar las propiedades CSS de las imágenes de fondo para mostrar cada imagen. Esta técnica se presentó en el artículo CSS Sprites: Image Slicing’s Kiss of Death y desde entonces se conoce con el nombre de sprites CSS.

El siguiente ejemplo explica el uso de los sprites CSS en un sitio web que muestra la previsión meteorológica de varias localidades utilizando iconos:

Aspecto de la previsión meteorológica mostrada con iconos

Figura 1.8 Aspecto de la previsión meteorológica mostrada con iconos

La solución tradicional para crear la página anterior consiste en utilizar cuatro elementos <img> en el código HTML y disponer de cuatro imágenes correspondientes a los cuatro iconos:

<h3>Previsiones meteorológicas</h3>
<p id="localidad1"><img src="imagenes/sol.png" /> Localidad 1: soleado, máx: 35º, mín: 23º</p>
<p id="localidad2"><img src="imagenes/sol_nubes.png" /> Localidad 2: nublado, máx: 25º, mín: 13º</p>
<p id="localidad3"><img src="imagenes/nubes.png" /> Localidad 3: muy nublado, máx: 22º, mín: 10º</p>
<p id="localidad4"><img src="imagenes/tormentas.png" /> Localidad 4: tormentas, máx: 23º, mín: 11º</p>

Aunque es una solución sencilla y que funciona muy bien, se trata de una solución completamente ineficiente. El navegador debe descargar cuatro imágenes diferentes para mostrar la página, por lo que debe realizar cuatro peticiones al servidor.

Después del tamaño de los archivos descargados, el número de peticiones realizadas al servidor es el factor que más penaliza el rendimiento en la descarga de páginas web. Utilizando la técnica de los sprites CSS se va a rehacer el ejemplo anterior para conseguir el mismo efecto con una sola imagen y por tanto, una sola petición al servidor.

El primer paso consiste en crear una imagen grande que incluya las cuatro imágenes individuales. Como los iconos son cuadrados de tamaño 32px, se crea una imagen de 32px x 128px:

Creando un sprite de CSS a partir de varias imágenes individuales

Figura 1.9 Creando un sprite de CSS a partir de varias imágenes individuales

Para facilitar el uso de esta técnica, todas las imágenes individuales ocupan el mismo sitio dentro de la imagen grande. De esta forma, los cálculos necesarios para desplazar la imagen de fondo se simplifican al máximo.

El siguiente paso consiste en simplificar el código HTML:

<h3>Previsiones meteorológicas</h3>
<p id="localidad1">Localidad 1: soleado, máx: 35º, mín: 23º</p>
<p id="localidad2">Localidad 2: nublado, máx: 25º, mín: 13º</p>
<p id="localidad3">Localidad 3: muy nublado, máx: 22º, mín: 10º</p>
<p id="localidad4">Localidad 4: tormentas, máx: 23º, mín: 11º</p>

La clave de la técnica de los sprites CSS consiste en mostrar las imágenes mediante la propiedad background-image. Para mostrar cada vez una imagen diferente, se utiliza la propiedad background-position que desplaza la imagen de fondo teniendo en cuenta la posición de cada imagen individual dentro de la imagen grande:

#localidad1, #localidad2, #localidad3, #localidad4 {
  padding-left: 38px;
  height: 32px;
  line-height: 32px;
  background-image: url("imagenes/sprite.png");
  background-repeat: no-repeat;
}

#localidad1 {
  background-position: 0 0;
}
#localidad2 {
  background-position: 0 -32px;
}
#localidad3 {
  background-position: 0 -64px;
}
#localidad4 {
  background-position: 0 -96px;
}

La siguiente imagen muestra lo que sucede con el segundo párrafo:

Funcionamiento de la técnica de los sprites CSS

Figura 1.10 Funcionamiento de la técnica de los sprites CSS

El párrafo tiene establecida una altura de 32px, idéntica al tamaño de los iconos. Después de añadir un padding-left al párrafo, se añade la imagen de fondo mediante background-image. Para cambiar de una imagen a otra, sólo es necesario desplazar de forma ascendente o descendente la imagen grande.

Si se quiere mostrar la segunda imagen, se desplaza de forma ascendente la imagen grande. Para desplazarla en ese sentido, se utilizan valores negativos en el valor indicado en la propiedad background-position. Por último, como la imagen grande ha sido especialmente preparada, se sabe que el desplazamiento necesario son 32 píxel, por lo que la regla CSS de este segundo elemento resulta en:

#localidad2 {
  padding-left: 38px;
  height: 32px;
  line-height: 32px;
  background-image: url("imagenes/sprite.png");
  background-repeat: no-repeat;
  background-position: 0 -32px;
}

La solución original utilizaba cuatro imágenes y realizaba cuatro peticiones al servidor. La solución basada en sprites CSS sólo realiza una conexión para descargar una sola imagen. Además, los iconos del proyecto Tango que se han utilizado en este ejemplo ocupan 7.441 bytes cuando se suman los tamaños de los cuatro iconos por separado. Por su parte, la imagen grande que contiene los cuatro iconos sólo ocupa 2.238 bytes.

Los sprites que incluyen todas sus imágenes verticalmente son los más fáciles de manejar. Si en el ejemplo anterior se emplea un sprite con las imágenes dispuestas también horizontalmente:

Sprite complejo que dispone las imágenes de forma vertical y horizontal

Figura 1.11 Sprite complejo que dispone las imágenes de forma vertical y horizontal

Aparentemente, utilizar este nuevo sprite sólo implica que la imagen de fondo se debe desplazar también horizontalmente:

#localidad1, #localidad2, #localidad3, #localidad4 {
  padding-left: 38px;
  height: 32px;
  line-height: 32px;
  background-image: url("imagenes/sprite.png");
  background-repeat: no-repeat;
}

#localidad1 {
  background-position: 0 0;
}
#localidad2 {
  background-position: -32px 0;
}
#localidad3 {
  background-position: 0 -32px;
}
#localidad4 {
  background-position: -32px -32px;
}

El problema del sprite anterior es que cuando una imagen tiene a su derecha o a su izquierda otras imágenes, estas también se ven:

Errores producidos por utilizar un sprite complejo

Figura 1.12 Errores producidos por utilizar un sprite complejo

La solución de este problema es sencilla, aunque requiere algún cambio en el código HTML:

<h3>Previsiones meteorológicas</h3>
<p id="localidad1"><img src="imagenes/pixel.gif" /> Localidad 1: soleado, máx: 35º, mín: 23º</p>
<p id="localidad2"><img src="imagenes/pixel.gif" /> Localidad 2: nublado, máx: 25º, mín: 13º</p>
<p id="localidad3"><img src="imagenes/pixel.gif" /> Localidad 3: muy nublado, máx: 22º, mín: 10º</p>
<p id="localidad4"><img src="imagenes/pixel.gif" /> Localidad 4: tormentas, máx: 23º, mín: 11º</p>

El código anterior muestra uno de los trucos habituales para manejar sprites complejos. En primer lugar se añade una imagen transparente de 1px x 1px a todos los elementos mediante una etiqueta <img>. A continuación, desde CSS se establece una imagen de fondo a cada elemento <img> y se limita su tamaño para que no deje ver las imágenes que se encuentran cerca:

#localidad1 img, #localidad2 img, #localidad3 img, #localidad4 img {
  height: 32px;
  width: 32px;
  background-image: url("imagenes/sprite2.png");
  background-repeat: no-repeat;
  vertical-align: middle;
}

#localidad1 img {
  background-position: 0 0;
}
#localidad2 img {
  background-position: -32px 0;
}
#localidad3 img {
  background-position: 0 -32px;
}
#localidad4 img {
  background-position: -32px -32px;
}

Utilizar sprites CSS es una de las técnicas más eficaces para mejorar los tiempos de descarga de las páginas web complejas. La siguiente imagen muestra un sprite complejo que incluye 241 iconos del proyecto Tango y sólo ocupa 42 KB:

Sprite complejo que incluye 210 iconos en una sola imagen

Figura 1.13 Sprite complejo que incluye 210 iconos en una sola imagen

La mayoría de sitios web profesionales utilizan sprites para mostrar sus imágenes de adorno. La siguiente imagen muestra el sprite del sitio web YouTube:

Sprite utilizado por el sitio web de YouTube

Figura 1.14 Sprite utilizado por el sitio web de YouTube

Los principales inconvenientes de los sprites CSS son la poca flexibilidad que ofrece (añadir o modificar una imagen individual no es inmediato) y el esfuerzo necesario para crear el sprite.

Afortunadamente, existen aplicaciones web como CSS Sprite Generator que generan el sprite a partir de un archivo comprimido en formato ZIP con todas las imágenes individuales.