Comunidad de diseño web y desarrollo en internet

Cómo pensar y crear un slider en jQuery

Hola amigos, voy a intentar explicar lo mejor que pueda y razonando todas las partes (y ni que decir tiene que desde mis conocimientos que no lucen por ser completos precisamente), cómo crear un slider en jQuery que sea capaz de generar varios sliders en pantalla y que todos funcionen de manera independiente. También intentaré razonar la estructuración inicial, el por qué de los cálculos y demás...

No puedo prometer ni prometo, que el slider no se pueda hacer mejor ni más completo que como yo lo planteo en este tutorial (pues es obvio que sí se puede), por lo que si sabes cómo mejorarlo y estás dispuesto a compartirlo con los demás, estaría encantado de recibir tus consejos. Y si son factibles y no son una chapuza, como muchas de las que se me ocurren a mí, los incluiré encantado nombrándote en el tuto e indicando tu aportación.

Empecemos por ver qué es lo que queremos conseguir y cómo estructurarlo:



1. Estructura interna del slider y de las imágenes



Vamos a visualizar cualquiera de los 2 sliders que encontraréis aquí. Fijaos en el parpadeo y el cambio de textos que suceden cuando se pone el ratón encima de una de las imágenes. Con fijarnos en estos elementos tendremos una idea de todos los elementos que componen el slider.

(Internamente tiene alguno más pero ya los veremos cuando desarrollemos código, pues no son visibles para el usuario)

Bien, visto esto vamos a pensar en la estructura del contenedor. Lo básico sería tener un div contenedor con 4 divs internos que sean las imágenes. Veámoslo:



Ahora vamos a intentar imaginar la estructura de cada div imagen. Este también se habrá de componer de un div contenedor y dentro de éste una imagen o un div con fondo de imagen (nosotros usaremos el div), encima de la imagen un div blanco tranparente que parpadee con los hover del mouse y encima de este un div alineado al pie que contenga: un div negro con transparencia, encima un div con el título y encima de este un div con el mensaje alternativo ó 2º mensaje, este último oculto. Veámoslo:



2. Datos necesarios para generar un slider


El siguiente paso es pensar en los datos que necesitaremos para generar un slider (no hace falta decir que sólo hemos de pensar en los datos que puedan cambiar de un slider a otro y que no se puedan calcular a partir de otros datos).

    1. Ancho del contenedor principal.
    2. Alto de las imágenes (el contenedor principal también medirá esto de alto)
    3. Ancho de las imágenes.
    4. Altura de la caja de textos.
    5. Ruta de la carpeta de las imágenes.
    6. Un array con las imágenes y sus datos.
      • Nombre de la imagen.
      • Url del link.
      • Texto para el título
      • Texto para el subtítulo


¡Hecho!, vamos a generar un objeto javaScript con todos estos datos y vamos a llamarlo losDatosDelSlider.

Código :

losDatosDelSlider = {
   'anchoImagen': 733,
   'altoImagen': 350,
   'anchoTotal': 930,
   'alturaCajaTextos': 75,
   'carpetaImagenes': 'imgb/',
   'imagenes':[
      {'url': '1.jpg', 'Link': '1.cristalab.com', 'tit': 'Uno', 'subt': '1'},
      {'url': '2.jpg', 'Link': '2.cristalab.com', 'tit': 'Dos', 'subt': '2'},
      {'url': '3.jpg', 'Link': '3.cristalab.com', 'tit': 'Tres', 'subt': '3'},
      {'url': '4.jpg', 'Link': '4.cristalab.com', 'tit': 'Cuatro', 'subt': '4'}
   ]   
}


Ahora vamos a crear nuestra función principal e inicializar las variables. Lo primero es preparar la función para que reciba los datos y los pueda introducir en algún elemento del DOM, así que la creamos con la variable datos y con la variable contenedorSlider (esta variable debería ser un string con la id del objeto donde se vaya a crear el slider y con la almohadilla incluida (ej: '#laIdObjetivo') y debería pertenecer a un div por norma general).

Código :

function crearSlider(datos, contenedorSlider) {
   //Bien en teoría al ejecutar la función estamos recibiendo los datos del objeto js que hemos creado más arriba (luego lo haremos en la llamada a la función), así como el nombre de lo que vamos a usar de contenedor.
   //Empezamos a definir las variables
   var contenedor = $(contenedorSlider),
      carpetaImagenes = datos.carpetaImagenes,
      anchoImagen = datos.anchoImagen,
      altoImagen = datos.altoImagen,
      anchoTotal = datos.anchoTotal,
      imagenes = datos.imagenes,
      totalImagenes = imagenes.length,
      boxNegro = datos.alturaCajaTextos,
      //Calculo lo que ocupa en pantalla cada imagen que es el ancho del contenedor dividido por el número de imágenes que va a contener (este cálculo nos valdrá para establecer la posición inicial de los divs de imagen)
      anchoMedia = anchoTotal/totalImagenes;
}



3. Asignando datos y rellenando el contenedor


El siguiente paso es rellenar el contenedor, lo primero que haremos es asignarle una clase y definiremos su ancho y su alto, posteriormente mediante un bucle, lo rellenaremos con tantas imágenes como necesitemos, ¡vamos a ello!

Código :

function crearSlider(datos, contenedorSlider) {
   
   [Código anterior]
   
   //Asignamos el ancho y el alto al contenedor y le añadimos su clase personalizada (esta la configuraremos más tarde en el css)
   contenedor.width(anchoTotal).height(altoImagen).addClass('bloqueDisplay');
   
   //Ahora creo el bucle y relleno el contenedor (el bucle entrará una vez por cada imagen definida en el objeto js que hemos creado al principio "losDatosDelSlider", e índice valdrá +1 cada vez que el bucle pase por una imagen, la primera valdrá 0, la segunda 1, la tercera 2...)
   for (indice in imagenes) {
      
      //Creo el div imagen dentro de una selección (para agregarle contenido) y le asigno una id personalizada por el índice (imagen0, imagen1, imagen2...)
      contenedor.append($('<div class="imagen" id="imagen'+indice+'">')
         
         //Agrego al div imagen -- un div interno que estará oculto por css y que contendrá el link que se cargará al hacer click sobre la imagen. En la siguiente línea cuando pongo imagenes[indice].Link, estoy cogiendo el link de la imagen que hay en la posición relativa al paso del bucle, es decir en el primer paso estaré recogiendo el link de la imagen que hay en la primera posición, posición 0 (imagenes[0]), en la segunda pasada estaré cogiendo el link de la imágenes que hay en la segunda posición, la posición 1 (imagenes[1]) y así consecutivamente hasta que no haya más imágenes.
         .append($('<div class="links">').html(imagenes[indice].Link))
         
         //Agrego al div imagen -- un div dentro de una selección (para aplicarle una animación) con fondo blanco que va a ocupar toda la imagen y lo hago desaparecer según lo creo. Este div nos hará el efecto del parpadeo de la imagen 
         .append($('<div class="parpadeo" />').fadeTo(0, 0).dequeue())
         
         //Agrego al div imagen -- un div dentro de una selección (para agregarle contenidos) que será la caja inferior con los textos
         .append($('<div class="cajaTextos" />')
         
            //Agrego al div cajaTextos -- el div que será el fondo oscuro de los textos
            .append('<div class="fondoOscuro" />')
            
            //Agrego al div cajaTextos -- un div dentro de una selección (para agregarle contenidos), que será el título visible por defecto
            .append($('<div class="titular" />').html(imagenes[indice].tit))
            
            //Agrego al div cajaTextos -- un div dentro de una selección (para agregarle contenidos), que estará oculto y será el título y subtítulo que aparecerán cuando el usuario haga over sobre alguna de las imágenes del slider. lo hago desaparecer según lo creo y le agrego un div para el título, y el subtítulo
            .append($('<div class="subtitular" />').fadeTo(0, 0).dequeue()
               .append($('<div class="titulo" />').html(imagenes[indice].tit))
               .append('<br />'+imagenes[indice].subt)
            )
         )
      )
   }
   
}


¡HOOOORAY!, con eso tenemos el slider relleno, ahora por partes, a cada elemento que lo necesite de los que acabamos de crear le vamos a ir aplicando medidas y así vamos completando el código.

Empezaremos por el primer elemento que se crea en el bucle. El div imagen: si recordamos, cada una de las imágenes del slider tiene una sombra a la izquierda, por lo que le agregaremos la clase sombraIzquierda que configuraremos más adelante. Después definiremos su ancho, su alto, su z-index (capa) y su imagen de background. También definiremos su cualidad left. Como hemos visto el bucle empieza por 0, por lo que para calcular el left, no nos hará más falta que multiplicar la variable anchoMedia por el índice. Veamos el cálculo.



Ahora, veamos cómo queda la línea con los nuevos datos:

Código :

//Línea anterior
contenedor.append($('<div class="imagen" id="imagen'+indice+'">')

//Nueva línea
contenedor.append($('<div'+
   'class="imagen sombraIzquierda"'+
   'id="imagen'+indice+'"'+
   'style="width:'+anchoImagen+'px;'+
      'height:'+altoImagen+'px;'+
      'left:'+(anchoMedia*indice)+'px;'+
      'z-index:'+indice+';'+
      'background: url('+carpetaImagenes+imagenes[indice].url+')" />')


Vamos a configurar el alto de la caja de textos. La medida está definida en las variables iniciales como "boxNegro" el ancho no hace falta definirlo pues será el 100%, así ocupará todo el ancho de la imagen e irá en el css.

Código :

//Línea anterior
.append($('<div class="cajaTextos" />')

//Línea nueva
.append($('<div class="cajaTextos" style="height:'+boxNegro+'px" />')

Con esto tenemos toda la personalización que necesitamos de momento, el resto de los estilos y medidas irán el el css y así podremos cambiar todos los estilos del slider sin modificar el javaScript. Es hora de ver la primera fusión del código. ¡Veamos lo que llevamos!

Código :

function crearSlider(datos, contenedorSlider) {
   var contenedor = $(contenedorSlider),
      carpetaImagenes = datos.carpetaImagenes,
      anchoImagen = datos.anchoImagen,
      altoImagen = datos.altoImagen,
      anchoTotal = datos.anchoTotal,
      imagenes = datos.imagenes,

      totalImagenes = imagenes.length,
      boxNegro = datos.alturaCajaTextos,
      anchoMedia = anchoTotal/totalImagenes;
      
   contenedor.width(anchoTotal).height(altoImagen).addClass('bloqueDisplay');

   for (indice in imagenes) {
      contenedor
         .append($('<div class="imagen sombraIzquierda"'+
            'id="imagen'+indice+'"'+
            'style="width:'+anchoImagen+'px;'+
               'height:'+altoImagen+'px;'+
               'left:'+(anchoMedia*indice)+'px;'+
               'z-index:'+indice+';'+
               'background: url('+carpetaImagenes+imagenes[indice].url+')" />')
         .append($('<div class="links">').html(imagenes[indice].Link))
         .append($('<div class="parpadeo" />').fadeTo(0, 0).dequeue())
         .append($('<div class="cajaTextos" style="height:'+boxNegro+'px" />')
            .append('<div class="fondoOscuro" />')
            .append($('<div class="titular" />').html(imagenes[indice].tit))
            .append($('<div class="subtitular" />').fadeTo(0, 0).dequeue()
               .append($('<div class="titulo" />').html(imagenes[indice].tit))
               .append('<br />'+imagenes[indice].subt)
            )
         )
      )
   }
}


4. Creando los eventos del mouse y calculando las posiciones para los eventos



Poco a poco, poco a poco, vamos llegando al grosso del tuto. Vamos a empezar a configurar los listeners del mouse y a calcular las nuevas posiciones de las imágenes cuando se tengan que mover, el último paso será programar la pequeña función que se va a encargar de ejecutar los movimientos (animaciones) y después crearemos el css. Comenzaremos creando la función reiniciarListeners que se encargará de desvincular los eventos actuales asociados a la clase imagen y volverá a crearlos. También vamos a crear acción del click:

Código :

function reiniciarListeners () {
   //Desvinculo todos los eventos que puedan tener las imágenes
   $('.imagen').unbind();
   
   //Vinculo el evento click a todos los elementos que tengan la clase imagen.
   $('.imagen').click(function () {
      
      //Al hacer click almaceno en la variable url la url destino del objeto clickeado, que está almacenada en su div.links (este div está oculto con la propiedad css display:none, lo configuraremos más adelante) 
      var url = $(this).find('.links').html();
      
      //Y le paso la nueva url al navegador         
      window.location.href = '#/'+url;
   });
}


Con esto ya tenemos configurado el click de las imágenes y su correspondiente acción, que será abrir una url al hacer click. Ahora vamos a configurar las acciones over y out, es decir lo que ocurre cuando el ratón pasa sobre una de las imágenes y lo que ocurre cuando sale de ella. Por si alguien nunca ha visto cómo asignar en jQuery los eventos over y out en un único selector mediante .hover(), lo vamos a ver rápidamente.

Código :

$(miSelector).hover(
   //Definimos la primera de las funciones, la que se ejecutará al hacer over
   function () {
      //Aquí programamos las cosas que ocurrirán en el over
   },
   //Definimos la segunda de las funciones, la que se ejecutará al hacer out
   function () {
      //Aquí programamos las cosas que ocurrirán en el out
});


Sencillo ¿no?, pues ahora a programar las acciones que nos calcularán las nuevas posiciones izquierdas de las imágenes en cada evento del ratón. Vamos a parar un par de minutos a pensar lo que sucerá cuando pongamos el ratón sobre una de las imágenes.

      DIV IMAGEN
    • En la imagen seleccionada el div parpadeo parpadeará 1 vez.
    • En la imagen seleccionada el título se desvanecerá.
    • En la imagen seleccionada el subtítulo aparecerá.

      MOVIMIENTOS
    • Todas las imágenes se moverán a excepción de la primera.
    • Si el ratón está sobre la primera de las imágenes todas se moverán a la derecha
    • Si el ratón está sobre la última de las imágenes todas se moverán a la izquierda
    • Si el ratón está sobre cualquier otra imagen, las imágenes anteriores a esta y esta se moverán a la izquierda y las posteriores a la derecha
    • La imagen sobre la que está el ratón mostrará todo su ancho dentro del slider


Sabido esto, y dado que la imagen sobre la que esté el ratón se va a mostar entera en el slider, hemos de asumir que el resto de imágenes ocupará lo que nos quede libre del slider, dividido entre el resto de las imágenes, es decir (anchoTotal-anchoImagen), con esto obtenemos el espacio que nos queda libre para las imágenes que estén fuera del mouse, si esto lo dividimos por el total de imágenes menos 1 (-1 por que la imagen sobre la que está el ratón no hay que contarla, pues ya tiene su espacio asignado dentro del slider que es igual a su ancho), obtendremos el espacio que han de ocupar el resto de las imágenes en su posición cerrada, ((anchoTotal-anchoImagen)/(totalImagenes-1)). Veamos el cálculo:



Vamos a empezar en la función OVER, definiendo los valores que vamos a necesitar. Además de las medidas tendremos que definir el div que parpadeará, el que se desvanecerá y el que aparecerá. Con respecto a las medidas, podríamos obtener alguno de los valores necesitados de nuestro objeto js losDatosDelSlider, pero dado que queremos hacer una función que sea capaz de generar varios sliders y una función que sea capaz de moverlos independientemente, los vamos a extraer directamente del DOM, es decir de los objetos que tengamos instanciados en el navegador.

Código :

//Primero voy a extraer el índice de la imagen dentro de su slider, es decir la posición que ocupa entre las demás imágenes. Para esto primero buscamos todas las imágenes que hay dentro de su contenedor

$($this).parent().find('.imagen')
         
//Y ahora vamos a buscar la posición de la imagen que está bajo el ratón dentro de la selección que acabamos de hacer. Creamos la selección dentro de un selector y preguntamos por el índice de la imagen en over "this"

$($(this).parent().find('.imagen')).index(this)

//Con esto ya tenemos la posición de la imagen sobre la que estamos.
//Vamos a definir el anchoTotal, que extraeremos del padre (.parent()) de la imagen, y el anchoImagen que extraeremos de la propia imagen.

anchoTotal = $(this).parent().width()
anchoImagen = $(this).width()

//Lo siguiente es calcular la cantidad de imágenes que hay en el contenedor.
////////////
Seguro que hay una manera más fácil de hacer esto, si alguien la pudiera aportar se lo agradecería.
////////////
//Ahora vuelvo a crear la selección que he creado al principio de la función, pero esta vez le pregunto el índice de la última imagen que haya dentro del contenedor, y como el índice siempre empieza en 0, le sumo 1 para obtener el total de imágenes.

//SELECCIÓN INICIAL

$(this).parent() //Voy a su elemento superior
   .find('.imagen') //Busco cualquier elemento con la clase imagen
   
//SELECCIÓN DE LA ULTIMA IMAGEN DENTRO DE LA CAJA

$(this).parent() //Voy a su elemento superior
   .find('.imagen:last') //Busco el último elemento con la clase imagen
   
//Y AHORA LO UNO PARA BUSCAR EL ÍNDICE DE LA ULTIMA Y LE SUMO 1

$($(this).parent().find('.imagen'))
   .index($(this).parent().find('.imagen:last'))+1

//lo siguiente es calcular el ancho de las imágenes cerradas (podemos ver el cálculo en el último gráfico)

cerrado = (anchoTotal-anchoImagen)/(totalImagenes-1)

//Ahora localizaremos el div títulos que contiene el título y el subtítulo

titulos = $(this).find('div:eq(2)')

//Y por último localizaremos el div que va a parpadear

parp = $(this).find('.parpadeo')


Antes de ver lo anterior unido, voy a explicar que queremos conseguir con las funciones over y out. De estas funciones lo único que necesitamos que hagan es que nos calculen las posiciones x de las imágenes y que monten un array con ellas, después este array se lo pasaremos a la última función que será la que genere los movimientos a partir de este array, así que también definiremos un array vacío. Veamos lo que llevamos de la función OVER:

Código :

function /*OVER*/ () {         
   var num = $($(this).parent().find('.imagen')).index(this),
      anchoTotal = $(this).parent().width(),
      anchoImagen = $(this).width(),
      totalImagenes = $($(this).parent().find('.imagen'))
         .index($(this).parent().find('.imagen:last'))+1,
      cerrado = (anchoTotal-anchoImagen)/(totalImagenes-1)
      titulos = $(this).find('div:eq(2)'),
      parp = $(this).find('.parpadeo'),
      arr = [];
}


Ahora un poco de animación. Lo primeo que queremos que haga la imagen seleccionada es que parpadee (efecto tipo iluminación, ¡¡¡"CUTRE"!!!, que ya estoy viendo a alguno decir que eso no es iluminación, lo sé, pero no se me ocurre mejor manera más corta de explicarlo con palabras), y que a la vez que se produce el parpadeo, desaparezca el título y aparezca el subtítulo.

Código :

//Hago aparecer el div parpadeo en 0 milisegundos (a partir de ahora ms), al 50% de opacidad (0.5 pues la opacidad va de 0 a 1)

parp.fadeTo(0, 0.5).dequeue();

//En medio segundo 500 ms, lo hago desaparecer, opacidad 0. 

parp.fadeTo(500, 0).dequeue();

//Localizo el div título y subtitulo y en 300 ms hago desaparecer el primero, opacidad 0 y hago aparecer el segundo opacidad 1

titulos.find('div:eq(1)').fadeTo(300,0).dequeue();
titulos.find('div:eq(2)').fadeTo(300,1).dequeue();

//Y los índices son 1 y 2 por que en el 0 está el fondo negro semi-opaco


Bueno, ahora sólo nos queda calcular las posiciones de las imágenes y para ello vamos a crear un bucle que pase por encima de cada imagen y con un condicional diferenciaremos si la imagen a mover está posicionada antes que la imagen o es la imagen con over, y si está detrás de la imagen con over. Por qué es esto, bueno por que depende de donde esté posicionada la imagen con respecto a la que tiene over, habrá que calcular su left de manera diferente. Veamos el primer caso, imágenes a la derecha.



Como vemos en el ejemplo, a la posición x de todas las imágenes que hay a la derecha de la seleccionada, hay que sumarle el AnchoImagen y lo que ocupa en pantalla la imagen en posición cerrada multiplicado por su índice-1 que viene a ser lo que vemos en el gráfico Posicion = anchoImagen+(w*(indice-1)). Este cálculo es diferente para la imagen seleccionada, o las que estén posicionadas anteriores a ella. Veamos el segundo caso, imágenes a la izquierda.



Como vemos en este último gráfico el cálculo cambia y es más sencillo, pues para hallar la posición sólo hay que multiplicar lo que ocupa la imagen en posición cerrada por su índice. Hagámoslo.

Código :

//Primero creamos el bucle, vamos a hacer que comience en 1 pues la posición de la primera imagen será siempre 0 y no hay que calcularla ni que moverla, así que:
for (i=1; i<totalImagenes; i++) {
   
}

//Ahora crearemos el condicional, si la variable i del bucle es inferior o igual a la posición de imagen seleccionada, le aplicaremos la primera fórmula de los ejemplos, si no lo es, aplicaremos la segunda.
if (i <= num) {
   //introducimos en el array el valor resultado de la fórmula. El valor índice de la fórmula será la i del bucle y el valor w será la variable cerrado
   arr.push(cerrado*i);
}

//Si la imagen está después de la seleccionada aplicaremos la segunda fórmula con la misma asociación de valores que acabamos de nombrar.
else {
   arr.push(anchoImagen+(cerrado*(i-1)))
}

//Veámoslo completo:
for (i=1; i<totalImagenes; i++) {
   if (i <= num) {arr.push(cerrado*i)}
   else {arr.push(anchoImagen+(cerrado*(i-1)))}
}


Bueno bueno bueno, con esto ya tenemos toda la función OVER completa. Ahora por cada imagen superior a la primera me ha introducido en el array arr un valor. Como muestra ficticia, por ejemplo podría recibir un array que fuera así: [50, 100, 150]. Con esos 3 valores del array (3 será variable en función del número de imágenes cargadas) posteriormente le indicaremos a la función que se encarga de mover las imágenes a donde ha de mandar cada una. Ya crearemos la función al final, ahora vamos a ver la función OVER completa:

Código :

function /*OVER*/ () {         
   var num = $($(this).parent().find('.imagen')).index(this),
      anchoTotal = $(this).parent().width(),
      anchoImagen = $(this).width(),
      totalImagenes = $($(this).parent().find('.imagen'))
         .index($(this).parent().find('.imagen:last'))+1,
      cerrado = (anchoTotal-anchoImagen)/(totalImagenes-1)
      titulos = $(this).find('div:eq(2)'),
      parp = $(this).find('.parpadeo'),
      arr = [];
      
   parp.fadeTo(0, 0.5).dequeue();
   parp.fadeTo(500, 0).dequeue();
   titulos.find('div:eq(1)').fadeTo(300,0).dequeue();
   titulos.find('div:eq(2)').fadeTo(300,1).dequeue();
   for (i=1; i<totalImagenes; i++) {
      if (i <= num) {arr.push(cerrado*i)}
      else {arr.push(anchoImagen+(cerrado*(i-1)))}
   }
}


Estupendo, ahora vamos a hacer la función out, tardaremos menos pues muchas de las cosas de esta función ya están explicadas con anterioridad. Primero tendremos que definir de nuevo el div títulos de la imagen, el ancho total, el total de imágenes, un nuevo array vacío para acumular las posiciones originales y ocultaremos el subtítulo a la par que mostramos el título. Vamos al lío:

Código :

function /*OUT*/ () {
   var titulos = $(this).find('div:eq(2)'),
      anchoTotal = $(this).parent().width(),
      totalImagenes = $($(this).parent().find('.imagen'))
         .index($(this).parent().find('.imagen:last'))+1,
      posiciones = [];
   titulos.find('div:eq(1)').fadeTo(300, 1).dequeue();
   titulos.find('div:eq(2)').fadeTo(300, 0).dequeue();
}


Como todo esto ya está explicado hace un momentín, podemos pasar a la función que rellenará el array, en este caso es mucho más sencillo, pues sólo tiene que calcular las posiciones iniciales, con lo que con una sola fórmula y sin condiciones podemos calcular todas las posiciones. Esta fórmula también está más arriba explicada, en el gráfico del punto 3. Asignando datos y rellenando el contenedor. Vamos a crear el bucle.

Código :

for (i=1; i<totalImagenes; i++) {
   posiciones.push((anchoTotal/totalImagenes)*i)
}


Fiiiiiiiiiiiuuuunnnn!!!, ya hemos acabado la función out, y a toda leche, como dicen en mi pueblo. Veámosla.

Código :

function /*OUT*/ () {
   var titulos = $(this).find('div:eq(2)'),
      anchoTotal = $(this).parent().width(),
      totalImagenes = $($(this).parent().find('.imagen'))
         .index($(this).parent().find('.imagen:last'))+1,
      posiciones = [];
   titulos.find('div:eq(1)').fadeTo(300, 1).dequeue();
   titulos.find('div:eq(2)').fadeTo(300, 0).dequeue();
   for (i=1; i<totalImagenes; i++) {
      posiciones.push((anchoTotal/totalImagenes)*i)
   }
}


OK, vamos a ver la función reiniciarListeners(), completa.

Código :

function reiniciarListeners () {
   $('.imagen').unbind();
   
   $('.imagen').click(function () {
      var url = $(this).find('.links').html();      
      window.location.href = '#/'+url;
   });

   $('.imagen').hover(
      function /*OVER*/ () {         
         var num = $($(this).parent().find('.imagen')).index(this),
            anchoTotal = $(this).parent().width(),
            anchoImagen = $(this).width(),
            totalImagenes = $($(this).parent().find('.imagen'))
               .index($(this).parent().find('.imagen:last'))+1,
            cerrado = (anchoTotal-anchoImagen)/(totalImagenes-1)
            titulos = $(this).find('div:eq(2)'),
            parp = $(this).find('.parpadeo'),
            arr = [];
         parp.fadeTo(0, 0.5).dequeue();
         parp.fadeTo(500, 0).dequeue();
         titulos.find('div:eq(1)').fadeTo(300,0).dequeue();
         titulos.find('div:eq(2)').fadeTo(300,1).dequeue();
         for (i=1; i<totalImagenes; i++) {
            if (i <= num) {arr.push(cerrado*i)}
            else {arr.push(anchoImagen+(cerrado*(i-1)))}
         }
      },
      function /*OUT*/ () {
         var titulos = $(this).find('div:eq(2)'),
            anchoTotal = $(this).parent().width(),
            totalImagenes = $($(this).parent().find('.imagen'))
               .index($(this).parent().find('.imagen:last'))+1,
            posiciones = [];
         titulos.find('div:eq(1)').fadeTo(300, 1).dequeue();
         titulos.find('div:eq(2)').fadeTo(300, 0).dequeue();
         for (i=1; i<totalImagenes; i++) {
            posiciones.push((anchoTotal/totalImagenes)*i)
         }
   });   
}


5. La animación de las imágenes



Bien el último paso, ahora sólo tenemos que crear la ridícula función que lo moverá todo, venga, vamos a echarnos unas risas todos, Ja ja ja, vale... Continuemos. Pensemos la función, necesitamos una FUNCION que SELECCIONE VARIOS elementos del navegador y los REPOSICIONE en función a un ARRAY, estas palabra en mayúscula me suenan a: SELECCIONE (id o clase), VARIOS (bucle), REPOSICIONE (una funcion animate()), ARRAY (el array que obtengo de la función over o out).

Es importante saber que en este tutorial estoy asumiendo que cuando tenga varios sliders en pantalla tendré varios elementos con la misma id, bien como seleccionaré los objetos con un find() desde su padre no habrá problema ya que la id sólo se usará en estos casos, pero NUNCA, JAMÁS, es buena práctica asignar la misma id a más de un objeto. Dicho esto hagamos la función.

Código :

//Lo primero es hacer que la función reciba un array, que serán las posiciones y una id que será la id del slider objetivo. Y para empezar, generaremos la id del contenedor padre
function reposicionar (posiciones, slider) {
   var contenedor = '#'+slider;
}

//Ahora crearemos el bucle que hará las animaciones, una por cada imagen (posiciones.length) excepto la primera que siempre estará en posición left: 0. De manera que comenzamos el bucle en 1
for (i=1; i<posiciones.length; i++) {
            
}

//Y por último generamos la id de la imagen,
var objetivo = '#imagen'+i;

//La buscamos dentro del contenedor objetivo
$(contenedor).find(objetivo)

//Y la animamos a la posición que indique el array, dado las posiciones del array empiezan en 0, y nosotros estamos haciendo el bucle desde 1, hay que restarle 1 a i, para establecernos en la posición correcta del array.
.animate({left:posiciones[i-1]}, 200).dequeue()


Veámosla completa:

Código :

function reposicionar (posiciones, slider) {
   var contenedor = '#'+slider;
   for (i=1; i<posiciones.length; i++) {
      var objetivo = '#imagen'+i;
      $(contenedor).find(objetivo).animate({left:posiciones[i-1]}, 200).dequeue();
   }
}


¡¡¡PUES HEMOS TERMINADO!!! Ya sólo nos queda ponerlo todo junto y generar las llamadas a la última función desde las funciones OVER y OUT. Vamos a hacer esa llamada.

Código :

//Mudemos nuestros pensamientos al final de la función OVER, para ejecutar la función reposicionar que acabamos de configurar, sólo habría que obtener la id del slider contenedor, pues el array ya lo tendríamos relleno. Vamos a crear la llamada a la funcion, enviado en primer lugar el array y en segundo lugar pasaremos como contenedor el padre de la imagen con over
//En el over
reposicionar(arr, $(this).parent().attr('id'))

//En el out
reposicionar(posiciones, $(this).parent().attr('id'))


6. Repaso breve del código desarrollado



Bien vamos a ver todo lo que hemos hecho juntito y de la mano, a partir de ahora sólo nos quedará generar una hoja de estilos css para el slider

Primero el objeto con los datos, recordad que esto podría ser el resultado de una consulta a una BBDD, un XML, un JSON... sólo habría que cambiar la manera de tratar los datos recibidos, ó convertir los datos recibidos en objetos como este y dejas el slider como está , lo mismo da, que da lo mismo.

Código :

losDatosDelSlider = {
   'anchoImagen': 733,
   'altoImagen': 350,
   'anchoTotal': 930,
   'alturaCajaTextos': 75,
   'carpetaImagenes': 'imgb/',
   'imagenes':[
      {'url': '1.jpg', 'Link': '1.cristalab.com', 'tit': 'Uno', 'sub': '1'},
      {'url': '2.jpg', 'Link': '2.cristalab.com', 'tit': 'Dos', 'sub': '2'},
      {'url': '3.jpg', 'Link': '3.cristalab.com', 'tit': 'Tres', 'sub': '3'},
      {'url': '4.jpg', 'Link': '4.cristalab.com', 'tit': 'Cuatro', 'sub': '4'}
   ]   
}


Ahora la llamada a la función crearSlider(), que genera el slider, le pasaremos el objeto y la id del contenedor del slider. Es imperativo que esta llamada siempre vaya dentro de una función $(document).ready(function () {}) para que se ejecute sólo cuando el DOM esté listo y así no obtengamos resultados indeseados. Las otras funciones pueden ir dentro o fuera.

Código :

crearSlider(losDatosDelSlider, '#contenedorSlider')


Vamos con la función crearSlider(), fijarse que al final he añadido la llamada a reiniciarListeners, por lo que cada vez que se cargue un slider se reiniciarán las escuchas.

Código :

function crearSlider(datos, contenedorSlider) {
   var contenedor = $(contenedorSlider),
      carpetaImagenes = datos.carpetaImagenes,
      anchoImagen = datos.anchoImagen,
      altoImagen = datos.altoImagen,
      anchoTotal = datos.anchoTotal,
      imagenes = datos.imagenes,
      totalImagenes = imagenes.length,
      boxNegro = datos.alturaCajaTextos,
      anchoMedia = anchoTotal/totalImagenes;
      
   contenedor.width(anchoTotal).height(altoImagen).addClass('bloqueDisplay');

   for (indice in imagenes) {
      contenedor
         .append($('<div class="imagen sombraIzquierda"'+
            'id="imagen'+indice+'"'+
            'style="width:'+anchoImagen+'px;'+
               'height:'+altoImagen+'px;'+
               'left:'+(anchoMedia*indice)+'px;'+
               'z-index:'+indice+';'+
               'background: url('+carpetaImagenes+imagenes[indice].url+')" />')
         .append($('<div class="links">').html(imagenes[indice].Link))
         .append($('<div class="parpadeo" />').fadeTo(0, 0).dequeue())
         .append($('<div class="cajaTextos" style="height:'+boxNegro+'px" />')
            .append('<div class="fondoOscuro" />')
            .append($('<div class="titular" />').html(imagenes[indice].tit))
            .append($('<div class="subtitular" />').fadeTo(0, 0).dequeue()
               .append($('<div class="titulo" />').html(imagenes[indice].tit))
               .append('<br />'+imagenes[indice].subt)
            )
         )
      )
   }
   reiniciarListeners()
}


La llamada a reiniciarListeners(), que eliminará las escuchas y las volverá a crear cada vez que se instancie un slider, fijaos que ya tiene las llamadas a reposicionar() añadidas.

Código :

function reiniciarListeners () {
   $('.imagen').unbind();
   
   $('.imagen').click(function () {
      var url = $(this).find('.links').html();      
      window.location.href = '#/'+url;
   });

   $('.imagen').hover(
      function /*OVER*/ () {         
         var num = $($(this).parent().find('.imagen')).index(this),
            anchoTotal = $(this).parent().width(),
            anchoImagen = $(this).width(),
            totalImagenes = $($(this).parent().find('.imagen'))
               .index($(this).parent().find('.imagen:last'))+1,
            cerrado = (anchoTotal-anchoImagen)/(totalImagenes-1)
            titulos = $(this).find('div:eq(2)'),
            parp = $(this).find('.parpadeo'),
            arr = [];
            
         parp.fadeTo(0, 0.5).dequeue();
         parp.fadeTo(500, 0).dequeue();
         titulos.find('div:eq(1)').fadeTo(300,0).dequeue();
         titulos.find('div:eq(2)').fadeTo(300,1).dequeue();
         for (i=1; i<totalImagenes; i++) {
            if (i <= num) {arr.push(cerrado*i)}
            else {arr.push(anchoImagen+(cerrado*(i-1)))}
         }
         reposicionar(arr, $(this).parent().attr('id'))
      },
      function /*OUT*/ () {
         var titulos = $(this).find('div:eq(2)'),
            anchoTotal = $(this).parent().width(),
            totalImagenes = $($(this).parent().find('.imagen'))
               .index($(this).parent().find('.imagen:last'))+1,
            posiciones = [];
         titulos.find('div:eq(1)').fadeTo(300, 1).dequeue();
         titulos.find('div:eq(2)').fadeTo(300, 0).dequeue();
         for (i=1; i<totalImagenes; i++) {
            posiciones.push((anchoTotal/totalImagenes)*i)
         }
         reposicionar(posiciones, $(this).parent().attr('id'))
   });   
}


Y por último la función reposicionar que se encargará de los movimientos

Código :

function reposicionar (posiciones, slider) {
   var contenedor = '#'+slider;
   for (i=1; i<posiciones.length; i++) {
      var objetivo = '#imagen'+i;
      $(contenedor).find(objetivo).animate({left:posiciones[i-1]}, 200).dequeue();
   }
}


Espero que en este punto nos hayamos enterado bien de las cosas, porque ya no hay vuelta atrás! Bien, vamos a ver en el navegador qué es lo que hace ahora mismo la función, sin ningún estilo aplicado. El link a continuación ya está cargando 2 sliders Slider sin estilos


7. Aplicación de estilos


¡¡UHUUUUHUUUUUUU!!, esto ya es el fín. Vamos a estilizar el slider a base de css. Yo creo que lo más correcto, y por favor que alguien me corrija si me equivoco, es que el orden a seguir sea de abajo -> arriba, es decir, primero el contenedor principal, después la clase .imagen y después también por orden todo lo que contiene una div imagen. De manera que vamos con los estilos, no me voy a parar a explicarlos.

Código :

#contenedorSlider {
   display: block;
   overflow: hidden;
   position: relative;
}

.imagen {
   position: absolute;
}


Veamos cómo con estos dos estilos ya parece el slider final Primeros estilos, bien continuemos con los estilos

Código :

#contenedorSlider, .parpadeo {
   display: block;
}

.imagen, .parpadeo, .subtitular, .titular, .cajaTextos, .fondoOscuro, .titulo {
   position: absolute;
}

.parpadeo, .fondoOscuro, .titular {
   width: 100%;
   height: 100%;
}

#contenedorSlider {
   overflow: hidden;
   position: relative;
   margin: auto;
   top: 35px;
   cursor: pointer;
   border: 1px solid black;
}

.sombraIzquierda {
   box-shadow:   -8px -2px 16px #000;
   -webkit-box-shadow:   -8px -2px 16px #000;
   -moz-box-shadow:   -8px -2px 16px #000;
}

.links {
   display: none;
}

.parpadeo {
   background: white;
}

.cajaTextos {
   bottom: 0;
   width: 100%;
   border-top: 1px solid black;
}

.cajaTextos * {
   font-family: "Trebuchet MS", Arial;
   font-size: 13px;
   color: white;
   text-transform: uppercase;
}

.fondoOscuro {
   background: #000;
   opacity: 0.45;
   filter: Alpha(opacity=45);
}

.titular, .subtitular {
   padding-left: 15px;
}

.titular {
   line-height: 50px;
}

.subtitular {
   line-height: 15px;
   font-size: 45px;
}

.titulo {
   bottom: 0;
   left: 41px;
}


Y con todos los estilos definidos, repasemos el resultado que hemos visto al principio. Sliders finales cargados.

Bueno espero que estéis contentos con el resultado y sobre todo que sea útil para alguien.

Un saludo a todos.

Cristalab y Mejorando.la te traen el Curso Profesional de Node.js y Javascript. Online, avanzado, con diploma de certificación y clases en vivo.

Publica tu comentario

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate