В библиотеке Floating UI ключевым понятием является плавающий элемент (floating element) — элемент интерфейса, который позиционируется относительно другого элемента (reference element) с учётом доступного пространства на экране. Библиотека обеспечивает точное позиционирование с минимальным вмешательством разработчика, учитывая переполнение контейнеров, размеры окна и динамические изменения DOM.
Reference element — это элемент, относительно которого позиционируется плавающий блок. Floating element — это сам блок, который может быть тултипом, выпадающим меню, поповером или модальным элементом.
Floating UI не просто задаёт координаты top и
left. Она предоставляет полноценную систему
модификаторов, которые можно комбинировать для тонкой настройки
поведения и стилей элементов.
Позиционирование строится на функции
computePosition(reference, floating, options). Основные
параметры options:
top,
bottom, left, right и их вариации
с -start и -end.absolute или fixed. absolute
позволяет элементу следовать за прокруткой родителя, fixed
делает элемент неподвижным относительно окна.Пример базового позиционирования с middleware:
import { computePosition, offset, flip, shift } from '@floating-ui/dom';
const reference = document.querySelector('#button');
const floating = document.querySelector('#tooltip');
computePosition(reference, floating, {
placement: 'top',
middleware: [offset(10), flip(), shift({ padding: 5 })],
}).then(({ x, y }) => {
Object.assign(floating.style, {
left: `${x}px`,
top: `${y}px`,
});
});
Выделенные моменты:
offset(10) — задаёт смещение на 10px от reference.flip() — автоматически меняет сторону, если нет
места.shift({ padding: 5 }) — предотвращает выход элемента за
границы viewport.Floating UI не управляет стилями напрямую, но позволяет точно позиционировать элементы, что облегчает работу с CSS. Для красивой интеграции стоит использовать:
Пример стилизации тултипа с учётом вычисленных координат:
#tooltip {
position: absolute;
background-color: #333;
color: #fff;
padding: 8px 12px;
border-radius: 4px;
font-size: 14px;
transition: opacity 0.2s ease, transform 0.2s ease;
opacity: 0;
pointer-events: none;
}
#tooltip.show {
opacity: 1;
pointer-events: auto;
}
С помощью JavaScript можно добавлять класс .show после
вычисления позиции и обеспечить плавное появление.
Для создания стрелки используется middleware arrow. Она
позволяет точно позиционировать указатель относительно reference. Важные
моменты:
arrow({ element }) — middleware для стрелки, где
element — DOM-элемент стрелки.padding — минимальное расстояние между стрелкой и
границами floating element.middlewareData.arrow.Пример:
import { arrow } from '@floating-ui/dom';
const arrowElement = document.querySelector('#tooltip-arrow');
computePosition(reference, floating, {
placement: 'top',
middleware: [offset(10), flip(), 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}px`, top: `${arrowY}px` });
});
Floating UI предоставляет autoUpdate для динамического
отслеживания изменений положения reference или размеров окна. Она
автоматически пересчитывает координаты без ручного вызова
computePosition при каждом событии.
Пример:
import { autoUpdate } from '@floating-ui/dom';
const cleanup = autoUpdate(reference, floating, () => {
computePosition(reference, floating, {
placement: 'top',
middleware: [offset(10), flip(), shift({ padding: 5 })],
}).then(({ x, y }) => {
Object.assign(floating.style, { left: `${x}px`, top: `${y}px` });
});
});
// Для остановки обновлений
cleanup();
Для более сложной стилизации используют CSS-анимации с transform на основе координат, вычисленных библиотекой. Обычно применяется комбинация:
opacity для плавного появленияtranslate3d(x, y, 0) для корректного рендеринга на GPU
и предотвращения смазыванияtransition для контроля скорости анимацииПример анимации с учётом координат:
computePosition(reference, floating, { placement: 'bottom', middleware: [offset(8)] })
.then(({ x, y }) => {
floating.style.transform = `translate3d(${x}px, ${y}px, 0)`;
floating.style.opacity = '1';
});
computePosition следует выполнять по событию, а не на
каждом кадре.offset,
flip, shift и arrow позволяют
создавать адаптивные интерфейсы.Эта система обеспечивает гибкое позиционирование и красивую визуальную интеграцию элементов интерфейса с минимальной нагрузкой на производительность.