Принцип работы Swappable

Swappable — это модуль из библиотеки Shopify Draggable, предназначенный для управления обменом элементов между контейнерами и внутри одного контейнера с поддержкой drag-and-drop. В отличие от Sortable, который перемещает элементы по списку, Swappable ориентирован на обмен позиций элементов, что особенно удобно для интерфейсов с сетками, карточками или досками.

Ключевой принцип работы Swappable заключается в том, что каждое перетаскиваемое событие инициирует «swap» между исходным элементом и целевым. Процесс делится на несколько этапов: инициализация, отслеживание движения, определение цели обмена и фактическая замена DOM-элементов.


Инициализация Swappable

Для использования Swappable необходимо создать экземпляр класса с конфигурацией:

import { Swappable } from '@shopify/draggable';

const swappable = new Swappable(document.querySelectorAll('.container'), {
  draggable: '.draggable-item',
  mirror: {
    constrainDimensions: true
  }
});

Пояснения параметров:

  • .container — CSS-селектор контейнера(ов), внутри которого происходит обмен.
  • draggable — селектор элементов, которые можно перемещать.
  • mirror.constrainDimensions — опция, которая фиксирует размеры «зеркального» элемента (элемент, который визуально перемещается при drag).

При инициализации создается слушатель событий для drag:start, drag:move и drag:stop. Эти события позволяют отслеживать весь жизненный цикл перетаскивания и управлять визуальными эффектами.


Работа с зеркальным элементом (Mirror)

При перетаскивании Swappable создает зеркальный элемент, который визуально повторяет исходный. Зеркало отделяется от DOM исходного элемента и следует за курсором, сохраняя размеры и стиль исходника, если включена опция constrainDimensions.

swappable.on('drag:start', (event) => {
  console.log('Начало перетаскивания:', event.source);
});

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


Логика определения цели обмена

Когда пользователь перемещает элемент, Swappable постоянно отслеживает позицию курсора относительно других draggable-элементов. Цель обмена определяется на основе ближайшего пересечения с другим элементом:

  • event.over — элемент, над которым находится курсор.
  • event.source — перетаскиваемый элемент.
  • Если курсор пересек другой элемент на достаточную часть (обычно >50% площади), Swappable считает его целью для swap.
swappable.on('swappable:swapped', (event) => {
  console.log('Элементы обменяны:', event.source, '↔', event.over);
});

События Swappable

Swappable предоставляет набор событий, с помощью которых можно контролировать поведение обмена:

  • swappable:start — инициируется при начале drag.
  • swappable:swapped — вызывается после завершения обмена элементов.
  • swappable:stop — когда drag прекращается.
  • swappable:over / swappable:out — при наведении и уходе курсора над другим элементом.

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


Обмен элементов в DOM

После того как цель обмена определена, Swappable меняет местоположение элементов в DOM, используя стандартные методы insertBefore или replaceChild. При этом исходный элемент и целевой элемент полностью меняются местами, сохраняя все атрибуты и обработчики событий.

swappable.on('swappable:swapped', (event) => {
  // Можно обновить состояние приложения или синхронизировать данные
  updateOrderInDatabase(event.source, event.over);
});

Важно: Swappable не создает копии элементов, а выполняет именно перемещение, поэтому любые привязанные события и данные сохраняются.


Настройка поведения

Swappable поддерживает дополнительные опции:

  • draggable — селектор перетаскиваемых элементов.
  • swapAnimation — позволяет включить плавную анимацию обмена.
  • delay — задержка перед началом drag.
  • mirror — настройка зеркала (размер, класс, ограничения).
const swappable = new Swappable('.container', {
  draggable: '.item',
  delay: 100,
  mirror: { constrainDimensions: true },
  swapAnimation: { duration: 300, easing: 'ease-in-out' }
});

Эти опции делают Swappable гибким инструментом для интерфейсов с динамическими сетками, списками или панелями.


Практические сценарии применения

  1. Сортировка карточек на доске: перемещение задач между колонками с мгновенной заменой позиций.
  2. Игровые интерфейсы: обмен элементов в пазлах или настольных играх.
  3. Редактирование списков продуктов: Drag-and-drop смена позиций товаров в каталоге Shopify.

Ключевое преимущество Swappable — интуитивно понятный обмен элементов без дублирования DOM и с поддержкой всех событий drag-and-drop.