Смещение по осям

Библиотека Floating UI предоставляет мощные инструменты для управления позиционированием всплывающих элементов (popper, tooltip, dropdown и других). Одним из ключевых аспектов точного позиционирования является смещение по осям, которое позволяет контролировать расстояние между целевым элементом и плавающим элементом, а также корректировать положение с учётом особенностей интерфейса.


Параметр offset

В Floating UI смещение задаётся через модуль offset, который принимает параметры для регулировки позиции по горизонтали и вертикали.

import { offset } from '@floating-ui/dom';

const middleware = [offset({ mainAxis: 10, crossAxis: 5 })];

Пояснение параметров:

  • mainAxis — смещение вдоль основной оси позиции. Если элемент позиционируется сверху (top), основная ось — вертикальная, и положительное значение будет отодвигать элемент вниз. Для позиции left основная ось — горизонтальная.
  • crossAxis — смещение вдоль перекрёстной оси (перпендикулярной основной). Это полезно, когда нужно сдвинуть элемент вправо или влево относительно центра целевого элемента.
  • alignmentAxis — используется для уточнения смещения при центрированной позиции (например, start или end).

Динамическое смещение

offset может принимать не только фиксированные числа, но и функцию, возвращающую значения на основе размеров элементов:

offset(({ rects, placement }) => {
  if (placement.startsWith('top')) {
    return { mainAxis: rects.reference.height + 8, crossAxis: 0 };
  }
  return { mainAxis: 10, crossAxis: 5 };
});

Пояснение:

  • rects.reference — размеры и положение целевого элемента.
  • rects.floating — размеры плавающего элемента.
  • placement — текущее позиционирование (например, top-start, bottom-end), позволяющее менять смещение в зависимости от направления.

Такой подход обеспечивает адаптивность интерфейса: всплывающий элемент корректно располагается даже при изменении размера окна или контента.


Комбинирование с другими middleware

Смещение часто используется вместе с flip и shift для предотвращения выхода элемента за границы окна:

import { computePosition, offset, flip, shift } from '@floating-ui/dom';

computePosition(reference, floating, {
  middleware: [
    offset({ mainAxis: 12, crossAxis: 0 }),
    flip({ fallbackPlacements: ['bottom', 'right'] }),
    shift({ padding: 5 })
  ],
});
  • flip — изменяет позицию, если плавающий элемент не помещается на текущей оси.
  • shift — корректирует смещение, чтобы элемент не выходил за пределы видимой области.

Важно: offset применяется до flip и shift, поэтому значения смещения учитываются при вычислении возможных альтернативных позиций.


Смещение с учётом скролла и трансформаций

При использовании контейнеров с прокруткой или CSS-трансформациями, смещение по осям остаётся точным, если вычислять позицию через Floating UI DOM API:

computePosition(reference, floating, {
  strategy: 'fixed',
  middleware: [offset({ mainAxis: 15 })],
});
  • strategy: 'fixed' — игнорирует скролл страницы и фиксирует элемент относительно окна.
  • strategy: 'absolute' — смещение рассчитывается относительно ближайшего позиционированного родителя.

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


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

  1. Минимизировать магические числа — использовать размеры элементов (rects) для вычисления смещения.
  2. Разделять mainAxis и crossAxis — это обеспечивает гибкое позиционирование при разных ориентациях.
  3. Тестировать на всех возможных размещениях — всплывающие элементы могут изменять направление (top, bottom, left, right), смещение должно корректно работать для всех.
  4. Использовать middleware вместе — offset, flip и shift в комбинации дают максимально предсказуемое поведение.

Пример сложного смещения

computePosition(reference, floating, {
  placement: 'top-start',
  middleware: [
    offset(({ rects }) => {
      const spacing = rects.reference.height * 0.2;
      return { mainAxis: spacing, crossAxis: 10 };
    }),
    flip(),
    shift({ padding: 8 })
  ],
});

В этом примере смещение рассчитывается динамически, адаптируясь под размеры целевого элемента, а flip и shift обеспечивают корректное отображение при ограниченном пространстве.