Клонирование при перетаскивании

Shopify Draggable предоставляет гибкий API для реализации перетаскивания элементов в интерфейсе. Одной из важных возможностей является клонирование элементов при начале перетаскивания, что особенно полезно для реализации функционала перемещения с сохранением исходного элемента или создания визуального эффекта “копирования”.


Создание клонируемого draggable

Для реализации клонирования используется опция mirror и событие mirror:created. При активации клонирования Draggable создаёт зеркало элемента (mirror), которое визуально повторяет исходный элемент, но при этом остаётся независимым от него в DOM.

Пример базовой инициализации с клонированием:

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

const container = document.querySelector('.draggable-container');

const draggable = new Draggable(container, {
  draggable: '.draggable-item',
  mirror: {
    constrainDimensions: true
  }
});

Пояснение ключевых параметров:

  • draggable — селектор элементов, которые можно перетаскивать.
  • mirror.constrainDimensions — ограничивает размеры зеркала размерами исходного элемента, что улучшает визуальное соответствие.
  • Зеркало создаётся автоматически при начале перетаскивания, его можно кастомизировать через события.

Обработка событий зеркала

Shopify Draggable генерирует несколько событий, связанных с зеркалом:

  1. mirror:created — вызывается после создания зеркала.
  2. mirror:move — вызывается при перемещении зеркала.
  3. mirror:destroy — вызывается при завершении перетаскивания.

Пример кастомизации внешнего вида клона при создании:

draggable.on('mirror:created', (event) => {
  const mirror = event.mirror;
  mirror.style.backgroundColor = '#f0f0f0';
  mirror.style.border = '2px dashed #999';
  mirror.style.opacity = '0.8';
});

Здесь event.mirror — это DOM-элемент зеркала, который можно стилизовать независимо от исходного элемента.


Перетаскивание с клонированием исходного элемента

Если требуется, чтобы исходный элемент оставался на месте, а перемещался только клон, используется стандартное поведение зеркала. В этом случае исходный элемент не меняется, а пользователь видит перемещаемый клон.

Для реализации эффекта “перетаскивание копии” можно дополнительно внедрить создание нового элемента в контейнере после окончания перемещения:

draggable.on('drag:stop', (event) => {
  const original = event.source;
  const newItem = original.cloneNode(true);
  newItem.classList.add('cloned');
  document.querySelector('.target-container').appendChild(newItem);
});

Пояснение:

  • event.source — исходный элемент, с которого началось перетаскивание.
  • cloneNode(true) — создаёт глубокую копию элемента со всеми его дочерними узлами.
  • Добавление нового элемента в другой контейнер позволяет реализовать логику “добавления в корзину” или “копирования карточки”.

Клонирование с сохранением функциональности

При клонировании элементов важно помнить о событиях и интерактивных компонентах, встроенных в исходный элемент. Простое клонирование через cloneNode(true) копирует HTML-структуру, но не привязанные слушатели событий. Чтобы сохранить интерактивность, можно:

  1. Повторно инициализировать Draggable на новых элементах.
  2. Использовать делегирование событий через родительский контейнер.
  3. Копировать необходимые атрибуты и состояния вручную.

Пример с повторной инициализацией draggable на новом элементе:

draggable.on('drag:stop', (event) => {
  const clone = event.source.cloneNode(true);
  document.querySelector('.draggable-container').appendChild(clone);

  const newDraggable = new Draggable(document.querySelector('.draggable-container'), {
    draggable: '.draggable-item'
  });
});

Ограничения и рекомендации

  • Производительность: при большом количестве клонируемых элементов желательно ограничивать количество одновременно активных зеркал.
  • Стилизация: зеркала создаются поверх документа, поэтому важно учитывать z-index и прозрачность, чтобы клон не перекрывал интерфейс.
  • События drag и mirror: всегда проверять, какое событие актуально для кастомной логики, чтобы не создавать лишние элементы.
  • Accessibility: при клонировании сохраняется визуальный эффект, но нужно убедиться, что копии элементов корректно обрабатываются с точки зрения доступности (например, aria-атрибуты).

Использование mirror в сочетании с сортировкой

Draggable позволяет одновременно реализовать перетаскивание и сортировку элементов с клонированием. Для этого используется класс Sortable:

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

const sortable = new Sortable(document.querySelectorAll('.sortable-container'), {
  draggable: '.sortable-item',
  mirror: {
    constrainDimensions: true
  }
});
  • Mirror создаётся автоматически при перетаскивании.
  • Исходный элемент остаётся на месте, позволяя визуально перетаскивать копию.
  • После завершения сортировки можно обновить DOM, чтобы сохранить новый порядок элементов или создать новые копии.

Продвинутая кастомизация зеркала

Shopify Draggable предоставляет возможность полного контроля над клоном через mirror:create и mirror:move. Это позволяет:

  • Динамически изменять размер клона.
  • Добавлять анимацию при перемещении.
  • Применять специальные эффекты при наведении на drop-зону.

Пример с динамическим масштабированием зеркала:

draggable.on('mirror:move', (event) => {
  const mirror = event.mirror;
  const scale = 1 + event.sensorEvent.deltaY / 200;
  mirror.style.transform = `scale(${scale})`;
});

Здесь event.sensorEvent.deltaY — величина смещения курсора, позволяющая анимировать масштаб клона в зависимости от перемещения.


Использование клонирования при перетаскивании с Shopify Draggable обеспечивает гибкость в создании интерфейсов с копированием элементов, drag-and-drop корзинами и динамическими эффектами. Контроль над зеркалом и правильная работа с событиями позволяют реализовать сложные сценарии без потери производительности и интерактивности.