Popper.js позволяет динамически позиционировать всплывающие элементы, учитывая положение курсора. Это особенно полезно для тултипов, подсказок и контекстных меню, которые должны следовать за указателем пользователя. Основным подходом является передача координат мыши в Popper вместо привязки к статическому элементу.
Popper.js по умолчанию принимает в качестве reference
DOM-элемент. Чтобы использовать координаты курсора, необходимо создать
объект с полями getBoundingClientRect и
contextElement. Например:
const virtualElement = {
getBoundingClientRect: () => ({
width: 0,
height: 0,
top: mouseEvent.clientY,
bottom: mouseEvent.clientY,
left: mouseEvent.clientX,
right: mouseEvent.clientX,
}),
contextElement: document.body
};
mouseEvent.clientX и
mouseEvent.clientY.document.body.Popper создается стандартным методом createPopper, но с
виртуальным элементом:
const popperInstance = Popper.createPopper(virtualElement, tooltipElement, {
placement: 'top',
modifiers: [
{
name: 'offset',
options: {
offset: [0, 8] // сдвиг тултипа от курсора
}
},
{
name: 'preventOverflow',
options: {
padding: 5
}
}
]
});
Ключевые моменты:
placement — начальное направление всплывающего элемента
относительно курсора.offset позволяет добавить небольшой отступ,
чтобы элемент не перекрывал сам курсор.preventOverflow гарантирует, что тултип не выйдет за
пределы видимой области.Чтобы тултип следовал за курсором, необходимо динамически обновлять
reference:
document.addEventListener('mousemove', (event) => {
virtualElement.getBoundingClientRect = () => ({
width: 0,
height: 0,
top: event.clientY,
bottom: event.clientY,
left: event.clientX,
right: event.clientX,
});
popperInstance.update();
});
update() пересчитывает позицию Popper с учетом новых
координат.getBoundingClientRect позволяет
Popper использовать актуальное положение мыши.Popper.js поддерживает кастомные модификаторы, которые позволяют
выполнять дополнительные вычисления при обновлении позиции. Например,
можно изменить placement в зависимости от положения курсора
на экране:
const followCursorModifier = {
name: 'followCursorPlacement',
enabled: true,
phase: 'main',
fn({ state }) {
if (state.rects.reference.top < 100) {
state.placement = 'bottom';
} else {
state.placement = 'top';
}
}
};
Popper.createPopper(virtualElement, tooltipElement, {
modifiers: [followCursorModifier]
});
state.placement динамически изменяет
направление тултипа.При использовании события mousemove стоит учитывать
частоту обновлений:
requestAnimationFrame для синхронизации с
рендером:let rafId;
document.addEventListener('mousemove', (event) => {
if (rafId) cancelAnimationFrame(rafId);
rafId = requestAnimationFrame(() => {
virtualElement.getBoundingClientRect = () => ({
width: 0,
height: 0,
top: event.clientY,
bottom: event.clientY,
left: event.clientX,
right: event.clientX,
});
popperInstance.update();
});
});
update() предотвращает падение FPS
при интенсивном движении мыши.throttle или
debounce для управления частотой.Попперы, привязанные к курсору, часто используют анимации для плавного появления:
.tooltip {
transition: transform 0.1s ease, opacity 0.1s ease;
opacity: 0;
transform: scale(0.9);
}
.tooltip.show {
opacity: 1;
transform: scale(1);
}
mouseenter добавляется класс
show, при mouseleave удаляется.Даже с preventOverflow стоит учитывать углы экрана:
flip модификатор для автоматической смены
стороны:{
name: 'flip',
options: {
fallbackPlacements: ['top', 'bottom', 'right', 'left']
}
}
offset, preventOverflow и
flip обеспечивает корректное позиционирование на всех
разрешениях.mousemove с обновлением
getBoundingClientRect и вызовом update().flip и preventOverflow для
корректного позиционирования на краях экрана.Такой подход позволяет создавать максимально отзывчивые и точные всплывающие подсказки, полностью привязанные к движению мыши.