Entendiendo los Sprites

Siempre cuesta entender un poco el funcionamiento de los sprites cuando los vemos por primera vez, así que vamos a intentar definirlos lo más simplemente posible:

“Una imagen en Sprite es un conjunto de imágenes agrupadas en una sola imagen”

Sin sprite lo que hacemos es usar un archivo por cada imagen que queremos mostrar y si tenemos, por ejemplo, 3 iconos serían 3 archivos diferentes:
Sprites

  • 3 Peticiones al servidor, una por cada archivo
  • Cada petición tiene un tiempo de espera aparte del tiempo de descarga
  • Tenemos que crear 3 imágenes

Con sprites lo que intentamos es unir todas esas imágenes en una sola:

  • Una sola petición al servidor
  • Hemos creado UNA SOLA imagen
  • Podemos estructurar mejor nuestro CSS

Pero… De verdad optimizan el tiempo de descarga?

Veamos.

Tenemos una web con 3 imágenes, y los test arrojan los siguientes resultados:
Requests: 4 (3 imágenes y el .HTML)
Peso total de la página: 6.2kB
Tiempo de descarga: 162ms
Tiempo usado por tipo de contenido:

  • Imagenes 73.77%
  • HTML 26.23%

Ahora las mismas pruebas con una sola imagen (Sprite):

Requests: 2 (1 imagen y el .HTML)
Peso total de la página: 5.3kB -.9kB de diferencia-
Tiempo de descarga: 157ms -5ms más rápido-
Tiempo usado por tipo de contenido:

  • Imagenes 50.34%
  • HTML 49.66%

Aunque puede parecer que al diferencia es muy pequeña, no olvidemos que hacemos test con una y 3 imágenes y un HTML sin más HTTP requests que las hechas por las imágenes. Imaginaros esto en una web completa, con todas sus imágenes y calculad cuanto podemos ahorrar a un móvil en cargar el contenido si optimizamos con sprites.

Crear un sprite no es complicado si trabajamos con el todopoderoso Photoshop, tenemos que aprender a utilizar las guías y darle a cad sprite las medidas que queremos. Si somos diseñadores sabemos que las proporciones se mantendrán, si no sois diseñadores, ya sabéis que hay que mantenerlas ;)

Esto quiere decir que tendremos 2 o 3 tamaños de imágenes cuando mucho y un solo sprite si nos organizamos bien.

Existe un plugin para Photoshop llamado GuideGuide que nos facilita la creación de patrones de guías. Pero nosotros empezaremos con un sprite bastante sencillo de 3 secciones de 50px x 50px cada una para utilizarlo como iconos un menú de 3 items de navegación:

Nuestro objetivo es que el elemento muestre solo una parte de ese fondo, la parte correspondiente al item en la navegación. Que el menú PacMan tenga la imagen correspondiente, el menú Fantasma la imagen correspondiente y lo mismo para Robot.

Con background-position podemos especificar el lugar de esa imagen:


li {
	background: url(sprite.png) #f6e2b9 no-repeat 0 0; //en el origen
	padding: 1em 0 1em 46px;
	display: block;

}

Y en este caso tendremos todas las listas li los primeros 50px verticales y los primeros 50px horizontales de nuestra imagen sprite.png. Si nos fijamos, he dejado 50px de padding izquierdo para que el texto no se monte sobre la imagen del sprite – y he usado el box-sizing:border-box

CSS “mide” las imágenes de arriba-izquierda a abajo-derecha, lo que quiere decir que la esquina superior izquierda es la posición cero y a partir de ahí podemos modificar su posición en un sistema de coordenadas xy. En nuestro sprite si queremos mostrar la segunda imagen, si tenemos que miden 50px cada una, debemos “subir” un poco el fondo: exactamente 50px. Pero si “subimos” la imagen para que muestre las coordenadas x0 y50px debemos usar un valor negativo, o de lo contrario la imagen baja: 50px es 50px a partir del borde superior del contenedos, -50px es 50px ANTES del borde superior del contenedor.

Si queremos asignar diferentes coordenadas para la imagen solo tenemos que cambiar el posicionamiento del background. La manera más fácil en mi opinión para realizar esto, es asignando clases:


li {
	padding: 1em 0 1em 46px;
	display: block;

}

.pacman {
	background: url(sprite.png) #f6e2b9 no-repeat 0 0; //coordenadas 0(x) 0(y) la imagen 
}

.fantasma {
	background: url(sprite.png) #f6e2b9 no-repeat 0 -50px; //coordenadas 0(x) -50px(y) la imagen 
}

.robot {
	background: url(sprite.png) #f6e2b9 no-repeat 0 -100px; //coordenadas 0(x) -100px(y) la imagen 
}



Y en nuestro HTML asignamos esa clase al elemento donde queremos que se muestre la imagen:



<ul>
		<li class="pacman">PacMan</li>
		<li class="fantasma">Fantasma</li>
		<li class="robot">Robot</li>
</ul>



Aquí podéis ver el código funcionando y si os interesa jugar con la posición en codepen, podéis ver el código.

Eso es básicamente un Sprite, pero podemos ir más lejos: Podemos usar sprites para crear efectos hover, e imaginad lo fácil que debe ser crear en Photoshop un sprite con su versión hover en vez de tener que crear, en nuestro caso, 6 imágenes independientes.

Es doblar el tamaño del canvas – solo el ancho, ya que el largo será el mismo – y duplicar las imágenes. Ya solo queda agregarles los efectos que queramos.

Como vemos nuestra rejilla se amplía, ahora para acceder a la versión “hover” tenemos que mover el fondo también horizontalmente: Si queremos obtener el pacman, las coordenadas son 0 0, si queremos el :hover del pacman, las coordenadas son -50px 0. Para el fantasma normal las coordenadas son 0 -50px y para el hover son -50px -50px y finalmente para el robot, las coordendas son 0 -100px y -50px -100px para el :hover status.

Ahora crearemos las mismas clases que antes pero con una versión :hover y las coordenadas dichas.


li {

	padding: 1em 0 1em 46px;
	display: block;
	background-color: #f6e2b9;
	-webkit-border-radius: 2em;
	border-radius: 2em;
	width: 100%;
	max-width: 50px;
	margin: 0 1em;
	height: 50px;
	width: 50px;
	-webkit-box-shadow: 1px 1px 1px 1px rgba(0, 0, 0, .3);
	box-shadow: 1px 1px 1px 1px rgba(0, 0, 0, .3);
	cursor: hand;
	cursor: pointer;

}

li:hover {
	-webkit-box-shadow: 0 0 0 0 rgba(0, 0, 0, .0);
	box-shadow: 0 0 0 0 rgba(0, 0, 0, .0);

}
.pacman {
	background: url(sprite-hover.png) no-repeat 0 0; 
	
}

.pacman:hover {
	background: url(sprite-hover.png) no-repeat -50px 0; 

}

.fantasma {
	background: url(sprite-hover.png) no-repeat 0 -50px; 

}

.fantasma:hover {
	background: url(sprite-hover.png) no-repeat -50px -50px;

}

.robot {
	background: url(sprite-hover.png) no-repeat 0 -100px;

}
.robot:hover {
	background: url(sprite-hover.png) no-repeat -50px -100px; 

}

y el HTML: (en este caso, obvio el texto, son unos simples botones)



<ul>
		<li class="pacman"></li>
		<li class="fantasma"></li>
		<li class="robot"></li>
	</ul>

De nuevo, si queréis jugar con el código en Codepen: http://codepen.io/Wakkos/full/DpoAJ

La idea de todo esto es entender el funcionamiento de los Sprites y sobretodo porque es importante implementarlos, pero si sois muy vagos y pasáis de todo este rollo de calcular las coordenadas, crear grids y todo eso, podéis ir a generadores de sprites y que ellos trabajen mientras os tomáis un café:

SpritePad: En mi opinión es uno de los más fáciles que he visto. Antes de que pierdas tiempo investigando como se usa te doy el tip que les falta: arrastra la imagen a la ventana.

SpriteBox: Sin duda el más completo.

Espero que ahora entendamos un poco mejor los Sprites!

UPDATE:

Quique (CkGrafico) nos ha creado un script para ver como manejar sprites grandes con jQuery sin tener que calcular todos los posicionamientos: http://codepen.io/Wakkos/pen/rFlzg. Dadle las gracias y seguidle en Twitter!

Daniel Martínez

31 comentarios - ¿Quieres opinar?
  1. Articulazo ^^

    ¡Es difícil que después de leer ésto queden dudas sobre Sprites!

  2. yeah, muy provechosa y completa la info sobre los sprites

  3. Yeah!!
    me habría encantado haber tenido un tutorial así hace unos años cuando me partía la cabeza buscando como evitar el retraso en la carga del “hover”.
    se lo recomendare a todo mundo. d(^_^o)

  4. Excelente aportazo!, gracias

  5. Excelente muy claro y utili

  6. Excelente articulo!

  7. Recomiendo SpriteCow también para hacer los sprites.

    Muy buen artículo, Daniel.

  8. Excelente, Gracias wakkos y CkGrafico

  9. ¿Y qué tal los sprites con responsive? ¿Cómo se portan?

    • Se portan Perfectamente! son un fondo y el control es total =)

      • Si pero me he encontrado con una dificultad, utilizo un sprite para un menú vertical como el de tu ejemplo, y al ser responsive, luego el menú es horizontal con lo que los botones crecen y se me ve parte de la imagen del siguiente :D
        No se si me explico, ¿te ha pasado alguna vez?

  10. Excelente tutorial… es algo muy vital entender los sprites

  11. Excelente tutorial, me encantó la aplicación SpriteBox, cuando haga un juego html5 sin duda la voy a usar!

  12. Buen artículo.
    Sólo un pequeño apunte:
    para ayudar a optimizar y mejorar del código se puede declarar una sola vez el background para los li y cambiar sólo el b-position de cada li.clase:


    li {background: #f6e2b9 url(sprite.png) no-repeat;}
    .pacman {background-position: 0 0;}

    Un saludo, Mr. Sombrerero

    • Sr Kseso! que alegría verle por acá!

      Si, eso ya lo había discutido por Twitter, pero opté por dejarlo así porque las dudas que me plantea la gente indican que no lo ven tan claro, así que por razones educativas, el código no es el más optimizado.

      Un Abrazo!

  13. Muy buen articulo de Sprite, intentare aprender

  14. hola!

    me ha llamado la atención ver que no hay nada en tu blog sobre Fireworks. Es la herramienta indicada para el diseño web (para pantallas), supongo que lo conoces, pero dejo aquí este link para ti y para tus lectores. Quizás alguno descubra algo muy bueno como me pasó a mí. :)

    50 motivos para no usar Photoshop para Diseño Web
    http://www.reinegger.net/50_motivos_para_no_usar_photoshop_para_diseno_web.html

    Felicidades por el blog y sigue escribiendo!
    Un saludo

    • Si ves charlas mías, sabrás que estoy en contra de usar Photoshop, o fireworks o cualquier editor visual para “crear” una web =D

  15. Gracias! ahora comprendo perfectamente cómo funciona, ahora podré hacer mis propios sprites *-*

  16. excelente articulo!

  17. Genial !!!
    Lo he leido y lo he puesto en practica. ( llevaba una semana en pendientes…)

    Quería comentar o por lo menos dejar constancia, que tuve problemas a la hora de que me cogiera las coordenadas.

    No me funcionaba, le di mil vueltas esta noche, probé con todas las coordenadas.
    Hasta que en un momento de iluminación, me dio por probar con porcentajes en vez de pixels.
    Y funciono, he utilizado un plugin para hacer sprites en gimp, no se si ha sido ese el motivo.
    Me pregunto si a ti esto te ha ocurrido alguna vez, o hay otras personas que te hayan comentado el uso de porcentajes.

    ¿Alguno de los que venimos asiduamente por aquí de visita, saben algo al respecto ?

    Por cierto, ¿ no hechaís en falta en Css3 un background-font, para poner estilo en la fuente ( un patrón, una imagen..) ?

    Gracias. Un abrazo !!!

  18. excelente explicación … simplemente espectacular .. gracias por el aporte

  19. Hola Daniel,
    Gracias por el tutorial, es de gran utilidad por lo que lo he guardado para estudiar y poder aplicar aunque tengo un problema: he copiado los tres archivos (html, css y js) a tres archivos nuevos en sublimeText y en el index los he enlazado al css y al js. Pero no me reconoce el java! Qué etiqueta hay que incluir en el index.html para que actúe el Jquery de CkGrafico? Gracias por tu ayuda.

  20. En el ejemplo que has puesto estas realizando un llamamiento continuamente a la misma hoja… Estas, aun siendo el mismo documento .png, realizando continuas llamadas al mismo. Y digo yo: no sera mejor utilizar la propiedad css “background-position” en vez de estar llamando todo el tiempo a la misma foto por url? Un saludo

  21. No entiendo. Todas las referencias al sprite son por CSS.

  22. ¿Se puede escalar un sprite con background-size?

    He estado probando y según creo esa propiedad hace grande/pequeño todo el sprite, no el elemento que queremos.

    Por lo tanto, ¿lo estoy haciendo mal, hay otro método o simplemente hay que meter varios tamaños de la misma imagen en el sprite?

    Un saludo y gracias por el artículo.

  23. Hola Daniel, me refiero a que en cada de uno de los 6 “estados” que aplicas del mismo sprite (3 imagenes y 3 hover) estas llamando a la imagen por medio de una direccion url (es verdad que a traves de css). Yo te comentaba que pudiera ser mejor disponer del background del sprite una sola vez por llamamiento de url y despues tan solo utilizar la propiedad de background-position (que es cierto que tu lo haces tambien) pero sin volver a llamar a esa imagen por url. Al fin y al cabo y aun siendo la misma imagen del sprite la estas llamando 6 veces. El servidor no entiende de si es la misma imagen o no, entiende que la estas llamando. Un saludo y gracias por tus tutoriales de nuevo.

  24. Llevas razón que lo repito varias veces y no es óptimo en CSS, sin embargo cuando intenté explicarlo en algunos cursos no se entendía, por eso lo puse así: a efectos didácticos.

    Sin embargo te equivocas en algo: por más veces que lo repita en el CSS solo es llamada una vez al servidor. Puedes comprobarlo con el DevTools de Chrome.

    Saludos y gracias por los comentarios!

  25. Hola Daniel,
    pues encantado de que me hayas corregido pues yo siempre he pensado que toda llamada por url contabilizaba fuera la misma imagen o no. Lo voy a echar un ojo por el Google DevTools. Y gracias a ti por publicar estos tutoriales :)

Opiniones y preguntas:

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *