Позиционирование стрелки

Floating UI — это библиотека для динамического позиционирования элементов интерфейса, таких как всплывающие подсказки (tooltips), выпадающие меню (dropdowns) и модальные окна. Одной из ключевых возможностей является управление положением стрелки (arrow), которая визуально указывает на элемент-триггер. Стрелка не просто декоративный элемент: её точное позиционирование требует учёта множества факторов, включая размеры контейнера, границы окна и возможные смещения.


Основные понятия

Arrow — это отдельный элемент DOM, который следует за плавающим элементом (floating element) и указывает на целевой элемент (reference element). Floating UI обеспечивает автоматическое вычисление позиции стрелки с учётом:

  • Основной позиции плавающего элемента (top, bottom, left, right)
  • Смещения относительно границ reference element
  • Размеров стрелки (ширина, высота, радиус скругления)
  • Допустимого пространства вокруг плавающего элемента (boundary)

Настройка стрелки

Для работы с стрелкой в Floating UI используется модуль arrow, который подключается через middleware при создании позиции:

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

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

computePosition(reference, floating, {
  placement: 'top',
  middleware: [
    offset(8),
    flip(),
    shift({ padding: 5 }),
    arrow({ element: arrowElement })
  ]
}).then(({ x, y, middlewareData }) => {
  Object.assign(floating.style, {
    left: `${x}px`,
    top: `${y}px`
  });

  const { x: arrowX, y: arrowY } = middlewareData.arrow;

  Object.assign(arrowElement.style, {
    left: arrowX != null ? `${arrowX}px` : '',
    top: arrowY != null ? `${arrowY}px` : ''
  });
});

Ключевые моменты:

  • offset(8) задаёт расстояние между плавающим элементом и reference element.
  • flip() позволяет менять сторону, если изначальная позиция не помещается на экране.
  • shift({ padding: 5 }) сдвигает элемент, чтобы он не выходил за границы viewport.
  • arrow({ element: arrowElement }) сообщает Floating UI, что необходимо учитывать стрелку при расчёте позиции.

Работа middleware arrow

Middleware arrow возвращает объект middlewareData.arrow с координатами x и y, которые указывают на центр стрелки относительно плавающего элемента.

Пример структуры:

{
  "arrow": {
    "x": 50,
    "y": 0
  }
}
  • Если плавающий элемент расположен сверху (top), y всегда равен 0, а x указывает горизонтальное смещение стрелки.
  • Если элемент слева (left), x = 0, а y задаёт вертикальное смещение.
  • Если элемент перевёрнут (flip), координаты автоматически пересчитываются.

Учёт размеров стрелки

Размер стрелки задаётся через CSS. Floating UI рекомендует использовать переменные для динамических вычислений:

#arrow {
  width: 12px;
  height: 12px;
  background: black;
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
  position: absolute;
}
  • clip-path создаёт треугольник.
  • position: absolute необходим для точного позиционирования.
  • Middleware учитывает реальные размеры стрелки при расчёте x и y.

Комбинация с другими middleware

Стрелка часто используется вместе с offset, flip и shift, чтобы обеспечить корректное позиционирование в любых условиях:

  1. Смещение стрелки: offset() увеличивает или уменьшает расстояние между плавающим элементом и reference element.
  2. Автоматическая смена стороны: flip() помогает стрелке оставаться видимой при переполнении viewport.
  3. Корректировка границ: shift() не позволяет стрелке выходить за пределы окна и предотвращает её обрезание.

Примеры сложных сценариев

Стрелка на углу

Иногда требуется, чтобы стрелка находилась не по центру, а смещалась к углу:

arrow({
  element: arrowElement,
  padding: 10 // Минимальное расстояние от края
})
  • padding задаёт отступ стрелки от границ плавающего элемента.
  • Middleware автоматически корректирует координаты стрелки с учётом этого параметра.

Динамическая адаптация размеров

Если размеры reference element изменяются (например, кнопка растёт при hover), стрелка автоматически перестраивается при каждом вызове computePosition. Это позволяет создавать адаптивные тултипы без ручного пересчёта координат.


Выводы по позиционированию стрелки

  • Floating UI позволяет управлять стрелкой с помощью middleware arrow.
  • Координаты стрелки вычисляются автоматически, но могут быть скорректированы через padding.
  • Размеры и форма стрелки задаются CSS.
  • Стрелка корректно работает с offset, flip и shift для предотвращения наложений и переполнения.
  • Для сложных интерфейсов можно комбинировать несколько middleware для максимальной точности.

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