Floating UI — это библиотека для управления позиционированием всплывающих элементов, таких как тултипы, модальные окна, контекстные меню и поповеры, в реальном времени с точной синхронизацией с фреймами браузера. Ключевой принцип работы библиотеки заключается в обеспечении frame-perfect рендеринга, то есть обновлении позиции элементов строго в рамках цикла рендеринга браузера, без лишних просчетов и дерганий.
Основным механизмом, обеспечивающим точность позиционирования,
является использование requestAnimationFrame. Эта функция
позволяет выполнять вычисления и обновление стилей перед
следующим рендером, что гарантирует плавность анимаций и точное
позиционирование относительно целевого элемента.
Пример базового использования:
import { computePosition, offset, flip, shift } from '@floating-ui/dom';
const button = document.querySelector('#button');
const tooltip = document.querySelector('#tooltip');
function updatePosition() {
computePosition(button, tooltip, {
middleware: [offset(8), flip(), shift({ padding: 5 })],
}).then(({ x, y }) => {
Object.assign(tooltip.style, {
left: `${x}px`,
top: `${y}px`,
});
});
requestAnimationFrame(updatePosition);
}
updatePosition();
Здесь computePosition пересчитывает координаты на каждый
кадр, а requestAnimationFrame обеспечивает
синхронизацию с визуальным обновлением браузера, что
минимизирует дергания и смещения.
Middleware в Floating UI — это функции, которые модифицируют координаты элемента перед его окончательным рендером. Ключевые middleware:
Каждый middleware выполняется в рамках одного кадра, что позволяет создавать комплексное позиционирование без визуальных артефактов.
Для плавного движения Floating UI можно сочетать
frame-perfect обновление позиции с CSS-переходами.
Важно избегать использования setTimeout или
setInterval, так как это нарушает синхронизацию с кадрами
браузера и приводит к дерганым анимациям.
Пример интеграции с CSS-переходами:
#tooltip {
transition: transform 0.2s ease, opacity 0.2s ease;
position: absolute;
will-change: transform;
}
computePosition(button, tooltip, { middleware: [offset(10), flip()] })
.then(({ x, y }) => {
tooltip.style.transform = `translate3d(${x}px, ${y}px, 0)`;
tooltip.style.opacity = '1';
});
Использование translate3d и
will-change: transform позволяет браузеру активно
оптимизировать рендеринг, минимизируя нагрузку на основной
поток.
Frame-perfect рендеринг требует эффективного управления вычислениями:
requestAnimationFrame, чтобы
вычисления выполнялись не чаще одного кадра.Floating UI учитывает scroll и resize события в
реальном времени. Для frame-perfect рендеринга используются подписки на
эти события с интеграцией в requestAnimationFrame:
function trackScrollResize() {
requestAnimationFrame(() => {
updatePosition();
});
}
window.addEventListener('scroll', trackScrollResize, { passive: true });
window.addEventListener('resize', trackScrollResize);
Благодаря такой интеграции элементы остаются точно на месте даже при сложной динамике страницы.
computePosition внутри цикла
requestAnimationFrame.Эта структура обеспечивает безупречно плавное позиционирование, минимизирует дергания и полностью раскрывает потенциал Floating UI для сложных интерфейсов.