Канбан доски

Interact.js — это мощная библиотека для реализации интерактивных элементов на веб-странице. Она позволяет создавать перетаскиваемые, изменяемые по размеру и отзывчивые элементы без сложных обработчиков событий.

Настройка Draggable элементов

Для перетаскивания карточек на канбан-доске используется метод interact().draggable(). Простейший пример выглядит так:

interact('.kanban-card')
  .draggable({
    inertia: true, // добавляет эффект инерции
    modifiers: [
      interact.modifiers.restrictRect({
        restriction: 'parent', // ограничение перемещения рамкой родителя
        endOnly: true
      })
    ],
    listeners: {
      move: dragMoveListener
    }
  });

function dragMoveListener(event) {
  const target = event.target;
  const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
  const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

  target.style.transform = `translate(${x}px, ${y}px)`;
  target.setAttribute('data-x', x);
  target.setAttribute('data-y', y);
}

Ключевые моменты:

  • inertia: true добавляет плавность перемещения.
  • restrictRect ограничивает движение элементами внутри контейнера.
  • Хранение координат в data-x и data-y необходимо для правильного расчёта относительного смещения при каждом событии.

Создание Drop зон

Для перемещения карточек между колонками канбан-доски используются drop-зоны. Основной синтаксис:

interact('.kanban-column').dropzone({
  accept: '.kanban-card',
  overlap: 0.75, // сколько элемента должно пересекать зону для активации drop
  ondropactivate: function(event) {
    event.target.classList.add('drop-active');
  },
  ondragenter: function(event) {
    const draggableElement = event.relatedTarget;
    event.target.classList.add('drop-target');
    draggableElement.classList.add('can-drop');
  },
  ondragleave: function(event) {
    event.target.classList.remove('drop-target');
    event.relatedTarget.classList.remove('can-drop');
  },
  ondrop: function(event) {
    event.target.appendChild(event.relatedTarget);
    event.relatedTarget.style.transform = 'translate(0,0)';
    event.relatedTarget.setAttribute('data-x', 0);
    event.relatedTarget.setAttribute('data-y', 0);
  },
  ondropdeactivate: function(event) {
    event.target.classList.remove('drop-active');
    event.target.classList.remove('drop-target');
  }
});

Ключевые моменты:

  • accept задаёт, какие элементы разрешены для drop.
  • overlap позволяет настраивать чувствительность попадания элемента в drop-зону.
  • В ondrop важно сбрасывать трансформацию, чтобы карточка корректно вставала в колонку.

Resizable карточки

Иногда нужно менять размеры карточек, например, при добавлении дополнительных деталей. Interact.js поддерживает resizable:

interact('.kanban-card')
  .resizable({
    edges: { left: true, right: true, bottom: true, top: false },
    listeners: {
      move(event) {
        let { x, y } = event.target.dataset;
        x = parseFloat(x) || 0;
        y = parseFloat(y) || 0;

        Object.assign(event.target.style, {
          width: `${event.rect.width}px`,
          height: `${event.rect.height}px`,
          transform: `translate(${x}px, ${y}px)`
        });
      }
    },
    modifiers: [
      interact.modifiers.restrictSize({
        min: { width: 100, height: 50 },
        max: { width: 500, height: 300 }
      })
    ],
    inertia: true
  });

Ключевые моменты:

  • edges определяет, с каких сторон можно изменять размеры.
  • restrictSize ограничивает минимальные и максимальные размеры элемента.
  • Хранение координат важно для синхронизации с draggable.

События для взаимодействия с Kanban

Interact.js предоставляет богатый набор событий, которые позволяют реагировать на пользовательские действия:

  • dragstart — начало перетаскивания.
  • dragmove — движение элемента.
  • dragend — завершение перемещения.
  • dropactivate / dropdeactivate — активация и деактивация drop-зоны.
  • resizemove — изменение размера элемента.

Использование этих событий позволяет создавать динамичные реакции интерфейса, например, подсвечивание колонок, отображение индикаторов места для вставки карточки, анимацию перестановки элементов.

Работа с Grid и Snap

Для аккуратного расположения карточек в колонках полезно использовать сетку и привязку к ней:

interact('.kanban-card').draggable({
  modifiers: [
    interact.modifiers.snap({
      targets: [interact.snappers.grid({ x: 10, y: 10 })],
      range: Infinity,
      relativePoints: [{ x: 0, y: 0 }]
    })
  ]
});

Ключевые моменты:

  • snap обеспечивает привязку к виртуальной сетке, что улучшает визуальное выравнивание.
  • range: Infinity позволяет привязываться на любом расстоянии, можно ограничить, чтобы snap срабатывал только вблизи grid.

Оптимизация производительности

Для больших досок с множеством карточек важно:

  1. Использовать requestAnimationFrame внутри dragMoveListener для плавной анимации.
  2. Ограничить количество DOM-операций внутри событий dragmove и resizemove.
  3. Группировать изменения стилей через transform, а не top/left, чтобы задействовать GPU-ускорение.

Интеграция с хранением состояния

Перемещение и изменение размеров карточек должны синхронизироваться с данными. Например, при drop:

const cardId = event.relatedTarget.dataset.id;
const columnId = event.target.dataset.id;

updateBoardState(cardId, columnId);

Ключевой момент: хранить координаты и размеры карточек в объекте состояния доски, чтобы при перезагрузке интерфейс мог восстановить позицию элементов.

Заключение по возможностям

Interact.js позволяет создавать канбан-доски с функционалом перетаскивания, изменения размеров, snap-сеток, ограничений и инерции. В сочетании с правильной структурой данных это даёт полностью интерактивный, отзывчивый и удобный интерфейс для работы с задачами.