Библиотека Interact.js предоставляет мощные инструменты для создания интерактивных веб-элементов, включая возможность перетаскивания, изменения размеров и жестов. Одной из важных задач является реализация копирования элементов при перетаскивании, когда исходный элемент остаётся на месте, а пользователь работает с его клоном.
Для начала необходимо сделать элемент перетаскиваемым:
interact('.draggable')
.draggable({
inertia: true,
autoScroll: true,
onstart: dragStartListener,
onmove: dragMoveListener,
onend: dragEndListener
});
inertia: true — добавляет эффект
инерции при завершении перетаскивания.autoScroll: true — позволяет
автоматически прокручивать контейнер при приближении курсора к
границам.onstart, onmove,
onend — события жизненного цикла
перетаскивания.Для копирования элемента нужно в обработчике onstart создавать новый DOM-элемент, идентичный исходному:
function dragStartListener(event) {
const original = event.target;
// Создание клона
const clone = original.cloneNode(true);
clone.classList.add('drag-clone');
clone.style.position = 'absolute';
clone.style.left = original.getBoundingClientRect().left + 'px';
clone.style.top = original.getBoundingClientRect().top + 'px';
document.body.appendChild(clone);
// Сохраняем ссылку на клон для дальнейших перемещений
event.interactable._currentClone = clone;
}
Особенности реализации:
position: absolute, чтобы его можно было
свободно перемещать по документу.getBoundingClientRect()
для точного позиционирования на месте оригинала.drag-clone позволяет стилизовать копию отдельно
(например, изменить прозрачность или рамку).В обработчике onmove перемещается именно клон, а не исходный элемент:
function dragMoveListener(event) {
const clone = event.interactable._currentClone;
if (!clone) return;
const dx = event.dx;
const dy = event.dy;
const x = (parseFloat(clone.getAttribute('data-x')) || 0) + dx;
const y = (parseFloat(clone.getAttribute('data-y')) || 0) + dy;
clone.style.transform = `translate(${x}px, ${y}px)`;
clone.setAttribute('data-x', x);
clone.setAttribute('data-y', y);
}
Принцип работы:
data-x и data-y для хранения
текущего смещения.transform: translate() обеспечивает плавное и
производительное перемещение.При завершении перетаскивания можно оставить клон на месте или удалить его, если перетаскивание отменено:
function dragEndListener(event) {
const clone = event.interactable._currentClone;
if (!clone) return;
// Если клон перетащен в допустимую область, оставляем его
if (isDropzoneValid(clone)) {
clone.style.transform = '';
clone.style.left = (parseFloat(clone.style.left) + parseFloat(clone.getAttribute('data-x') || 0)) + 'px';
clone.style.top = (parseFloat(clone.style.top) + parseFloat(clone.getAttribute('data-y') || 0)) + 'px';
} else {
// Иначе удаляем клон
clone.remove();
}
event.interactable._currentClone = null;
}
Особенности:
isDropzoneValid позволяет
определить, оставлять ли элемент на странице.transform переводится в абсолютные
координаты, чтобы клон корректно позиционировался._currentClone предотвращает утечки памяти при
повторных перетаскиваниях.Если требуется создавать несколько копий одновременно, лучше хранить массив клонированных элементов:
interact('.draggable').draggable({
onstart(event) {
const clone = event.target.cloneNode(true);
clone.classList.add('drag-clone');
document.body.appendChild(clone);
if (!event.interactable._clones) {
event.interactable._clones = [];
}
event.interactable._clones.push(clone);
event.interactable._currentClone = clone;
}
});
_clones позволяет управлять всеми копиями,
например, удалять их или проверять пересечения.Чтобы клон мог быть размещён в области Dropzone,
используется комбинация draggable и
dropzone:
interact('.dropzone').dropzone({
accept: '.drag-clone',
overlap: 0.75,
ondrop: function(event) {
const clone = event.relatedTarget;
clone.style.position = 'relative';
clone.style.transform = '';
event.target.appendChild(clone);
}
});
accept фильтрует элементы, которые можно сбросить в
зону.overlap: 0.75 определяет минимальное перекрытие для
активации события ondrop.transform, чтобы повысить производительность.cloneNode(true)) и повторной
инициализации интерактивных функций.Эта структура позволяет создавать мощные системы перетаскивания с копированием элементов, которые сохраняют оригинальные объекты нетронутыми, поддерживают несколько копий и легко интегрируются с зонами сброса.