Métodos para precargar y verificar imágenes

publicado en la categoría JavaScript
Cuando se cargan las imágenes de una página, estas quedan almacenadas en el caché. Por lo tanto, si una de ellas se utiliza en otras páginas no será requerida al servidor para ser cargada de nuevo. Por ello, siempre que se pueda, es conveniente repetir la misma imagen para los botones, los íconos, las barras de separación, etc.

Cuando se cargan las imágenes de una página, estas quedan almacenadas en el caché. Por lo tanto, si una de ellas se utiliza en otras páginas no será requerida al servidor para ser cargada de nuevo. Por ello, siempre que se pueda, es conveniente repetir la misma imagen para los botones, los íconos, las barras de separación, etc.

Un aspecto muy importante a tener en cuenta cuando utilizamos imágenes es su tamaño. Cuanto más grande sea el archivo, más tiempo de carga será necesario y dado que los navegadores leen y ejecutan de manera secuencial, al encontrarse una etiqueta img, se inicia la carga de la imagen y recién cuando termina se continúa con el resto de la página. Esto es así por una simple razón, el navegador desconoce su tamaño y, por lo tanto debe cargarla por completo para saber dónde continuar.

Una forma de minimizar este efecto es utilizar los atributos width (ancho) y height (alto) para indicarle al navegador cuál es el tamaño para que pueda reservar el espacio necesario y continuar con la carga del resto de la página al mismo tiempo que lo va haciendo con la imagen:

<img src="URL_imagen" alt="texto explicativo" width="valor_ancho" height="valor_alto">

Esto conviene hacerlo con todas, incluso con las más pequeñas (iconos, botones, etc), para que no haya ninguna interrupción en el proceso de carga del documento.

Pero, aún así, a veces veremos un espacio en blanco entre dos bloques de texto ¿será un error? ¿se habrá colgado el navegador? No hay un único método para resolver esto porque los problemas pueden ser múltiples; quizás, baste informar a los visitantes que algo se está cargando estableciendo una regla general que coloque una imagen de fondo:

img {background: #color url(URL_imagen) no-repeat 50% 50%;}

El mismo problema se produce cuando hay efectos de rollover o slideshows ya que la única imagen que se carga es la primera, la que está visible y las demás sólo lo hacen cuando las activamos con el ratón poniéndolo encima o haciendo click. Si bien a veces esta demora pasa desapercibida, si la imagen tarda en cargarse (es muy grande o la conexión es lenta), no veremos el efecto y nuestras intenciones habrán caído en saco roto.

Un método que suele usarse para precargar imágenes es agregarlas en algún contenedor oculto, ya sea como fondos o como etiquetas img:

<style type="text/css">
	.preload {display: none;}
</style>
<!-- cualquier contenido -->
<img class="preload" src="URL_imagen1" width="ancho" height="alto">
<img class="preload" src="URL_imagen2" width="ancho" height="alto">
<!-- ....... -->
<img class="preload" src="URL_imagenX" width="ancho" height="alto">

En teoría, las imágenes se cargarán (se guardarán en la caché) pero no se mostrarán, cuando las utilicemos el navegador no necesitará descargarlas nuevamente y el efecto será instantáneo.

Algo similar podemos lograr si le agregamos fondos a cualquier contenedor y lo posicionamos fuera de la pantalla:

#unDIV {
	background: url(URL_imagen_1) no-repeat -9999px -9999px;
}
#otroDIV {
	background: url(URL_imagen_2) no-repeat -9999px -9999px;
}

Otra técnica es la llamada preload (precarga) y la forma más común de hacerlos es utilizando JavaScript:

// enumerando cada imagen
img1 = new Image();
img2 = new Image();
img1.src = "URL_imagen1";
img2.src = "URL_imagen2";
// o creando una función para agregar varias
var images = new Array();
function preload() {
	for (i = 0; i < preload.arguments.length; i++) {
		images[i] = new Image();
		images[i].src = preload.arguments[i];
	}
}
preload("URL_imagen1", "URL_imagen2", "URL_imagen3", "URL_imagen4");

Obviamente, si esas imágenes son voluminosas el tiempo de carga del sitio se verá afectado. Para evitar eso podemos comenzar a cargar las imágenes cuando se termina de cargar la página:

function preloader() {
	if (document.getElementById){
		document.getElementById("unDIV").style.background = "url(URL_imagen1) no-repeat -9999px -9999px";
		document.getElementById("otroDIV").style.background = "url(URL_imagen2) no-repeat -9999px -9999px";
	}
}
function addLoadEvent(func) {
	var oldonload = window.onload;
	if (typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function() {
		if (oldonload) { oldonload(); }
			func();
		}
	}
}
addLoadEvent(preloader);

Previamente decía que es importante conocer las dimensiones de las imágenes para que el navegador reserve el espacio necesario y pueda armar el resto del contenido pero, en ciertas condiciones las desconocemos así que debemos esperar que se carguen por completo y recién entonces mostrarlas.

Esto es algo que podemos resolver usando dos funciones; la primera cargará la imagen en la memoria y usará los eventos onload y complete para verificarlo. Cuando ocurra esto, se ejecutará la segunda función que en este ejemplo, simplemente mostrará sus dimensiones.

function waitImagen(src, callback) {
	var imagen = new Image();
	imagen.src = src;
	if (imagen.complete) {
		callback(imagen);
		imagen.onload = function() {};
	} else {
		imagen.onload = function() {
			callback(imagen);
			imagen.onload = function() {};
		}
		imagen.onerror = function() {
			alert("error al cargar la imagen");
		}
	}
}

function imageREADY(imagen) {
	// la imagen esta disponible y podemos mostrarla
	alert(imagen.width + "x" + imagen.height);
}

Esto lo ejecutamos de este modo:

waitImagen("URL_imagen", imageREADY);

¿Otra alternativa? Si queremos verificar que la imagen exista o se pueda cargar, podemos usar el evento onerror y, si es necesario, cambiar la imagen errónea por otra que definamos por defecto:

function errorIMG(url) {
	url.onerror = "";
	url.src = "URL_imagenXdefecto";
	return true;
}

Y esto lo aplicamos a cada etiqueta:

<img src="URL_imagen" onerror="errorIMG(this);">

Para terminar, hay que divertirse un poco así que vamos a crear una imagen de carga sólo con CSS:

ver/ocultar código ejemplo
<style>
.spinner {
	animation-duration:2s;
	animation-iteration-count:infinite;
	animation-name: rotateThis;
	animation-timing-function:linear;
	height:100px;
	position:relative;
	width:100px;
}
.spinner span {
	background:#000;
	border-radius:50%;
	height:20px;
	left:40px;
	position:absolute;
	top:40px;
	width:20px;
}
/* se posiciona cada div, rotándolos */
.spinner span:nth-child(1) {transform:rotate(0deg) translate(0, -40px); opacity:0.12;}
.spinner span:nth-child(2) {transform:rotate(45deg) translate(0, -40px); opacity:0.25;}
.spinner span:nth-child(3) {transform:rotate(90deg) translate(0, -40px); opacity:0.37;}
.spinner span:nth-child(4) {transform:rotate(135deg) translate(0, -40px); opacity:0.50;}
.spinner span:nth-child(5) {transform:rotate(180deg) translate(0, -40px); opacity:0.62;}
.spinner span:nth-child(6) {transform:rotate(225deg) translate(0, -40px); opacity:0.75;}
.spinner span:nth-child(7) {transform:rotate(270deg) translate(0, -40px); opacity:0.87;}
.spinner span:nth-child(8) {transform:rotate(315deg) translate(0, -40px); opacity:1;}
@keyframes rotateThis {
	from {transform:scale(0.5) rotate(0deg);}
	to {transform:scale(0.5) rotate(360deg);}
}
</style>

<div class="spinner">
	<span></span>
	<span></span>
	<span></span>
	<span></span>
	<span></span>
	<span></span>
	<span></span>
	<span></span>
</div>