Floating UI — это мощная библиотека для создания позиционируемых элементов интерфейса, таких как тултипы, поповеры и дропдауны. Управление их анимацией требует понимания жизненного цикла отображаемого элемента, взаимодействия с DOM и интеграции CSS или JS-анимаций.
Каждый floating-элемент имеет следующие состояния:
display: none или opacity: 0).Для корректной анимации важно использовать transition hooks, которые позволяют запускать анимацию перед добавлением элемента в DOM или перед его удалением.
import { useFloating } from '@floating-ui/react';
const { floatingStyles, context } = useFloating({
placement: 'bottom',
});
Здесь floatingStyles можно расширить с помощью
анимационных классов, чтобы управлять переходами состояния элемента.
Наиболее простая и производительная стратегия — использование CSS-переходов для изменения прозрачности и трансформаций.
.floating-enter {
opacity: 0;
transform: translateY(-10px);
transition: opacity 0.2s ease, transform 0.2s ease;
}
.floating-enter-active {
opacity: 1;
transform: translateY(0);
}
.floating-exit {
opacity: 1;
transform: translateY(0);
transition: opacity 0.2s ease, transform 0.2s ease;
}
.floating-exit-active {
opacity: 0;
transform: translateY(-10px);
}
Для более точного контроля анимаций применяются JavaScript-функции,
которые управляют классами или стилями в зависимости от событий
open и close.
import { useState, useEffect } from 'react';
import { useFloating } from '@floating-ui/react';
function Tooltip({ referenceElement, content }) {
const { x, y, reference, floating, strategy } = useFloating({ placement: 'top' });
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
if (isVisible) {
floating.current.classList.add('floating-enter');
requestAnimationFrame(() => {
floating.current.classList.add('floating-enter-active');
});
} else if (floating.current) {
floating.current.classList.add('floating-exit');
requestAnimationFrame(() => {
floating.current.classList.add('floating-exit-active');
});
}
}, [isVisible]);
return (
<div ref={floating} style={{ position: strategy, top: y ?? 0, left: x ?? 0 }}>
{content}
</div>
);
}
requestAnimationFrame гарантирует, что браузер
правильно отработает изменения классов и запустит transition.isVisible позволяет синхронизировать
появление/исчезновение с внутренним состоянием Floating UI.Для сложных анимаций часто применяют сторонние анимационные библиотеки, такие как Framer Motion, позволяющие создавать масштабные эффекты появления, движения и вращения.
import { motion, AnimatePresence } from 'framer-motion';
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.2 }}
ref={floating}
style={{ position: strategy, top: y ?? 0, left: x ?? 0 }}
>
{content}
</motion.div>
)}
</AnimatePresence>
Floating UI вычисляет точное положение элемента, учитывая размеры,
окно просмотра и смещения. Для плавных анимаций важно не менять
позицию элемента во время анимации, иначе появятся рывки.
Лучший способ — анимировать только opacity,
scale, translate и не трогать
координаты top и left до завершения
анимации.
opacity,
transform).AnimatePresence или аналогами, чтобы управлять lifecycle
элементов.Floating UI поддерживает вложенные floating-элементы (например, меню
внутри тултипа). В таких случаях анимации должны
синхронизироваться, чтобы дочерние элементы появлялись
после родительских или исчезали перед ними. Используются
setTimeout или анимационные библиотеки с поддержкой
последовательных переходов.
Понимание этих принципов позволяет создавать интерфейсы с плавным, отзывчивым поведением, где floating-элементы появляются и исчезают естественно, без рывков или задержек.