Для интеграции Floating UI в проект на Svelte
сначала необходимо установить пакет. Основной пакет для позиционирования
элементов — @floating-ui/dom, но для использования в
реактивной среде Svelte часто удобнее использовать обертку, которая
управляет состоянием позиции через реактивные переменные.
npm install @floating-ui/dom
Импорт основных функций:
import { computePosition, offset, flip, shift } from '@floating-ui/dom';
computePosition — центральная функция библиотеки,
которая рассчитывает позицию «флоатинга» относительно целевого элемента.
offset, flip и shift — middleware
для управления смещением, отражением при выходе за границы экрана и
подстройкой позиции.
В Svelte позиция часто хранится в реактивных переменных:
<script>
import { onMount } from 'svelte';
import { computePosition, offset, flip, shift } from '@floating-ui/dom';
let referenceEl;
let floatingEl;
let x = 0;
let y = 0;
function updatePosition() {
if (referenceEl && floatingEl) {
computePosition(referenceEl, floatingEl, {
placement: 'bottom-start',
middleware: [offset(8), flip(), shift({ padding: 5 })]
}).then(({ x: newX, y: newY }) => {
x = newX;
y = newY;
});
}
}
onMount(() => {
const observer = new ResizeObserver(updatePosition);
observer.observe(referenceEl);
observer.observe(floatingEl);
window.addEventListener('scroll', updatePosition);
window.addEventListener('resize', updatePosition);
updatePosition();
return () => {
observer.disconnect();
window.removeEventListener('scroll', updatePosition);
window.removeEventListener('resize', updatePosition);
};
});
</script>
<div bind:this={referenceEl}>Триггер</div>
<div
bind:this={floatingEl}
style="position: absolute; top: {y}px; left: {x}px;"
>
Всплывающий элемент
</div>
Ключевые моменты:
bind:this позволяет получить ссылку на
DOM-элемент.ResizeObserver отслеживает изменения размеров
элементов, чтобы позиция оставалась корректной.scroll и resize события на window
гарантируют реактивное обновление при прокрутке или изменении размеров
окна.Middleware позволяют модифицировать финальные координаты:
Пример с использованием стрелки:
<script>
import { arrow } from '@floating-ui/dom';
let arrowEl;
function updatePosition() {
computePosition(referenceEl, floatingEl, {
placement: 'top',
middleware: [
offset(10),
flip(),
shift({ padding: 5 }),
arrow({ element: arrowEl })
]
}).then(({ x: newX, y: newY, middlewareData }) => {
x = newX;
y = newY;
arrowX = middlewareData.arrow?.x ?? 0;
arrowY = middlewareData.arrow?.y ?? 0;
});
}
</script>
<div bind:this={floatingEl} style="position:absolute; top:{y}px; left:{x}px;">
Всплывающий элемент
<div bind:this={arrowEl} style="position:absolute; left:{arrowX}px; top:{arrowY}px;">
▼
</div>
</div>
Floating UI прекрасно сочетается с переходами и анимациями Svelte.
Для плавного появления/исчезновения можно использовать
transition:fade:
<script>
import { fade } from 'svelte/transition';
let visible = false;
</script>
<button on:click={() => visible = !visible}>Toggle</button>
{#if visible}
<div
bind:this={floatingEl}
style="position:absolute; top:{y}px; left:{x}px;"
transition:fade
>
Появляющийся элемент
</div>
{/if}
Совет: вызов updatePosition() следует
делать после появления элемента, чтобы координаты были рассчитаны уже
для существующего DOM.
Для сложных интерфейсов удобно хранить состояние позиции во внешнем
store:
import { writable } from 'svelte/store';
export const floatingPosition = writable({ x: 0, y: 0 });
function updatePosition(referenceEl, floatingEl) {
computePosition(referenceEl, floatingEl, {
placement: 'bottom',
middleware: [offset(8), flip(), shift({ padding: 5 })]
}).then(({ x, y }) => floatingPosition.set({ x, y }));
}
Компоненты могут подписываться на floatingPosition и
автоматически обновлять стили:
<div bind:this={floatingEl} style="position:absolute; top:{ $floatingPosition.y }px; left:{ $floatingPosition.x }px;">
Контент
</div>
Если содержимое всплывающего блока изменяется динамически, важно
повторно вызывать computePosition. Для этого удобно
использовать Svelte reactive statements:
$: updatePosition();
Эта конструкция автоматически пересчитает позицию при изменении зависимых переменных, например размеров элементов или состояния видимости.
Floating UI можно применять внутри сложных компонентных структур:
При этом следует:
bind:this для всех опорных и всплывающих
элементов.onMount и реактивные блоки.Эта стратегия обеспечивает корректное позиционирование и позволяет полностью раскрыть возможности Floating UI в среде Svelte.