Визуализация состояний

Механика drag-and-drop не ограничивается перемещением элементов. Для пользователя важны визуальные сигналы, отражающие текущее состояние взаимодействия. Библиотека Shopify Draggable предоставляет встроенную систему классов и событий, позволяющих отображать различные стадии перетаскивания.

Ключевые состояния взаимодействия:

  • элемент готов к перетаскиванию
  • элемент находится в процессе перемещения
  • элемент находится над допустимой областью
  • элемент перемещается между контейнерами
  • элемент возвращается на исходную позицию
  • элемент успешно размещён

Каждое из этих состояний может быть визуализировано через CSS-классы или обработчики событий.


CSS-классы состояний

Библиотека автоматически добавляет служебные классы к элементам в процессе работы.

Основные классы:

Класс Назначение
draggable--is-dragging элемент находится в процессе перетаскивания
draggable--over элемент находится над допустимой областью
draggable--placed элемент успешно размещён
draggable--source исходный элемент, который был захвачен
draggable--mirror визуальная копия перетаскиваемого элемента

Использование этих классов позволяет управлять состояниями интерфейса исключительно средствами CSS.

Пример оформления:

.draggable--is-dragging {
  opacity: 0.4;
}

.draggable--mirror {
  background: #ffffff;
  border: 2px dashed #444;
  transform: scale(1.05);
}

.draggable--over {
  background: #f2f6ff;
}

.draggable--placed {
  animation: dropSuccess 0.25s ease;
}

@keyframes dropSuccess {
  from {
    transform: scale(0.95);
  }
  to {
    transform: scale(1);
  }
}

Такая визуализация помогает пользователю мгновенно понимать текущий этап взаимодействия.


Зеркальный элемент (Mirror)

При перетаскивании Shopify Draggable создаёт специальный элемент — mirror, визуальную копию исходного объекта. Он следует за курсором и отображает перемещаемый элемент.

Особенности mirror-элемента:

  • создаётся динамически
  • позиционируется через position: fixed
  • не влияет на структуру контейнера
  • удаляется после завершения операции

Пример настройки внешнего вида:

.draggable-mirror {
  pointer-events: none;
  z-index: 9999;
  box-shadow: 0 10px 25px rgba(0,0,0,0.15);
}

Эта визуализация делает перемещение более плавным и интуитивным.


Подсветка контейнеров

Контейнеры, принимающие элементы, могут менять внешний вид при наведении перетаскиваемого объекта.

Для этого используется класс draggable--over.

Пример:

.dropzone {
  border: 2px dashed #ccc;
  transition: background 0.2s;
}

.dropzone.draggable--over {
  background: #e8f0ff;
  border-color: #5b8cff;
}

Такое поведение особенно важно в интерфейсах с большим количеством зон.


Индикация позиции вставки

При сортировке элементов важно показать точное место вставки.

Библиотека предоставляет плагин:

  • Sortable

Он автоматически управляет позиционированием элементов внутри контейнера.

Инициализация:

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

const sortable = new Sortable(document.querySelectorAll('.list'), {
  draggable: '.item'
});

Для визуализации позиции можно использовать разделители или отступы:

.list .item {
  transition: transform 0.15s ease;
}

.list .item.draggable--over {
  border-top: 3px solid #3a7afe;
}

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


Визуализация недопустимых зон

Иногда требуется показать пользователю, что элемент нельзя разместить в определённой области.

Это реализуется через обработчики событий.

Событие:

drag:over

Пример:

draggable.on('drag:over', (event) => {
  const container = event.overContainer;

  if (!container.classList.contains('allowed')) {
    container.classList.add('invalid-drop');
  }
});

CSS-оформление:

.invalid-drop {
  background: #ffe9e9;
  border-color: #ff5b5b;
}

Пользователь сразу видит ограничение.


Анимация возврата элемента

Если элемент отпускается вне допустимого контейнера, он возвращается на исходную позицию.

Этот процесс можно анимировать.

Пример:

.draggable--source {
  transition: transform 0.25s ease;
}

Дополнительная визуализация:

.draggable-return {
  animation: returnAnimation 0.3s ease;
}

@keyframes returnAnimation {
  0% {
    transform: scale(1.1);
  }

  100% {
    transform: scale(1);
  }
}

Такая анимация делает поведение интерфейса более понятным.


Состояние захвата элемента

В момент начала перетаскивания элемент получает состояние drag start.

Событие:

drag:start

Обработчик:

draggable.on('drag:start', (event) => {
  event.source.classList.add('active-drag');
});

CSS:

.active-drag {
  opacity: 0.5;
  cursor: grabbing;
}

Сразу становится ясно, какой элемент участвует во взаимодействии.


Состояние завершения перетаскивания

Когда пользователь отпускает элемент, происходит событие:

drag:stop

Обработчик:

draggable.on('drag:stop', (event) => {
  event.source.classList.remove('active-drag');
});

Также можно добавить эффект подтверждения.

.drop-success {
  animation: success 0.25s ease;
}

@keyframes success {
  0% { background: #eaffea; }
  100% { background: transparent; }
}

Использование пользовательских классов

В настройках библиотеки можно указать собственные классы состояний.

Пример конфигурации:

const draggable = new Draggable(document.querySelectorAll('.container'), {
  draggable: '.item',
  classes: {
    'mirror': 'drag-mirror',
    'source:dragging': 'dragging-source',
    'container:over': 'container-over'
  }
});

Это позволяет:

  • интегрировать библиотеку в существующую систему стилей
  • соблюдать соглашения CSS-архитектуры (BEM, SMACSS и др.)
  • избегать конфликтов с другими компонентами

Индикация направления перемещения

При сортировке длинных списков полезно показывать направление движения.

Реализация через события:

sortable:sorted

Пример:

sortable.on('sortable:sorted', (event) => {
  const direction = event.oldIndex < event.newIndex ? 'down' : 'up';

  event.dragEvent.source.classList.add(`move-${direction}`);
});

CSS:

.move-up {
  animation: moveUp 0.2s ease;
}

.move-down {
  animation: moveDown 0.2s ease;
}

@keyframes moveUp {
  from { transform: translateY(-5px); }
  to { transform: translateY(0); }
}

@keyframes moveDown {
  from { transform: translateY(5px); }
  to { transform: translateY(0); }
}

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


Визуализация активной области

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

События:

  • drag:enter
  • drag:leave

Пример:

draggable.on('drag:enter', (event) => {
  event.container.classList.add('active-container');
});

draggable.on('drag:leave', (event) => {
  event.container.classList.remove('active-container');
});

CSS:

.active-container {
  outline: 2px solid #4c7eff;
}

Контейнер становится визуально активным.


Плавность переходов

Для качественной визуализации состояний важны CSS-переходы.

Рекомендуемые свойства для анимации:

  • transform
  • opacity
  • box-shadow
  • background-color

Пример:

.item {
  transition:
    transform 0.15s ease,
    background 0.15s ease,
    box-shadow 0.15s ease;
}

Использование transform предпочтительно, поскольку:

  • не вызывает перерасчёт layout
  • ускоряется GPU
  • обеспечивает плавную анимацию

Индикация доступности перетаскивания

Некоторые элементы могут быть временно недоступны для перемещения.

CSS-индикация:

.item.locked {
  opacity: 0.4;
  cursor: not-allowed;
}

JS-проверка:

draggable.on('drag:start', (event) => {
  if (event.source.classList.contains('locked')) {
    event.cancel();
  }
});

Визуальный сигнал предотвращает ошибки пользователя.


Сложные сценарии визуализации

Комбинация состояний позволяет строить богатые интерфейсы:

  • Kanban-доски
  • редакторы макетов
  • системы управления задачами
  • визуальные конструкторы

Например, доска задач:

  • карточка становится полупрозрачной при захвате
  • колонка подсвечивается при наведении
  • отображается линия вставки
  • после размещения появляется краткая анимация подтверждения

Все эти эффекты реализуются через комбинацию:

  • CSS-классов
  • событий библиотеки
  • пользовательских анимаций

Система состояний Shopify Draggable предоставляет полный контроль над визуальной обратной связью интерфейса перетаскивания.