drag:move и отслеживание позиции

В библиотеке Shopify Draggable событие drag:move является ключевым для отслеживания перемещения элемента во время перетаскивания. Оно срабатывает непрерывно при каждом изменении позиции draggable элемента и позволяет динамически реагировать на движение пользователя, обновлять интерфейс или взаимодействовать с другими компонентами страницы.

Синтаксис регистрации обработчика выглядит следующим образом:

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

draggable.on('drag:move', (event) => {
  console.log(event);
});

В объекте event доступны следующие основные свойства:

  • source — DOM-элемент, который перетаскивается.
  • sensorEvent — оригинальное событие мыши или сенсорного экрана, содержащее координаты clientX и clientY.
  • over — элемент, над которым в данный момент находится перетаскиваемый объект (если настроен sortable или swap animation).
  • mirror — элемент-макет, отображаемый поверх интерфейса во время перетаскивания.
  • originalEvent — исходное нативное событие браузера (MouseEvent или TouchEvent).

Получение текущих координат элемента

Для отслеживания позиции элемента чаще всего используется сочетание event.sensorEvent.clientX и event.sensorEvent.clientY. Эти координаты обновляются при каждом срабатывании события drag:move.

Пример динамического отслеживания позиции и вывода в консоль:

draggable.on('drag:move', (event) => {
  const x = event.sensorEvent.clientX;
  const y = event.sensorEvent.clientY;
  console.log(`Текущая позиция: x=${x}, y=${y}`);
});

Если требуется более точное позиционирование относительно контейнера, можно использовать getBoundingClientRect:

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

draggable.on('drag:move', (event) => {
  const rect = container.getBoundingClientRect();
  const x = event.sensorEvent.clientX - rect.left;
  const y = event.sensorEvent.clientY - rect.top;
  console.log(`Позиция относительно контейнера: x=${x}, y=${y}`);
});

Использование mirror для визуальных эффектов

Элемент mirror создается Draggable автоматически и является клоном исходного элемента, который следует за курсором. Через drag:move можно изменять его стили динамически:

draggable.on('drag:move', (event) => {
  const mirror = event.mirror;
  mirror.style.transform = `rotate(${event.sensorEvent.clientX / 5}deg)`;
});

Это позволяет создавать сложные анимации, изменять прозрачность или масштаб перетаскиваемого объекта в реальном времени.


Ограничение движения и кастомные координаты

Событие drag:move можно использовать для ограничения движения по определённой оси или внутри контейнера:

draggable.on('drag:move', (event) => {
  const mirror = event.mirror;
  const rect = event.source.parentElement.getBoundingClientRect();
  let x = event.sensorEvent.clientX - rect.left;
  let y = event.sensorEvent.clientY - rect.top;

  // Ограничение по вертикали
  y = Math.max(0, Math.min(rect.height - mirror.offsetHeight, y));

  // Ограничение по горизонтали
  x = Math.max(0, Math.min(rect.width - mirror.offsetWidth, x));

  mirror.style.transform = `translate(${x}px, ${y}px)`;
});

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


Интеграция с другими событиями Draggable

Событие drag:move тесно связано с другими событиями:

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

Типичная цепочка использования:

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

draggable.on('drag:move', (event) => {
  console.log('Движение элемента', event.sensorEvent.clientX, event.sensorEvent.clientY);
});

draggable.on('drag:stop', (event) => {
  console.log('Элемент сброшен', event.source);
});

Такое разделение позволяет отдельно управлять логикой отображения, ограничениями движения и обновлением данных модели.


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

  1. Сетки товаров: отслеживание позиции товара в каталоге для обновления порядка отображения.
  2. Анимированные списки: динамическое изменение стилей mirror для эффекта “парящего” элемента.
  3. Игровые интерфейсы: точное отслеживание координат курсора для интерактивных перетаскиваемых объектов.
  4. Формы и панели: ограничение движения элементов внутри контейнера с сохранением пропорций.

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

При интенсивном использовании drag:move важно учитывать, что событие вызывается десятки раз в секунду. Рекомендуется:

  • Использовать requestAnimationFrame для обновления стилей:
let ticking = false;

draggable.on('drag:move', (event) => {
  if (!ticking) {
    requestAnimationFrame(() => {
      event.mirror.style.transform = `translate(${event.sensorEvent.clientX}px, ${event.sensorEvent.clientY}px)`;
      ticking = false;
    });
    ticking = true;
  }
});
  • Минимизировать DOM-операции внутри обработчика.
  • Избегать сложных вычислений без необходимости.

Такой подход снижает нагрузку на браузер и обеспечивает плавное перетаскивание даже при множестве элементов.


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