Программное перемещение элементов

Dragula — это лёгкая библиотека JavaScript для перетаскивания элементов между контейнерами. Она обеспечивает удобный интерфейс для drag-and-drop, минимизируя необходимость ручного управления событиями мыши и состояниями элементов. Основная концепция Dragula заключается в автоматическом отслеживании источников перетаскивания и целевых контейнеров, а также в обработке визуальной обратной связи при перемещении.

Инициализация выполняется с помощью вызова функции dragula с массивом контейнеров:

const containers = [document.getElementById('left'), document.getElementById('right')];
const drake = dragula(containers);

После этого все дочерние элементы указанных контейнеров становятся перетаскиваемыми.

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

Dragula предоставляет ряд опций для тонкой настройки поведения:

  • moves — функция, определяющая, можно ли перемещать конкретный элемент:
moves: function (el, source, handle, sibling) {
    return handle.classList.contains('handle');
}

Только элементы с указанным классом handle становятся доступными для перемещения.

  • accepts — функция, контролирующая, можно ли помещать элемент в конкретный контейнер:
accepts: function (el, target, source, sibling) {
    return target.id !== 'forbidden';
}

Элемент не может быть перемещён в контейнер с id forbidden.

  • copy — булевое значение или функция, позволяющая создавать копию элемента при перетаскивании вместо перемещения:
copy: true
  • revertOnSpill — возвращает элемент на место, если он сброшен вне допустимых контейнеров:
revertOnSpill: true

Эти параметры дают полный контроль над правилами перетаскивания и перемещения элементов.

События Dragula

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

  • drag — когда элемент начинает перемещаться.
  • drop — при успешном сбросе элемента в контейнер.
  • over и out — когда перетаскиваемый элемент находится над контейнером или покидает его.
  • cancel — когда перемещение отменяется.
  • remove — когда элемент удаляется после сброса вне контейнера (если настроена соответствующая опция).

Пример использования событий:

drake.on('drag', function(el) {
    el.style.opacity = '0.5';
});

drake.on('drop', function(el, target) {
    el.style.opacity = '1';
    console.log('Элемент перемещён в контейнер:', target.id);
});

drake.on('out', function(el, container) {
    container.style.background = '';
});

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

Перетаскивание с ограничениями

Можно задать ограничения на перемещение элементов:

  • Горизонтальные и вертикальные контейнеры — в Dragula нет встроенного контроля направления, но можно комбинировать с CSS:
.horizontal-container { display: flex; flex-direction: row; }
.vertical-container { display: flex; flex-direction: column; }
  • Фильтрация элементов с помощью moves предотвращает перемещение определённых элементов.
  • Ограничение на количество элементов в контейнере реализуется через accepts:
accepts: function (el, target) {
    return target.children.length < 5;
}

Копирование и удаление элементов

Dragula поддерживает копирование элементов и удаление при сбросе вне контейнера:

const drake = dragula(containers, {
    copy: function (el, source) {
        return source.id === 'source-list';
    },
    removeOnSpill: true
});

При этом элементы из source-list будут копироваться, а если элемент сброшен вне контейнеров, он будет удалён.

Динамическое управление контейнерами

Контейнеры можно добавлять и удалять во время работы приложения:

drake.containers.push(document.getElementById('new-container'));
drake.containers.splice(drake.containers.indexOf(oldContainer), 1);

Это позволяет создавать интерфейсы с динамически изменяемыми зонами перетаскивания.

Взаимодействие с данными

Dragula интегрируется с любыми структурами данных. Для синхронизации состояния можно использовать событие drop:

drake.on('drop', function(el, target, source, sibling) {
    const itemId = el.dataset.id;
    updateDataModel(itemId, source.id, target.id);
});

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

Стилизация и визуальная обратная связь

Dragula добавляет классы к элементам и контейнерам для управления стилями:

  • gu-mirror — копия элемента, перемещаемая мышью.
  • gu-transit — элемент в процессе перемещения.
  • gu-hide — скрывает оригинал при копировании.
  • gu-unselectable — предотвращает выделение текста при перетаскивании.

Пример CSS для визуальной обратной связи:

.gu-mirror {
    opacity: 0.7;
    border: 2px dashed #333;
    background: #f0f0f0;
}

.gu-transit {
    opacity: 0.5;
}

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

Dragula оптимизирован для работы с большим количеством элементов:

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

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

Интеграция с фреймворками

Dragula можно использовать совместно с React, Vue или Angular. Основной принцип — инициализация после рендера DOM и управление состоянием через события drop или remove.

Пример для React:

useEffect(() => {
    const drake = dragula([leftRef.current, rightRef.current]);
    drake.on('drop', (el, target) => {
        handleDrop(el, target);
    });
    return () => drake.destroy();
}, []);

Это позволяет сохранять реактивность компонентов и управлять состоянием приложения через стандартные методы фреймворка.