Tecnología y Coaching para Emprendedores

jQuery: Crea un Efecto de Organización Tipo Pinterest

pinterest

Pinterest es una red social que no hace más que crecer gracias a sus fantásticas funcionalidades. Uno de los efectos más valorados es la facilidad para organizar los tableros arrastrándolos, y como desarrolladores web es algo muy interesante para aplicar a nuestros próximos proyectos. En este artículo verás cómo crear un efecto de mezcla de tableros tipo Pinterest con jQuery que deseo te sirva para tus próximos trabajos de desarrollo web.

La teoría

Antes de sumergirnos de pleno en el código, necesitamos tener un poco clara la teoría. Es un sistema relativamente sencillo una vez pillas la idea. Para hacer que este sistema funcione hemos de saber tres cosas:

  • El ancho del contendedor
  • El ancho del bloque
  • El margen entre ambos

Con estos datos podemos calcular el número de bloques que pueden caber dentro del contenedor. Esto nos da el número de columnas para esta instancia, así, en el cuadro de arriba el número de columnas es de 4.

De este modo podemos crear una matriz o array con cuatro valores. El valor por defecto es el margen entre los bloques. Este array se utiliza para guardar las alturas de cada columna en cada momento. Por ejemplo, la altura del bloque de 120px se añade a la columna 1, con lo que el valor del array se incrementa en 120.

También utilizamos el número índice de cada valor para calcular la posición desde la izquierda de la pantalla. Aquí tenemos el esquema de flujo de como funcionará esta función. Esta función se ejecutará cada vez que la ventana se redimensione, de manera que los bloques se puedan autoajustar.

Estructura HTML

<body>
    <!-- Crea múltiples versiones de este bloque con diferente contenido -->
    <div class="block">
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut in dui velit. Curabitur purus odio, adipiscing ut vehicula at, pulvinar eu justo. Suspendisse potenti. Suspendisse dictum massa non mi posuere ac convallis nisi pellentesque. Morbi posuere mauris elementum metus intlla faProin et malesuada arcu. Quisque sed nulla odio, at interdum diam. Proin mollis, dui eget tristique dictum, diam purus hendrerit urna, lacinia interdum sem justo sit amet justo. Morbi a neque ut velit tempus auctor. Sed condimentum dolor in est facilisis id malesuad</p>
    </div>
</body>

El CSS

.block {
    position: absolute;
    background: #eee;
    padding: 20px;
    width: 300px;
    border: 1px solid #ddd;
}

El jQuery

var colCount = 0;
var colWidth = 0;
var margin = 20;
var windowWidth = 0;
var blocks = [];

function positionBlocks() {
    windowWidth = $(window).width();
    colWidth = $('.block').outerWidth();
    colCount = Math.floor(windowWidth/(colWidth+margin));
    for(var i=0;i<colCount;i++) {
        blocks.push(margin);
    }
    $('.block').each(function(){
        var min = Array.min(blocks);
        var index = $.inArray(min, blocks);
        var leftPos = margin+(index*(colWidth+margin));
        $(this).css({
            'left':leftPos+'px',
            'top':min+'px'
        });
        blocks[index] = min+block.outerHeight()+margin;
    });
}
// Función para obtener el valor mínimo del array
Array.min = function(array) {
    return Math.min.apply(Math, array);
};

Hemos empezado por por definir algunas variables globales, a la vez que el array que almacenará las alturas de cada columna. Se ha creado una función llamada positionBlocks.

Necesitamos también tener el ancho del contenedor, en este caso es la ventana, junto con el ancho del bloque. Estos valores los hemos guardado en las variables windowWidth y colWidth.

La variable colCount calcula el número de columnas que caben en el ancho de ventana actual. Aquí también añadimos el margen y entonces dividimos el ancho de la ventana windowWidth por la suma del ancho de la columna más el margen colWidth+margin. El resultado se redondea de modo que nos dé un número entero.

Seguidamente hacemos un loop que se ejecuta tantas veces como columnas caben en la ventana. Así, cada vez que se ejecuta el loop, se guarda un nuevo valor en el array, que inicialmente estaba vacío. Si, por ejemplo, caben cuatro columnas en la ventana, en el array aparecerán cuatro valores.

Posicionando los bloques

Ahora que ya tenemos creados los bloques, así como el array con las alturas iniciales de cada columna, es cuando podemos calcular la posición de cada bloque.

Primero de todo, hacemos un loop en cada elemento de la página que tenga la clase block. Se da valor entonces a dos variables: min y index. La variable min representa el valor más bajo en el array, que es a la vez la columna con la altura más pequeña. La variable index representa el número índice de este valor. Esto nos da el número de columnas que necesitamos para ubicar nuestros bloques.

Veamos un ejemplo. Si el array contiene los valores 20, 95 y 75, el valor de min será de 20 y el de index será de cero. Entonces procedemos a calcular a partir de estos valores la posición de la ventana desde la izquierda.


    $('.block').each(function(){
        var min = Array.min(blocks);
        var index = $.inArray(min, blocks);

Para calcular la posición a la izquierda, añadimos el ancho de cada columna al margen, multiplicamos este valor por nuestro índice y finalmente añadimos otro margen. Este margen adicional actúa como padding para la siguiente columna de bloques. Se aplica entonces el nuevo valor de leftPos así como el de min.

La última línea simplemente actualiza el array con la nueva altura. Esta altura se calcula sumando la altura del bloque actual y del margen al valor existente en el array de ese índice.


var leftPos = margin+(index*(colWidth+margin));
$(this).css({
    'left':leftPos+'px',
    'top':min+'px'
});
blocks[index] = min+$(this).outerHeight()+margin; 

Finalmente, añadimos un escuchador al código, de modo que cuando la ventana se redimensione se ejecute de nuevo la función:


$(window).load(function() {
    positionBlocks ();
    $(window).resize(positionBlocks);
});

Con esto hemos creado con jQuery un sistema de organización de tableros muy parecido al de Pinterest.

Últimos retoques

Para tener un mejor efecto visual, podemos añadir este código CSS3 a nuestra clase block:

-webkit-transition: all 1s ease-in-out;
-moz-transition: all 1s ease-in-out;
-o-transition: all 1s ease-in-out;
-ms-transition: all 1s ease-in-out;
transition: all 1s ease-in-out;

Con este código veremos que los bloques CSS cambian, haciendo que salten. Lo que podemos hacer para que esto no pase, así como para que el código sea más compatible (CSS3 no funciona bien en todos los navegadores), podemos utilizar este otro código:


$(this).css({
    'left':leftPos+'px',
    'top':min+'px'
});

$(this).stop().animate({
    'left':leftPos+'px',
    'top':min+'px'
});



Finalmente, añadimos el CSS del footer:



Añade este código al final de la función:


// Define la posición del footer
var maxHeight = Array.max(blocks);
$('#footer').css({'top':maxHeight+150});

Los 150px extra añaden padding a la parte inferior de los bloques. Finalmente, añade esta función que define el valor mínimo del array:

Array.min = function(array) {
    return Math.min.apply(Math, array);
};

Conclusión

Este artículo es una traducción del artículo publicado por Ben Holland, podéis ver aquí la demo. La principal diferencia con Pinterest es que en este código no aparece la opción de mover los tableros de manera manual, sino que se reorganizan de manera automática cuando redimensionamos la ventana. Aún así, me parece una idea excelente para los próximos proyectos de desarrollo web que lleve a cabo.


Comentarios

  • Oliver Cortinas

    no anda la demo trato de arrastrar algo y no responde

  • FernandoRengifo

    genial lo voy a aplicar, por que ya entendi como funciona, pero solo reubicare ya que tengo un contenedor con una anchura fija. buena la logica. excelente eso sale cuando se sientan los programadores creativos.! gracias por publicarlo!

  • opeto

    Como puedo poner un máximo a Js, es que solo quiero que me ocupe un 75% de la ventana (navegador)

    • Sandra

      El máximo lo tienes que poner por CSS, limitando el espacio del contenedor.

  • opeto

    Pues yo lo he intentado y no me salia de toda forma hoy o mañana lo mirare otra vez y muchas gracias por contestar y por el blog

  • opeto

    Gracias Sandra pero lo he intentado poner de toda forma y siempre me sale el ancho de del navegador me podrías poner lo que yo tengo que poner para ver en que me estoy equivocando, yo quiero que sea un 75% del navegador y que flote a la derecha. y por cierto el tutorial muy bueno porque me salio a la primera lo único malo es que no sé o no puedo modificar el ancho.

    • Sandra

      Debería mirar el código pero en principio deberías crear un contenedor al que supuestamente llamaremos pinterest y en css definirlo así:

      #main {
      width: 100%;
      }

      #pinterest {
      width: 75%;
      float: right;
      }

      Si no te funciona es posible que tengas que aplicar antes un clear:both.

      Y en el HTML:

      < div id="main">

  • opeto

    Perdona por ser tan pesado pero solo era para decirle que le he mandado un correo al Twitter, cuando puedas lo mira y muchas gracias

  • Fernando

    Excelente, estoy intentando hacer algo muy parecido a Pinterest ya que me asignaron como proyecto en una materia de la universidad y esto me ha sido de bastante ayuda, ya que no sabia como hacer precisamente esto, estoy un poco novato en el área, de hecho ya intente aplicarlo y no me ha funcionado bien pero allí voy, poco a poco…

  • Joryx

    El código tal cual lo pones no funciona, necesita algunas correcciones.
    Los que quieran ejecutarlo, deben copiar el código del sitio original y luego hacerle las correcciones que agrega Sandra en este artículo que ayudan mucho.

    Para empezar en el se elimina el onload porque ya lo cargariamos desde jquery con window.load. El codigo que pones acá tiene unos problemillas, aqui abajo el que si sirve:

    $(window).load(function() {
    setupBlocks();
    $(window).resize(positionBlocks);
    });

    Ahora, uno de mis principales problemas era el footer, tal cual lo pones debería funcionar pero no sirve. Le hice las siguientes correcciones:

    var maxHeight = Array.max(blocks);
    $(‘#contenedor’).css({‘height’:maxHeight+50});

    Donde contenedor es el div principal. Y también faltaría crear la función MAX, y es la siguiente:

    Array.max = function(array) {
    return Math.max.apply(Math, array);
    };