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

В React Aria управление состоянием перетаскивания (drag and drop) реализуется через хук useDrag и связанные с ним утилиты. Предпросмотр (drag preview) позволяет отображать визуальное представление элемента, который пользователь перемещает, независимо от его исходного положения в DOM. Это особенно важно для создания интерфейсов с высоким уровнем доступности и интерактивности.

Основные принципы работы drag preview

  1. Состояние перетаскиваемого элемента Хук useDrag возвращает объект состояния, содержащий:

    • isDragging — логическое значение, указывающее, активен ли процесс перетаскивания.
    • dragProps — свойства, которые нужно привязать к элементу для корректного инициирования перетаскивания.
    • dragPreviewProps — свойства для компонента, который будет отображаться как превью.
  2. Изоляция превью от DOM-элемента Предпросмотр обычно создаётся в отдельном контейнере, чтобы его позиция не зависела от реального элемента в списке. React Aria использует для этого порталы, позволяя рендерить превью в любом месте документа, чаще всего в body. Это повышает производительность и предотвращает нежелательные смещения других элементов.

  3. Синхронизация позиции превью Позиция превью рассчитывается на основе текущих координат курсора и размеров исходного элемента. React Aria предоставляет возможность автоматически отслеживать смещение (dragOffset) и передавать его в компонент превью:

    import {useDrag} from '@react-aria/dnd';
    
    function DraggableItem({item}) {
      let {dragProps, dragPreviewProps, isDragging} = useDrag({id: item.id});
    
      return (
        <>
          <div {...dragProps}>{item.name}</div>
          {isDragging && (
            <div {...dragPreviewProps} style={{position: 'fixed', pointerEvents: 'none'}}>
              {item.name}
            </div>
          )}
        </>
      );
    }

    В этом примере dragPreviewProps управляет положением превью, а pointerEvents: 'none' предотвращает перехват событий мыши.

Настройка кастомного превью

Для улучшения UX часто требуется стилизованное или анимированное превью. React Aria позволяет:

  • Присваивать превью любые стили CSS, включая тени, трансформации и прозрачность.
  • Рендерить произвольные React-компоненты, например мини-карточки товара, иконки или сложные элементы с вложенной структурой.
  • Использовать анимацию через CSS или библиотеки вроде framer-motion для плавного перемещения превью за курсором.

Пример с кастомным стилем:

<div {...dragPreviewProps} style={{
  position: 'fixed',
  top: dragOffset.y,
  left: dragOffset.x,
  transform: 'translate(-50%, -50%)',
  background: 'white',
  boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
  borderRadius: '8px',
  padding: '8px',
  pointerEvents: 'none'
}}>
  {item.name}
</div>

Доступность и экранные читалки

React Aria обеспечивает поддержку ARIA-атрибутов при работе с drag preview:

  • aria-grabbed указывает, что элемент выбран для перетаскивания.
  • aria-dropeffect указывает действие при отпускании (move, copy, link).
  • Экранные читалки могут озвучивать текущее состояние перетаскивания, если корректно привязаны события onDragStart и onDragEnd.

Важно, чтобы визуальное превью не было единственным источником информации о состоянии перетаскивания. Пользователь, использующий клавиатуру или экранный ридер, должен получать текстовые подсказки через ARIA-атрибуты или скрытые элементы.

Перетаскивание с клавиатуры

React Aria поддерживает доступное перетаскивание с клавиатуры. Для этого:

  • Хук useDrag предоставляет методы keyboardProps, которые можно присвоить фокусируемым элементам.
  • Перемещение элементов с клавиатуры обновляет состояние превью аналогично мышиному перетаскиванию.
  • Предпросмотр может быть визуально скрыт для клавиатурных пользователей, а изменения позиции транслироваться через ARIA-объявления.

Пример интеграции:

let {dragProps, dragPreviewProps, keyboardProps} = useDrag({id: item.id});

<div {...dragProps} {...keyboardProps}>
  {item.name}
</div>
{isDragging && <div {...dragPreviewProps}>Перетаскивается: {item.name}</div>}

Поддержка сложных контейнеров и списков

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

  • React Aria автоматически обновляет координаты превью при изменении размеров исходного элемента.
  • При использовании useDroppable можно синхронизировать превью с контейнером, в который элемент будет перемещён, обеспечивая точное визуальное соответствие.
  • Виртуализированные списки (react-virtualized, react-window) требуют, чтобы превью рендерилось вне зоны виртуализации, иначе оно может исчезать при прокрутке.

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

  • Использование pointerEvents: 'none' для превью снижает количество лишних событий.
  • Рендеринг через порталы уменьшает влияние на layout и repaint DOM.
  • Анимации следует ограничивать CSS-трансформациями (transform, opacity), чтобы не блокировать главный поток JavaScript.

Предпросмотр при перетаскивании в React Aria — это инструмент, который сочетает визуальное представление, доступность и контроль над состоянием drag-and-drop. Грамотная интеграция превью позволяет создавать интерфейсы с плавной анимацией, поддержкой клавиатуры и экранных читалок, а также обеспечивает высокий уровень UX даже в сложных списках и динамических контейнерах.