Floating UI предоставляет мощный инструмент для позиционирования элементов на веб-странице с высокой гибкостью. Одним из ключевых сценариев является позиционирование всплывающих элементов (tooltip, popover, меню) относительно координат мыши, что позволяет создавать интерфейсы, реагирующие на движение курсора в реальном времени.
Для начала необходимо импортировать необходимые функции из библиотеки:
import { computePosition, offset, flip, shift } from '@floating-ui/dom';
Эти функции позволяют вычислять положение всплывающего элемента и
применять стратегии корректировки позиции, такие как flip
(смена стороны) и shift (подстройка внутри границ).
В отличие от стандартного позиционирования относительно DOM-элемента, здесь опорными точками будут координаты курсора:
const tooltip = document.querySelector('#tooltip');
document.addEventListener('mousemove', async (event) => {
const { clientX: x, clientY: y } = event;
const position = await computePosition(
{ getBoundingClientRect: () => ({ x, y, width: 0, height: 0 }) },
tooltip,
{
middleware: [offset(10), flip(), shift({ padding: 5 })],
strategy: 'fixed',
}
);
Object.assign(tooltip.style, {
left: `${position.x}px`,
top: `${position.y}px`,
});
});
Ключевые моменты этого подхода:
getBoundingClientRect используется для создания
виртуального элемента с координатами мыши.offset(10) добавляет небольшое смещение, чтобы
всплывающий элемент не перекрывал курсор.flip() и shift() автоматически
корректируют положение, если элемент выходит за границы окна.strategy: 'fixed' фиксирует элемент относительно окна
браузера, а не документа.Floating UI поддерживает виртуальные элементы, что
позволяет вычислять позицию без привязки к реальному DOM-узлу. В примере
выше
{ getBoundingClientRect: () => ({ x, y, width: 0, height: 0 }) }
— это виртуальный элемент.
Для более сложного сценария, например, если требуется учитывать размер всплывающего блока, можно сделать так:
const virtualMouse = {
getBoundingClientRect: () => ({
x: mouseX,
y: mouseY,
width: 0,
height: 0,
top: mouseY,
left: mouseX,
right: mouseX,
bottom: mouseY,
}),
contextElement: document.body,
};
contextElement указывает, относительно какого элемента
будут вычисляться позиции.
Постоянное обновление позиции на mousemove может
вызывать нагрузку. Для оптимизации используют throttle
или requestAnimationFrame:
let frame;
document.addEventListener('mousemove', (event) => {
if (frame) cancelAnimationFrame(frame);
frame = requestAnimationFrame(() => updateTooltip(event));
});
async function updateTooltip(event) {
const { clientX: x, clientY: y } = event;
const position = await computePosition(
{ getBoundingClientRect: () => ({ x, y, width: 0, height: 0 }) },
tooltip,
{
middleware: [offset(10), flip(), shift({ padding: 5 })],
strategy: 'fixed',
}
);
Object.assign(tooltip.style, {
left: `${position.x}px`,
top: `${position.y}px`,
});
}
Это гарантирует, что вычисление позиции не выполняется чаще, чем частота перерисовки экрана, предотвращая лаги при быстром движении мыши.
Floating UI позволяет комбинировать позиционирование относительно координат мыши и реального элемента DOM. Это полезно, например, для меню, которое открывается относительно кнопки, но начальная точка открытия должна быть курсор:
const button = document.querySelector('#button');
button.addEventListener('click', async (event) => {
const virtual = {
getBoundingClientRect: () => ({
x: event.clientX,
y: event.clientY,
width: 0,
height: 0,
}),
};
const position = await computePosition(virtual, tooltip, {
middleware: [offset(5), flip(), shift({ padding: 8 })],
strategy: 'absolute',
});
Object.assign(tooltip.style, {
left: `${position.x}px`,
top: `${position.y}px`,
});
});
Такой подход объединяет контекст кнопки (для ограничений) и координаты мыши для точного открытия.
При изменении размера окна или прокрутке важно пересчитывать позицию.
Floating UI поддерживает динамическую адаптацию через вызов
computePosition на события resize и
scroll. Пример с использованием mousemove:
window.addEventListener('resize', () => updateTooltip(lastEvent));
window.addEventListener('scroll', () => updateTooltip(lastEvent));
lastEvent — объект последнего события мыши, чтобы
позиция оставалась корректной.
Такой подход к позиционированию на основе координат мыши позволяет создавать интерактивные, адаптивные и удобные интерфейсы, полностью используя возможности Floating UI для управления положением элементов с учетом границ окна, смещения и динамики пользовательского взаимодействия.