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).
- Ancho del contenedor principal.
- Alto de las imágenes (el contenedor principal también medirá esto de alto)
- Ancho de las imágenes.
- Altura de la caja de textos.
- Ruta de la carpeta de las imágenes.
- 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
- Nombre de la imagen.
¡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.
- 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
- DIV IMAGEN
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.
¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.
Por Mariux el 06 de Marzo de 2012
será que tenes el texto en limpio?
saludos
Por Abducted el 06 de Marzo de 2012
Un saludo.
Por DriverOp el 06 de Marzo de 2012
Por otro lado no veo el problema con los colores en el texto.
Por Abducted el 06 de Marzo de 2012
El código está completo y funcionando en la url con los ejemplos, es exactamente el mismo js que el del tuto, a excepción de que crea un segundo contenedor en el body y llama 2 veces a la función para crear los 2 sliders, y el css es un pelín más completo.
Lamentablemente he encontrado un par de faltas de ortografía al revisarlo por última vez ya publicado, y alguna palabra que me he comido. Lo he revisado un millón de veces, antes de publicarlo, pero no he usado ningún corrector de texto pues me gusta leer unas cuantas veces lo que escribo, para además de buscar las faltas, revisar la estructura de las frases, pero era tan extenso que alguna se me ha escapado, así que perdonad si encontráis alguna falta de ortografía.
De nuevo gracias DriverOp.
Un saludo a todos.
Por Mariux el 06 de Marzo de 2012
no te preocupes por la ortografía, yo lo edito entero luego
para la próxima vez recuerda no usar color. gracias
editado-mx
Por Abducted el 06 de Marzo de 2012
Y perdona por haberte molestado, que te has fijado más en esas nimiedades que en el contenido de lo que estoy publicando.
Un saludo.
Por HtrMancera el 06 de Marzo de 2012
Aunque lo podrías mejorar un poco proporcionando un poco mas de versatilidad, por ejemplo si en vez de ajustar las dimensiones manualmente pusieras algo como:
Código :
es solo un ejemplo de como podrías mejorarlo para que se ajuste dinámicamente según las imágenes que utilices.
saludos.
Por Arturon el 06 de Marzo de 2012
Por Abducted el 06 de Marzo de 2012
HtrMancera :
Código :
es solo un ejemplo de como podrías mejorarlo para que se ajuste dinámicamente según las imágenes que utilices.
Tienes razón así sería más versátil, e incluso podríamos cargar imágenes de diferente ancho y calcular posiciones diferentes para cada imagen. Y si no lo he hecho así es por una razón, y es que si un usuario hiciera un slider demasiado grande (que implica imágenes grandes) o le cargase imágenes demasiado pesadas, sería muy probable que en el momento en que la función fuera a obtener el ancho o alto de la imagen, esta no estuviera totalmente cargada y obtendría un undefined indeseado.
No obstante, tengo en mente ir ampliando este tutorial con otros que lo vallan implementado, para el primero por ejemplo tengo pensado que el slider se pueda generar de 3 tipos, los movimientos como se ven ahora, que se vea una sóla imagen en el slider y con botones se valla cambiado de una imagen a otra y otra igual que esta última, pero que la imagen que entre, entre aleatoriamente desde la derecha, izquierda, arriba o abajo. Cuando me ponga a desarrollarlo, le meteré un loader a las imágenes para evitar el fallo que te he comentado o precalculare sus medidas con un php, y pondré esas 3 líneas como me aconsejas.
Un saludo.
Por Abducted el 06 de Marzo de 2012
Eso sí quien lo quiera ver con sus colores originales, y con los códigos tabulados (sin duda UN GRAN AVANCE PARA LOS QUE MENOS SABEN), que pase por este post, que lo pondré es su formato original, que a nadie le cabrá duda de que es 2000 veces mejor que el actual.
Tutorial en su formato original.
Por Abducted el 06 de Marzo de 2012
Por Mariux el 06 de Marzo de 2012
deja que le consulto a maik o a freddie.
Por Abducted el 06 de Marzo de 2012
Menos mal.
OK, la versión que he creado en el foro de jS tiene los textos corregidos, además de ni una falta de ortografía (que ya le he pasado el corrector del outlook a mano palabra por palabra). Además he corregido los errores gramaticales que había y le he añadido alguna palabra que se me había escapado y un par de pequeñas frases.
Por favor coge ese, que creo está perfecto (gramaticalmente hablando).
Un saludo,
Muchas gracias.
Por Abducted el 06 de Marzo de 2012
Por favorrrrrr
¿Qué me dices?
Por Freddie el 06 de Marzo de 2012
Por otro lado, el problema del color en los textos es de confusión con enlaces que no existen y de impresión. Las negritas están OK, el exceso de colores en texto plano es un problema. Usa negritas sin problema.
Por Abducted el 07 de Marzo de 2012
Mariux, voy a quitarle los colores a la ultima versión, crear los 5 links de las palabras que he mencionado más arriba y cambiaré las url's de las imágenes por las nuevas de Cristalab.
En cuanto lo tenga te lo posteo aquí como post y te encargas de borrarlo si me haces el favor, voy a tardar un rato pues voy a reemplazar las negritas-cursivas, por sólo negritas y sólo cursivas.
Un saludo.
Por Mariux el 07 de Marzo de 2012
Abducted :
Mariux, voy a quitarle los colores a la ultima versión, crear los 5 links de las palabras que he mencionado más arriba y cambiaré las url's de las imágenes por las nuevas de Cristalab.
En cuanto lo tenga te lo posteo aquí como post y te encargas de borrarlo si me haces el favor, voy a tardar un rato pues voy a reemplazar las negritas-cursivas, por sólo negritas y sólo cursivas.
Un saludo.
no te preocupes, esta todo bajo control acá. yo me encargo
Gracias por aportar! mañana estará en portada bien editado
Por Abducted el 07 de Marzo de 2012
Y perdonad todos mi cabezonería, a veces me pierde.
Recuerda por favor coger la fuente del último que he publicado en el foro de Js, que es la versión definitiva, el link está un poco más arriba.
Un saludo.
Por Abducted el 07 de Marzo de 2012
¿Por qué no lo usaste?.
¿Estamos a tiempo de remediarlo?
Gracias Mariux,
Un saludo.
Por Erledie el 07 de Marzo de 2012
Por alejo8591 el 07 de Marzo de 2012
Por The Fricky! el 07 de Marzo de 2012
Yo creo que sí hay un par de cosas que pueden hacerse para que sea de mayor utilidad, por ejemplo: convertirlo en un plugin, posiblemente cambiar algunos elementos de HTML (en vista de usar menos o usar elementos más semánticos) y cosas así.
¿Por qué no te creas una cuenta en Github (si no tienes una ya) y lo subes allí? Luego puedes publicar la url y podemos presentarte las propuestas para mejorarlo (o al menos lo que cada quien crea que signifique eso)?
En cualquier caso, sobre todo en el cálculo de los elementos, felicitaciones, está muy bien hecho
Por Abducted el 07 de Marzo de 2012
Abducted :
¿Estamos a tiempo de remediarlo?
Erledie-blog :
alejo8591 :
The Fricky! :
Y gracias a todos por vuestro tiempo, que también habéis invertido en leerlo.
Dicho esto no contestaré más post de agradecimientos para que esto no se convierta en una cosa abigarrada de posts míos.
Gracias a todos de antemano y un saludo.
Por Kinduff el 07 de Marzo de 2012
Felicidades, quizás lo tome prestado para un proyecto, te dejo créditos en el source.
Saludos.
Por @ohcan35 el 07 de Marzo de 2012
Por jordano_p el 07 de Marzo de 2012
Por Dorian Rallon el 08 de Marzo de 2012
Por Abducted el 08 de Marzo de 2012
Abducted :
A pesar de que funciona sin fallos, esta línea que está repetida en diversas ocasiones a lo largo del tuto, y va dentro de la función reiniciar listeners() en la función over, está definiendo una variable y le falta una coma "," al final en todas las apariciones.
Lo siento, un saludo.
Por kineticfront el 14 de Marzo de 2012
Por Abducted el 14 de Marzo de 2012
Un saludo.
Por JoseAlejandro_Realza el 17 de Marzo de 2012
Por Mariux el 19 de Marzo de 2012
JoseAlejandro_Realza :
si. postealo en tutoriales y si está bien redactado, organizado y tiene buena ortografía (además de la calidad propia del tutorial) pasa a portada.
podes hostear las imágenes que uses en http://l4c.me/ que es de cristalab.
Por lordoracle el 31 de Marzo de 2012
Por EVA el 04 de Julio de 2012
Por Bossco el 17 de Agosto de 2012
Por luis el 04 de Octubre de 2012
Por Pablo Roman el 06 de Octubre de 2012
Por Pedro el 08 de Noviembre de 2012
Por xpumax el 16 de Enero de 2013
Por Jana el 14 de Mayo de 2013
podrias colgar tu codigo para guiarme.
gracias
Por Jana el 14 de Mayo de 2013
saludos
Por Areniah el 04 de Septiembre de 2013
Por Bry el 09 de Noviembre de 2013
Por Puta el 30 de Abril de 2015
Por el 01 de Agosto de 2016
Por José el 31 de Enero de 2018
classroom rental space
training rrom rental in singapore
training rooms in singapore
seminar room rental in singapore
indoor team building activities
corporate team building games singapore
team bonding in singapore
team building activities singapore
team building games singapore
10 soft skills you need
administrative office procedures
administrative support courses
adult learning mental skills
adult learning physical skills
anger management courses in singapore
appreciative inquiry courses
archiving and records management
assertiveness and self confidence
attention management courses
basic bookkeeping courses
being a likeable boss
body language basics courses
budgets and financial reports
business acumen courses
business ethics courses
business etiquette courses in singapore
business succession planning courses
business writing courses in singapore
call center training courses
change management courses in singapore
coaching and mentoring courses
coaching sales people courses
collaborative business writing
communication strategies courses
conducting annual employee reviews
conflict resolution courses
contact center training courses
contract management courses in singapore
creating a great webinar
creative problem solving courses
crisis management courses
critical thinking courses in singapore
customer service courses in singapore
customer support courses
cyber security courses in singapore
delivering constructive criticism
developing a lunch and learn
developing corporate behavior
developing creativity courses
developing new managers
digital citizenship courses
emotional intelligence courses
employee motivation courses
employee on boarding courses
employee recognition courses
employee recruitment courses
employee termination processes
entrepreneurship courses in singapore
event planning courses in singapore
executive and personal assistants
facilitation skills courses
generation gaps courses
goal setting and getting things done
handling a difficult customer
health and wellness at work courses
high performance teams inside the company
high performance teams remote work force
hiring strategies courses
human resource management courses in singapore
improving mindfulness
improving self awareness
increasing your happiness
internet marketing fundamentals courses
interpersonal skills courses
job search skills courses
knowledge management courses in singapore
leadership and influence courses
lean process and six sigma
life coaching essentials courses
manager management courses
managing personal finances courses
managing work place anxiety
marketing basics courses
measuring results from training
media and public relations courses
meeting management courses
middle manager courses
millennial on boarding courses
m learning essentials
motivating your sales team
multi level marketing courses
negotiation skills courses
networking outside the company
networking within the company
office politics for managers
organizational skills courses
overcoming sales objections
performance management courses
personal branding courses in singapore
personal productivity courses
presentation skills courses in singapore
project management courses in singapore
proposal writing courses
prospecting and lead generation
public speaking courses in singapore
risk assessment and management courses
safety in the work place courses
sales fundamentals courses
sales training courses in singapore
servant leadership courses
it courses in singapore
microsoft training singapore
corporate training in singapore
corporate sgx
social intelligence courses
social learning courses
social media in the work place
social media marketing courses in singapore
soft skills courses in singapore
stress management courses in singapore
supervising others
supply chain management courses
taking initiative courses
talent management courses
team building for managers
team building through chemistry
teamwork and team building
telephone etiquette courses
telework and telecommuting
time management courses in singapore
trade show staff training
train the trainer courses
virtual team building and management
women in leadership courses
work life balance courses in singapore
work place diversity courses
work place harassment courses
work place violence courses
sancy suraj
sancy suraj
sancy suraj
sancy suraj
sancy suraj
sancy suraj
sancy suraj
sancy suraj
[url=https://books.google.com.sg/books?id=1QykBQAAQBAJ&pg=PT362&lpg=PT362&dq=%22sancy+suraj+singh%22&source=bl&ots=E86QDyrLG2&sig=H-6a_YH-kTWaZWTfPSr1xfm4BOs&hl=en&sa=X&ved=0ahUKEwi3_56hhubVAhWJLo8KHcxTBxQ4ChDoAQgjMAA#v=onepage&q =% 22sancy% 20suraj% 20singh% 22 & f = false]sancy suraj[/url]
sancy suraj
longest colour sequence memorised
sancy suraj
longest colour sequence memorised
memory training course
memory training course
memory training course
memory training course
memory training course
memory training course
memory training course
memory training course
memory training course
memory training course
memory training course
memory training course
memory training course
memory training course
memory training course
memory training course
memory training course
memory training course
lunch talks
lunch talks
memory training course
memory training course
cabin crew
online memory course
memory training course
memory training course
memory training course
memory training course
memory training course
speed reading
tuition
tuition
tuition
tuition
tuition
tuition
tuition
tuition
tuition
tuition
geography tuition for secondary school students in singapore
geography tuition for secondary school students singapore
geography tuition for secondary school students singapore
geography tuition for secondary school students singapore
geography tuition for secondary school students singapore
secondary geography tuition in singapore
history tuition for secondary school students in singapore
social studies tuition for secondary school students in singapore
psle english tuition in singapore
psle science tuition in singapore
secondary 1 chemistry tuition in singapore
secondary 1 physics tuition in singapore
school holiday workshops courses for students in singapore
school holidays activitie in singapore
school holidays activitie in singapore
[url=http://umonictuitionadvantage.com/2017-november-school-holidays-activities-programmes-workshop-courses-camps-for- students-kids-in-singapore/]school holidays activitie in singapore[/url]
school holidays activitie in singapore
study skills
study skills
study skills workshops course in singapore
study skills workshops course in singapore
speed reading
speed reading
tuition
tuition
tuition
tuition
tuition
tuition
tuition
tuition
tuition
tuition
tuition
tuition
tuition
tuition
tuition
tuition[
tuition[
tuition[
tuition[
tuition[
tuition[
tuition[
tuition[
student courses
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
corporate training
lunch talk
lunch talk
lunch talk
lunch talk
lunch talk
lunch talk
lunch talk
lunch talk
lunch talk
lunch talk
lunch talk
lunch talk
corporate lunch talk
corporate lunch talk
corporate lunch talk
corporate lunch talk
corporate lunch talk
corporate lunch talk
corporate lunch talk
corporate lunch talk
corporate lunch talk
corporate lunch talk
corporate lunch talk
corporate lunch talk
corporate lunch talk
corporate lunch talk
team building
team building ideas
team building activities
unique team building
team building
corporate training in singapore
corporate training courses
corporate training courses
corporate training courses
corporate training courses
corporate health talk
corporate health talk
corporate health talk
lunch and learn talk
workplace lunch and learn
corporate training companies in singapore
training companies in singapore
emcee
emcee
health talks
soft skills training course
corporate training providers
professional development courses
training and development courses
short courses in singapore
corporate training courses in singapore
corporate training courses
corporate training in singapore
school holiday workshops courses for students in singapore
business students memory course in singapore
business students memory improvement workshop in singapore
memory improvement course for business students
memory improvement course for business students
business students memory improvement course
business students memory course in singapore
corporate health talks singapore
corporate health talks in singapore
corporate health talk singapore
corporate health talk in singapore
corporate health talks singapore
corporate health talks singapore
finance students memory training course in singapore
finance students memory training course in singapore
finance students memory training course in singapore
memory training courses for finance students in singapore
memory training courses for finance students in singapore
memory improvement courses for finance students in singapore
pinnacle minds
memory course
study skills
speed reading
memory training
school holiday
lunch and learn
march school holidays workshops
march school holidays workshops
march school holidays workshops
march school holidays workshops
march school holidays workshops
june school holidays workshops
june school holidays workshops
june school holidays workshops
june school holidays workshops
september 2018 school holidays workshops
september 2018 school holidays workshops
september 2018 school holidays workshops
september 2018 school holidays workshops
november 2018 school holidays workshops
november 2018 school holidays workshops
november 2018 school holidays workshops
november 2018 school holidays workshops
december 2018 school holidays workshops
december 2018 school holidays workshops
december 2018 school holidays workshops
december 2018 school holidays workshops
top 10 soft skills you need training course
administrative office procedures training course
administrative support training course
anger management training course
appreciative inquiry training course
archiving and records management training course
archiving and records management training course
self confidence and assertiveness training course
improving your attention management training course
bacis bookkeeping training course
being a likeable boss training course
body language training course
budgets and-financial reports training course
business acumen training course
business ethics training course
business etiquette training course
business succession planning training course
business writing training course
call centre training course
change management training course
civility in the workplace training course
coaching and mentoring training course
coaching salespeople training course
collaborative business writing training course
communication strategies training course
conducting annual employee reviews training course
conflict resolution training course
contact centre training course
contract management training course
creating a great webinar training course
creative problem solving training course
crisis management training course
critical thinking training course
customer service training course
customer support training course
cyber security training course
delivering constructive criticism training course
developing lunch and learn training course
developing corporate behavior training course
developing creativity training course
developing new managers training course
digital citizenship training course
emotional intelligence training course
employee motivation training course
employee onboarding training course
employee recognition training course
employee recruitment training course
employee termination processes training course
entrepreneurship training course
event planning training course
executive and personal assistants training course
facilitation skills training course
generation gaps training course
goal setting and getting things done training course
handling a difficult customer training course
health and wellness at work training course
high performance teams inside the company training course
high performance teams remote workforce training course
hiring strategies training course
human resource management training course
improving mindfulness training course
improving self awareness training course
internet marketing fundamentals training course
interpersonal skills training course
job search skills training course
knowledge management training course
leadership and influence training course
lean process and six sigma training course
life coaching essentials training course
manager management training course
managing personal finances training course
managing workplace anxiety training course
marketing basics training course
measuring results from training course
media and public relations training course
meeting management training course
middle manager training course
millennial onboarding training course
mlearning essentials training course
motivating your sales team training course
negotiation skills training course
networking outside the company training course
networking within the company training course
office politics for managers training course
organizational skills training course
overcoming sales objections training course
performance management training course
personal branding training course
personal productivity training course
presentation skills training course
project management training course
proposal writing training course
prospecting and lead generation training course
public speaking training course
risk assessment and management training course
safety in the workplace training course
sales fundamentals training course
servant leadership training course
social intelligence training course
social learning training course
social media in the workplace training course
social media marketing training course
stress management training course
supervising others training course
supply chain management training course
taking initiative training course
talent management training course
team building for managers training course
team building through chemistry training course
teamwork and team building training course
telephone etiquette training course
telework and telecommuting training course
time management training course
top 10 sales secrets training course
trade show staff training course
train the trainer training course
virtual team building and management training course
women in leadership training course
work life balance training course
workplace diversity training course
workplace harassment training course
workplace violence training course
half day memory improvement courses workshops
speed reading courses workshops in singapore
10 soft skills you need corporate training course in singapore
administrative office procedures corporate training course in singapore
administrative support corporate training course in singapore
anger management corporate training course in singapore
appreciative inquiry corporate training course in singapore
archiving and records management corporate training course in singapore
self confidence assertiveness corporate training course in singapore
improving your attention management corporate training course in singapore
basic bookkeeping corporate training course in singapore
being a likeable boss corporate training course in singapore
body language basics corporate training course in singapore
budgets and financial reports corporate training course in singapore
business acumen corporate training course in singapore
business ethics corporate training course in singapore
business etiquette corporate training course in singapore
business succession planning corporate training course in singapore
business writing corporate training course in singapore
call center corporate training course in singapore
change management corporate training course in singapore
civility in the workplace corporate training course in singapore
coaching and mentoring corporate training course in singapore
coaching salespeople corporate training course in singapore
collaborative business writing corporate training course in singapore
communication strategies corporate training course in singapore
conducting annual employee reviews corporate training course in singapore
conflict resolution corporate training course in singapore
contact center corporate training course in singapore
contract management corporate training course in singapore
creating a great webinar corporate training course in singapore
creative problem solving corporate training course in singapore
crisis-management corporate training course in singapore
critical thinking corporate training course in singapore
customer service corporate training course in singapore
customer support corporate training course in singapore
cyber security corporate training course in singapore
delivering constructive criticism corporate training course in singapore
developing a lunch and learn corporate training course in singapore
developing corporate behavior corporate training course in singapore
developing creativity corporate training course in singapore
developing new managers corporate training course in singapore
digital citizenship corporate training course in singapore
emotional intelligence corporate training course in singapore
employee motivation corporate training course in singapore
employee onboarding corporate training course in singapore
employee recognition corporate training course in singapore
employee recruitment corporate training course in singapore
employee termination processes corporate training course in singapore
entrepreneurship training course in singapore
event planning corporate training course in singapore
executive and personal assistants corporate training course in singapore
facilitation skills corporate training course in singapore
generation gaps corporate training course in singapore
goal setting and getting things done corporate training course in singapore
handling a difficult customer corporate training course in singapore
health and wellness at work corporate training course in singapore
high performance teams inside the company corporate training course in singapore
high performance teams remote workforce corporate training course in singapore
hiring strategies corporate training course in singapore
human resource management corporate training course in singapore
improving mindfulness corporate training course in singapore
improving self awareness corporate training course in singapore
increasing your happiness corporate training course in singapore
internet marketing fundamentals corporate training course in singapore
interpersonal skills corporate training course in singapore
job search skills corporate training course in singapore
knowledge management corporate training course in singapore
leadership and influence corporate training course in singapore
lean process and six sigma corporate training course in singapore
life coaching essentials corporate training course in singapore
manager management corporate training course in singapore
managing personal finances corporate training course in singapore
marketing basics corporate training course in singapore
measuring results from corporate training course in singapore
media and public relations corporate training course in singapore
meeting management corporate training course in singapore
middle manager corporate training course in singapore
millennial onboarding corporate training course in singapore
mlearning essentials corporate training course in singapore
motivating your sales team corporate training course in singapore
negotiation skills corporate training course in singapore
networking outside the company corporate training course in singapore
networking within the company corporate training course in singapore
office politics for managers corporate training course in singapore
organizational skills corporate training course in singapore
overcoming sales objections corporate training course in singapore
performance management corporate training course in singapore
personal branding corporate training course in singapore
personal productivity corporate training course in singapore
presentation skills corporate training course in singapore
project management corporate training course in singapore
proposal writing corporate training course in singapore
prospecting and lead generation corporate training course in singapore
public speaking corporate training course in singapore
[url