Ручное управление позицией

Библиотека Floating UI в JavaScript предоставляет мощные инструменты для динамического позиционирования элементов интерфейса, таких как всплывающие окна, тултипы и меню. В дополнение к автоматическим алгоритмам позиционирования, Floating UI позволяет полностью контролировать координаты элементов вручную, что особенно полезно при сложных пользовательских интерфейсах или нестандартных требованиях к расположению.

Ручное управление позицией достигается через использование функции computePosition с последующей обработкой результатов для явного задания стиля элемента.

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

const reference = document.querySelector('#button');
const floating = document.querySelector('#tooltip');

computePosition(reference, floating, {
  placement: 'bottom', 
}).then(({ x, y }) => {
  Object.assign(floating.style, {
    left: `${x}px`,
    top: `${y}px`,
    position: 'absolute',
  });
});

Ключевой момент: computePosition возвращает объект с координатами x и y, которые можно использовать напрямую для установки style.left и style.top. Это дает полный контроль над положением элемента без необходимости полагаться на встроенные middleware для смещения или предотвращения выхода за границы.


Использование смещений и кастомных трансформаций

Ручное позиционирование не ограничивается простым отображением на координатах x и y. Floating UI поддерживает middleware, которые могут быть применены даже при ручном управлении для тонкой настройки позиции:

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

computePosition(reference, floating, {
  placement: 'right',
  middleware: [offset(10), flip()],
}).then(({ x, y, placement }) => {
  Object.assign(floating.style, {
    left: `${x}px`,
    top: `${y}px`,
    position: 'absolute',
  });
  floating.dataset.placement = placement;
});
  • offset(value) позволяет добавить смещение от исходной позиции.
  • flip() динамически меняет сторону, если элемент не помещается в видимую область.

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


Анимация и обновление позиции

Ручное управление особенно важно, когда элемент должен следовать за движением reference-элемента или анимироваться. В таких случаях используется цикл обновления позиции:

function updatePosition() {
  computePosition(reference, floating).then(({ x, y }) => {
    Object.assign(floating.style, {
      left: `${x}px`,
      top: `${y}px`,
      position: 'absolute',
    });
    requestAnimationFrame(updatePosition);
  });
}

updatePosition();

Особенности:

  • Использование requestAnimationFrame позволяет обновлять позицию плавно при изменении положения reference-элемента.
  • Такой подход эффективен для интерфейсов с динамически изменяющимся контентом или при прокрутке страницы.

Ручное управление с ограничениями viewport

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

computePosition(reference, floating).then(({ x, y }) => {
  const boundedX = Math.max(0, Math.min(x, window.innerWidth - floating.offsetWidth));
  const boundedY = Math.max(0, Math.min(y, window.innerHeight - floating.offsetHeight));

  Object.assign(floating.style, {
    left: `${boundedX}px`,
    top: `${boundedY}px`,
    position: 'absolute',
  });
});

Принцип: ручное ограничение координат позволяет гибко контролировать положение без необходимости полагаться на middleware shift(). Это важно, когда требуется специфическая логика адаптивности или при работе с нестандартными контейнерами.


Интеграция с кастомными событиями

Ручное позиционирование легко комбинируется с событиями интерфейса:

document.addEventListener('mousemove', (event) => {
  const x = event.clientX + 10;
  const y = event.clientY + 10;

  Object.assign(floating.style, {
    left: `${x}px`,
    top: `${y}px`,
    position: 'absolute',
  });
});
  • Позволяет привязывать всплывающие элементы к курсору.
  • Можно использовать для создания интерактивных тултипов или кастомных drag-and-drop интерфейсов.

Отслеживание изменений размеров и контента

Floating UI позволяет учитывать размеры reference-элемента и самого floating-элемента, что критично при ручном управлении. Для динамического контента можно использовать ResizeObserver:

const resizeObserver = new ResizeObserver(() => {
  computePosition(reference, floating).then(({ x, y }) => {
    Object.assign(floating.style, { left: `${x}px`, top: `${y}px` });
  });
});

resizeObserver.observe(reference);
resizeObserver.observe(floating);

Преимущества:

  • Позволяет автоматически пересчитывать позицию при изменении размеров reference или floating элемента.
  • Поддерживает адаптивные интерфейсы без потери контроля над точными координатами.

Итоговые рекомендации по ручному управлению

  • Использовать computePosition для расчета базовых координат.
  • Применять middleware по необходимости, даже при ручном управлении, для смещений и предотвращения выхода за границы.
  • Обновлять позиции динамически, если reference-элемент или floating-элемент меняют размеры.
  • Ограничивать координаты вручную, когда стандартные middleware не подходят.
  • Интегрировать с событиями и анимациями, чтобы создавать сложные, интерактивные интерфейсы.

Ручное управление позицией позволяет полностью контролировать отображение элементов, при этом сохраняя возможности Floating UI по точному и безопасному вычислению координат в любых условиях.