Drag-and-drop для мобильных устройств

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

Для подключения Dragula достаточно установить библиотеку через npm или подключить CDN:

import dragula from 'dragula';
import 'dragula/dist/dragula.css';

или

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.3/dragula.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.3/dragula.min.js"></script>

Создание контейнеров и инициализация

Контейнеры — это элементы DOM, между которыми возможен перенос. Dragula использует их для определения зон, куда можно перетаскивать элементы.

const containers = [
    document.getElementById('container1'),
    document.getElementById('container2')
];

const drake = dragula(containers, {
    moves: (el, source, handle, sibling) => true,
    copy: false,
    revertOnSpill: true
});

Ключевые параметры:

  • moves(el, source, handle, sibling) — функция, определяющая, можно ли перемещать конкретный элемент.
  • copy — если true, элемент копируется при переносе, если false — перемещается.
  • revertOnSpill — возвращает элемент на исходное место, если он не был брошен в контейнер.

События Dragula

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

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

Пример обработки событий:

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

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

Поддержка мобильных устройств

Dragula по умолчанию поддерживает мобильные устройства через сенсорные события (touchstart, touchmove, touchend). Однако для более плавной работы рекомендуется использовать небольшие оптимизации:

  • Добавление CSS свойства touch-action: none; к элементам, чтобы отключить системное перетаскивание на мобильных.
  • Минимизация сложных анимаций в контейнерах, чтобы избежать подвисаний.
.draggable {
    touch-action: none;
    user-select: none;
}
  • Использование mirrorContainer для управления зеркалом перетаскиваемого элемента, особенно важно для сложных мобильных интерфейсов:
dragula(containers, {
    mirrorContainer: document.body
});

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

Dragula позволяет ограничивать перетаскивание с помощью функции accepts:

const drake = dragula(containers, {
    accepts: (el, target, source, sibling) => {
        return target.id !== 'container2' || el.dataset.type === 'allowed';
    }
});

В этом примере элементы могут попадать в container2 только если имеют атрибут data-type="allowed".

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

Иногда требуется не перемещать, а копировать элементы между контейнерами. Для этого используется параметр copy и функция copy для более гибкой логики:

const drake = dragula(containers, {
    copy: (el, source) => el.classList.contains('copyable')
});

Работа с зеркалом элемента

Dragula создаёт mirror — визуальную копию перетаскиваемого элемента. Это облегчает восприятие перемещения, особенно на мобильных экранах. Положение зеркала можно изменять через события cloned и CSS:

drake.on('cloned', function(clone, original, type) {
    clone.style.backgroundColor = '#f0f0f0';
});

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

Dragula хорошо интегрируется с React, Vue и Angular через обёртки или прямое использование в компонентах. Для React, например, достаточно подключить Dragula в useEffect и управлять контейнерами через ref:

useEffect(() => {
    const drake = dragula([refContainer1.current, refContainer2.current]);
    return () => drake.destroy();
}, []);

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

Для мобильных интерфейсов важны следующие моменты:

  • Минимизировать количество элементов в контейнерах.
  • Использовать revertOnSpill только при необходимости, так как частые возвраты могут тормозить интерфейс.
  • Ограничивать сложные DOM-структуры внутри draggable-элементов.
  • Устанавливать mirrorContainer в document.body, чтобы зеркальные элементы не влияли на поток документа.

Стилизация и кастомизация

Dragula полностью поддерживает CSS для кастомизации перетаскивания:

  • .gu-mirror — зеркало элемента во время перетаскивания.
  • .gu-transit — элемент, находящийся в процессе перетаскивания.
  • .gu-hide — временно скрытый элемент.

Пример CSS:

.gu-mirror {
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    border-radius: 4px;
}

.gu-transit {
    opacity: 0.7;
}

Эти классы автоматически применяются Dragula и позволяют создавать плавные и наглядные интерфейсы для drag-and-drop на мобильных устройствах.

Особенности для мобильных UX

  • Учитывать небольшие размеры сенсорных экранов при создании draggable-объектов.
  • Использовать более крупные зоны захвата (handle) для удобного перетаскивания пальцем.
  • Контролировать инерцию и скорость перемещения с помощью CSS-переходов и минимальных задержек в обработчиках событий.

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