Transition компоненты

Переходы (transitions) в экосистеме SvelteKit представляют собой декларативный механизм управления появлением, исчезновением и изменением состояния DOM-элементов. В отличие от классических подходов через CSS или сторонние анимационные библиотеки, Svelte предоставляет встроенную систему, тесно интегрированную с реактивностью.

UI-библиотеки, построенные поверх SvelteKit, расширяют этот механизм, предлагая готовые transition-компоненты, абстракции и шаблоны анимации.


Базовая модель переходов

В основе лежат три ключевых типа:

  • in — анимация появления элемента
  • out — анимация удаления
  • transition — объединённая логика для in/out

Пример:

<script>
  import { fade } from 'svelte/transition';
  let visible = false;
</script>

<button on:click={() => visible = !visible}>
  toggle
</button>

{#if visible}
  <div in:fade out:fade>
    content
  </div>
{/if}

Ключевая особенность — автоматическое управление жизненным циклом элемента. Svelte откладывает удаление DOM-узла до завершения анимации.


Встроенные переходы

Svelte поставляется с набором стандартных функций:

  • fade
  • fly
  • slide
  • scale
  • blur
  • crossfade

Каждая функция возвращает конфигурацию CSS-анимации.

Пример: fly

<div in:fly={{ y: 50, duration: 300 }}>
  элемент
</div>

Параметры:

  • x, y — смещение
  • duration — длительность
  • delay
  • easing

Архитектура transition-компонентов в UI-библиотеках

UI-библиотеки (например, Skeleton, Flowbite-Svelte, Melt UI) инкапсулируют transitions в отдельные компоненты:

<Transition type="fade" duration={200}>
  <Card />
</Transition>

Такая обёртка решает несколько задач:

  • унификация API
  • переиспользуемость
  • централизованная настройка анимаций
  • согласованность UI

Кастомные transition-функции

Переход можно определить вручную:

export function custom(node, params) {
  return {
    duration: 400,
    css: t => `
      opacity: ${t};
      transform: scale(${0.8 + t * 0.2});
    `
  };
}

Где:

  • t — прогресс (0 → 1)
  • u = 1 - t — обратный прогресс

Использование:

<div in:custom>
  элемент
</div>

Easing-функции

Svelte использует easing из svelte/easing:

import { cubicInOut, elasticOut } from 'svelte/easing';

Пример:

<div in:fly={{ y: 100, easing: elasticOut }}>

Популярные easing:

  • linear
  • cubicIn
  • cubicOut
  • cubicInOut
  • bounceOut
  • elasticOut

Crossfade: синхронизация элементов

Используется для анимации перемещения элементов между списками.

import { crossfade } from 'svelte/transition';

const [send, receive] = crossfade({
  duration: 300
});
<div out:send={{ key: item.id }}>
<div in:receive={{ key: item.id }}>

Особенности:

  • связывает элементы по ключу
  • создаёт illusion перемещения
  • используется в drag-and-drop UI

Группировка и координация переходов

В сложных UI необходимо синхронизировать несколько элементов.

stagger-анимации

function stagger(delay, index) {
  return delay * index;
}
{#each items as item, i}
  <div in:fly={{ delay: i * 50 }}>
{/each}

Transition в компонентах UI-библиотек

Типовые примеры:

Модальные окна

  • fade overlay
  • scale/fly контент
<Modal>
  <div in:scale={{ duration: 150 }}>
  • slide + fade
  • быстрые easing

Toast уведомления

  • fly + opacity
  • автоудаление с out-transition

Transition и реактивность

Переходы тесно связаны с реактивными блоками:

$: if (open) {
  // запускается transition
}

Особенности:

  • transition запускается при изменении DOM
  • не работает без изменения состояния
  • реактивность управляет жизненным циклом

SSR и ограничения

SvelteKit выполняет SSR, но:

  • transitions работают только в браузере
  • во время SSR анимации отсутствуют
  • необходимо учитывать onMount
import { onMount } from 'svelte';

let mounted = false;

onMount(() => {
  mounted = true;
});

Оптимизация производительности

Ключевые практики:

  • использовать transform и opacity
  • избегать layout thrashing (width, height)
  • минимизировать duration
  • избегать тяжелых JS-вычислений

Плохой пример:

height: ${t * 100}px;

Хороший:

transform: scaleY(${t});

Управление состояниями UI через transitions

Transitions часто используются как часть state machine:

  • entering
  • entered
  • exiting
  • exited

Некоторые UI-библиотеки реализуют это через store:

import { writable } from 'svelte/store';

export const state = writable('idle');

Интеграция с Tailwind и CSS

UI-библиотеки часто комбинируют:

  • Svelte transitions
  • Tailwind utility-классы

Пример:

<div
  class="transition-opacity duration-200"
  class:opacity-0={!visible}
  class:opacity-100={visible}
>

Сравнение:

Подход Особенность
Svelte transitions декларативный, lifecycle-aware
CSS transitions проще, но без контроля удаления DOM

Переходы между страницами (page transitions)

В SvelteKit можно реализовать анимации маршрутов:

{#key $page.url.pathname}
  <div in:fade out:fade>
    <slot />
  </div>
{/key}

Особенности:

  • key заставляет пересоздать DOM
  • запускается transition
  • применяется к layout

Layout-анимации

Используются для:

  • изменения размеров
  • repositioning элементов

Решения:

  • animate:flip
<div animate:flip>

FLIP:

  • First
  • Last
  • Invert
  • Play

Работа с асинхронными transition

Иногда требуется дождаться завершения:

import { tick } from 'svelte';

await tick();

Или использовать callbacks:

<div
  out:fade
  on:outroend={() => console.log('done')}
>

Accessibility и UX

Переходы должны учитывать:

  • prefers-reduced-motion
@media (prefers-reduced-motion: reduce) {
  * {
    transition: none;
  }
}
  • не мешать взаимодействию
  • не перегружать интерфейс

Расширения UI-библиотек

Современные библиотеки добавляют:

  • preset transitions
  • motion tokens
  • theme-based animation
  • spring physics (через svelte/motion)
import { spring } from 'svelte/motion';

Частые ошибки

  • transition не срабатывает без {#if}
  • неправильный ключ в списках
  • смешивание CSS и JS-анимаций
  • игнорирование SSR
  • чрезмерная длительность

Паттерны использования

1. Reveal on mount

{#if mounted}
  <div in:fade>

2. Conditional UI

{#if loading}
  <Spinner in:fade />
{:else}
  <Content in:fade />
{/if}

3. Animated lists

{#each items as item (item.id)}
  <div in:fly out:fade>

Роль transitions в дизайн-системе

В UI-библиотеках переходы становятся частью дизайн-токенов:

  • duration scale (fast, normal, slow)
  • easing presets
  • animation consistency

Пример:

export const motion = {
  fast: 150,
  normal: 250,
  slow: 400
};

Композиция переходов

Можно комбинировать:

<div
  in:fly={{ y: 20 }}
  in:fade
>

Svelte объединяет эффекты.


Глубокая кастомизация

Через JS можно управлять:

  • ключевыми кадрами
  • CSS-переменными
  • динамическими параметрами
css: (t, u) => `
  transform: translateY(${u * 20}px);
  opacity: ${t};
`

Использование в сложных интерфейсах

Transitions активно применяются в:

  • dashboard UI
  • drag-and-drop системы
  • редакторы
  • realtime приложения

Они обеспечивают:

  • визуальную непрерывность
  • предсказуемость интерфейса
  • улучшение восприятия изменений

Взаимодействие с store

import { derived } from 'svelte/store';

Transitions могут зависеть от store:

{#if $isOpen}
  <Panel in:slide />
{/if}

Декомпозиция анимаций

В крупных проектах:

  • вынос в отдельные файлы
  • единая библиотека transitions
  • повторное использование
// transitions.js
export { fade, fly };

Практика построения UI-библиотеки

Хорошая UI-библиотека:

  • предоставляет готовые transition-компоненты
  • поддерживает кастомизацию
  • учитывает производительность
  • согласует анимации с дизайн-системой

Transitions становятся не просто эффектом, а частью архитектуры интерфейса.