Drag and drop con JavaScript y Draggable

Crear una funcionalidad de tipo drag and drop, es posible con JavaScript y la librería Draggable. Sin necesidad de complicarse la vida con eventos nativos del navegador.

https://shopify.github.io/draggable/ “A lightweight, responsive, modern drag & drop library”

See the Pen Crear Drag&Drop con JavaScript y @Shopify/Draggable by Danivalldo (@Danivalldo) on CodePen.

¿Quieres aprender a hacer este ejercicio? Saltar directamente al tutorial

«Drag and drop» de la mano de Shopify

Draggable ofrece la posibilidad de implementar funcionalidades “drag and drop” a través de su librería modular.

Inicialmente, ésta librería fue creada por el equipo de desarrollo de Shopify, una conocida plataforma de creación de e-commerce. Sin embargo, el repositorio se entregó a otros colaboradores y se ha convertido en un proyecto open source mantenido por más de cuarenta desarrolladores. Actualmente cuenta con una valoración de más de 15.600 estrellas en Github.

La versión completa y minificada de Draggable ocupa unos 11kb. Y se puede integrar en cualquier proyecto mediante el comando “npm install @shopify/draggable –save” o si se prefiere, con “yarn add @shopify/draggable”.

La landing page llama especialmente la atención por su colorido diseño. Donde rápidamente se puede ver en acción la propia librería a través de varios ejemplos.

Mediante una sencilla interacción de tipo “drag and drop”, podemos desplazar y reordenar los cubos que se encuentran dispuestos en una perspectiva isométrica.

Entre las características que ofrece Draggable, podemos ver ejemplos donde los elementos interactivos pueden ser dispuestos de una zona “A” a una zona “B” específica. También vemos otro ejemplo donde pueden ser intercambiados por otros elementos interactivos o incluso reordenados dentro de una lista de múltiples ítems.

Adicionalmente, Draggable permite incorporar plugins que extienden sus funcionalidades básicas, para agregar nuevas características, como por ejemplo la detección de colisiones entre elementos arrastrados.

¿Cómo implementar Draggable?

Daremos un rápido vistazo a la documentación de la API. Allí se puede ver que la forma de implementar esta librería, es instanciando de la clase Draggable. La clase recibe como primer argumento el elemento HTML que actúa como contenedor principal, y un objeto JSON con múltiples opciones de inicialización. Por ejemplo el selector css para determinar a qué elementos del contenedor se aplica la interacción “drag and drop”.

Una vez instanciado el objeto, la librería se encarga de implementar todas funcionalidades de “drag and drop” a bajo nivel. Podemos detectar los distintos cambios de estado mediante eventos capturados por el método “on”. Ese método, va seguido por el argumento de tipo “string” y una función de callback. De forma muy similar a como se capturan eventos en la API del DOM mediante el método addEventListener.

A continuación, implementaremos un sencillo ejercicio. Veremos cómo integrar la librería para crear un efecto Drag and Drop, con código JavaScript y la librería Draggable.

Crar un ejercicio Drag and drop con JavaScript y Draggable

Empezaremos preparando el siguiente “layout” con HTML. Éste va a contener un conjunto de celdas con la clase CSS “BlockWrapper–isDropzone”. Cada una de estas, va a indicarle a la librería que nuestra pieza puede ser soltada en ese contenedor. Una de las celdas tiene la clase “draggable-dropzone–occupied”, ya que va a ser donde posicionamos nuestra pieza de inicio.

  <body>
    <div class="perspective-wrapper">
      <div id="chess-board" class="chess-board">
        <div class="chess-cell cell-white BlockWrapper--isDropzone"></div>
        <div class="chess-cell cell-black BlockWrapper--isDropzone"></div>
        <div class="chess-cell cell-white BlockWrapper--isDropzone"></div>
        <div class="chess-cell cell-black BlockWrapper--isDropzone"></div>
        <div class="chess-cell cell-white BlockWrapper--isDropzone"></div>
        <div class="chess-cell cell-black BlockWrapper--isDropzone"></div>
        <div class="chess-cell cell-white BlockWrapper--isDropzone draggable-dropzone--occupied">
          <div class="knight-piece Block--isDraggable">
            <img src="imgs/knight.svg" alt="">
          </div>
        </div>
        <div class="chess-cell cell-black BlockWrapper--isDropzone"></div>
        <div class="chess-cell cell-white BlockWrapper--isDropzone"></div>
      </div>
    </div>
  </body>

El elemento del DOM que tiene la clase “Block–isDraggable” va a actuar de pieza “draggeable”. Por ese motivo le añadiremos una imagen de un caballo de ajedrez.

<div class="chess-cell cell-white BlockWrapper--isDropzone draggable-dropzone--occupied">
  <div class="knight-piece Block--isDraggable">
    <img src="imgs/knight.svg" alt="">
  </div>
</div>

Para darle estilos al ejemplo, utilizaremos CSS. En este código vamos a posicionar los elementos del DOM que actúan de celdas, sobre un tablero de fondo.

$chessboardSize: 400px;
$chellWidth: ($chessboardSize / 3) - 40px;
$chellHeight: 50px;
html,
body {
  height: 100%;
}
body {
  margin: 0;
  padding: 0;
  background-color: #ff7a2c;
  &.draggable--is-dragging {
    .perspective-wrapper {
      .chess-board {
        .chess-cell {
          .knight-piece {
            cursor: grabbing;
          }
        }
      }
    }
  }
}
.perspective-wrapper {
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: flex-end;
  .chess-board {
    margin: 0 auto;
    width: $chessboardSize;
    height: 70%;
    background-image: url("../imgs/board.svg");
    background-size: cover;
    background-position: center top;
    position: relative;
    .chess-cell {
      width: $chellWidth;
      height: $chellHeight;
      box-sizing: border-box;
      position: absolute;
      &:nth-child(1) {
        top: 0;
        left: 50%;
        margin-left: -$chellWidth/2;
      }
      &:nth-child(2) {
        top: $chellHeight * 1;
        left: $chellWidth;
      }
      &:nth-child(3) {
        top: $chellHeight * 1;
        left: ($chellWidth * 2) + 40px;
      }
      &:nth-child(4) {
        top: $chellHeight * 2;
        left: 20px;
      }
      &:nth-child(5) {
        top: $chellHeight * 2;
        left: 60px + $chellWidth;
      }
      &:nth-child(6) {
        top: $chellHeight * 2;
        left: 100px + $chellWidth + $chellWidth;
      }
      &:nth-child(7) {
        top: $chellHeight * 3;
        left: $chellWidth;
      }
      &:nth-child(8) {
        top: $chellHeight * 3;
        left: ($chellWidth * 2) + 40px;
      }
      &:nth-child(9) {
        top: $chellHeight * 4;
        left: 50%;
        margin-left: -$chellWidth/2;
      }
      &.draggable-dropzone--active.draggable-dropzone--occupied {
      }
      .knight-piece {
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
        position: relative;
        &:hover {
          cursor: grab;
        }
        img {
          position: absolute;
          bottom: 0;
          left: 50%;
          margin-left: -40px;
          width: 80px;
          height: 160px;
        }
        &:active {
          cursor: grabbing;
        }
        &.draggable-mirror {
          opacity: 0.5;
          cursor: grabbing !important;
        }
      }
    }
  }
}
.draggable-mirror {
  cursor: grabbing;
}

Finalmente, importamos la librería en un archivo JavaScript. Tras hacerlo, declaramos la función “launch()”. En ella instanciamos la clase Droppable pasándole el contenedor principal, y definiendo los selectores CSS que indican qué elementos son “dropzones” y cual es “draggable”.

import { Droppable } from "@shopify/draggable";
const launch = () => {
  const containers = document.querySelectorAll("#chess-board");
  if (containers.length === 0) {
    return false;
  }
  const droppable = new Droppable(containers, {
    draggable: ".Block--isDraggable",
    dropzone: ".BlockWrapper--isDropzone",
    mirror: {
      constrainDimensions: true,
    },
  });
};
launch();

A modo de ejemplo, también se han incluído un par de eventos capturados con el método “on()” de Draggable.

droppable.on("drag:start", (evt) => {
  console.log("start draggin");
});
droppable.on("droppable:dropped", (evt) => {
  console.log(evt);
});

Con este breve ejemplo hemos visto cómo crear un efecto Drag and drop con JavaScript y Draggable

Una documentación un tanto caótica

Personalmente encontré la documentación un tanto confusa. Y tengo que reconocer que me costó implementar el ejercicio de muestra, a pesar de que está basado en uno de sus ejemplos.

Sin embargo, fué la misma “landing page” la que me motivó a no desistir en el aprendizaje. Los ejemplos que se muestran en el site, seducen hasta el punto de querer entender bien cómo funciona Draggable. No podía dejarlo sin reproducir, al menos, una de las «demos».

En una ocasión ya vimos otra librería que implementa funcionalidades muy parecidas. En este enlace encontrarás el pequeño análisis que hicimos.

Este ha sido un pequeño vistazo a la librería Draggable. A continuación os dejo enlaces a más recursos, así como el ejercicio subido al repositorio de Github de “Librerías Js”.

Nos vemos pronto, un abrazo desarrolladores!

Deja un comentario