Continuamos la serie sobre optimización web empezada en el artículo "Optimización web (I)" .
En este capítulo trataremos una técnica que viene siendo usada desde mucho antes de la aparición de la web dentro del mundo de la programación, en concreto dentro del mundo de los videojuegos, y en algunas ocasiones en la creación de interfaces gráficas. Los que ya sepáis algo sobre eso y hayáis leído el título, como buenos lectores que sois, ya debéis saber a qué me refiero, para los que el título no les diga mucho os daré una breve explicación.
La técnica de trabajar con sprites está relacionada con la presentación de imágenes por pantalla, por lo general pequeñas. Usualmente, tanto en webs como en videojuegos, acabamos teniendo conjuntos enormes de imágenes. Cada una de estas imágenes acostumbra a tener un fichero asociado (siempre y cuando no estemos usando ya la técnica de los sprites) y eso tiene bastantes implicaciones (negativas) en el rendimiento de nuestra página o videojuego.
En el caso de los videojuegos las desventajas son varias:
- Por cada imagen se tiene que hacer una lectura de disco, además, el hecho de que estén en ficheros separados puede implicar fácilmente que las imágenes estén alejadas unas de otras (físicamente) sobre el disco. No es que se trate de fragmentación, pero a efectos prácticos conlleva casi los mismos problemas de lentitud.
- Normalmente, siempre y cuando el programador o grafista no tenga problemas neuronales o de visión, las paletas de colores de cada imagen serán muy parecidas entre ellas (habrá paleta si y solo si usamos un esquema de colores indexados, lo que se hace para reducir el tamaño final de la imágen, aunque implica que podamos usar menos colores que usando directamente el esquema RGB). El hecho de que no se compartan esas paletas implica que se carga información redundante en memoria por cada imágen que leemos desde el disco (lo que implica un mayor consumo de memoria).
En el caso de las páginas web, los problemas pueden llegar a ser incluso peores:
- A parte de que el servidor tiene que hacer bastantes lecturas de disco por cada página web cargada (sí, ya sé que muchos los cargará en alguna caché en memoria y todo eso, pero si el sitio web es mínimamente grande los contenidos de la cache irán rotando, y si el sitio está alojado en un hosting compartido la cosa será más complicada aun) también tendrá que hacer más envíos al navegador del visitante para que éste pueda visualizar la página web por completo. Y ahí es donde está el gran problema, los tiempos de lectura de disco son casi despreciables si los comparamos con los tiempos de envío a través de la red, pues no se envía solo la imágen, sino que además también se envían sus respectivas cabeceras HTTP, lo que incrementa la cantidad de información a enviar.
- Como antes, el navegador consumirá más memoria para mantener esas imágenes, esto ahora mismo no supone un gran problema porque los ordenadores personales cada vez tienen más memoria principal, pero también es importante decir que los usuarios se están acostumbrando a abrir cada vez más pestañas en sus navegadores... y aquí el consumo de memoria se dispara y su importancia (aunque sigue siendo poca) crece algo más.
- Si se tienen elementos mínimamente dinámicos, como botones pulsables que cambian su imagen de fondo cuando se pasa el ratón por encima o cuando se hace click sobre ellos puede aparecer un problema estético muy importante, resulta que dado que la página no debía cargar la imagen en el primer momento de renderizado... casi en todos los navegadores se da el hecho de que ésta no se descarga hasta el momento de ser necesitada. Esto implica que en el momento de pasar el ratón por encima del susodicho botoncito nos encontraremos con que la imágen no cambia inmediatamente (la primera vez que lo hagamos), habrá un retraso perceptible bastante antiestético y que puede llegar incluso a confundir al visitante, pues puede tener la sensación de que el elemento está bloqueado o algo por el estilo.
El primer punto junto con el tercero son los que más motivan la técnica de los CSS Sprites, que paso a comentar seguidamente.
La técnica de los CSS Sprites consiste en combinar un conjunto de imágenes en una sola, formando una especie de mosaico, para luego mostrar solo las partes que nos interesen de ella en ciertos elementos de la web. Cargar una sola imagen nos ayudará a reducir la cantidad de peticiones HTTP, eliminará el problema del retraso comentado anteriormente, y en muchas ocasiones también nos servirá para reducir el tamaño total de las imágenes (porque, en el caso de tener una paleta de colores indexada es probable que compartan una gran parte de ésta). Tengo que decir que, aunque en la mayoría de casos podamos conseguir el beneficio extra de reducir el tamaño total de las imagenes, esto no siempre es así y a veces puede pasar incluso lo contrario, aun así los beneficios de reducir la cantidad de peticiones HTTP compensarían esta penalización.
Si nos fijamos, la mayoría de páginas web ultrarápidas que nos puedan pasar por la cabeza usan ésta técnica, el ejemplo paradigmático de página web ligera es la del buscador de Google. Os muestro una de las imágenes que usan para aplicar la técnica:
Y ahora viene la parte interesante, he hablado mucho pero todavía no he explicado como hacer esto. ¿Como se hace pues? Pues no hay una fórmula exacta, pero intentaré sentar las bases para que cada uno pueda aplicar las variaciones que considere pertinentes.
Para facilitar las cosas empezaremos con elementos de tamaño fijo, supongamos que tenemos un elemento div con tamaño 32x32, y tenemos en una imágen un set de iconos de 32x32 .. no sé, unas ocho filas por ocho columnas de iconos, unos 64. Supongamos también que queremos que el fondo de dicho elemento div sea el icono situado en la fila 6 y columna 5. Lo que escribiremos en la hoja de estilos CSS es:
1 | background:url(/ruta/a/la/imagen.png) no-repeat -160px -192px } |
Analizemos este trozo de código. la primera parte background nos indica que vamos a definir los atributos del fondo del elemento. url(/ruta/a/la/imagen.png) nos indica la imagen donde buscaremos nuestro fondo. -160px es la cantidad de pixels que nos tendremos que desplazar horizontalmente sobre la imagen que contiene los sprites para encontrar el fondo que queremos (5x32, 5 columnas), -192px ídem, pero para las filas. El elemento no-repeat nos indica que no queremos que la imagen se repita a modo de mosaico.
Aquí es importante destacar que debemos definir el tamaño del elemento al que queremos poner fondo para que no aparezcan a continuación de la imagen que queríamos mostrar todas sus "compañeras de viaje". Otro punto interesante es que el elemento no-repeat podría ser cambiado por repeat-x o por repeat-y en algunos casos, como cuando queremos dibujar fondos periódicos o dibujar los contornos de alguna caja con tamaño variable (dependiendo del contenido). En este caso la distribución de las imágenes dentro de la imagen contenedora es muy importante, pues si hacemos un repeat-x y tenemos otras imágenes a la izquierda o la derecha de la que queremos repetir, el efecto que queríamos conseguir será estropeado. Ídem con el caso de repeat-y.
Esta técnica puede ser usada para crear menus, para personalizar las "bolitas" de las listas, para dibujar los bordes y el fondo de cajas con tamaño variable, para mejorar el efecto de elementos pulsables, etc.
Un saludo, y hasta la próxima entrega.
