Shared element transitions

Shared element transitions представляют собой технику анимации, при которой один и тот же элемент визуально перемещается между разными страницами или компонентами, создавая ощущение непрерывного взаимодействия. В SvelteKit это реализуется за счет комбинации реактивности Svelte, системы маршрутизации и CSS-анимаций или библиотек для работы с motion.


Основные концепции

  1. Элемент как мост Элемент, который анимируется между состояниями или страницами, должен быть идентифицирован уникальным ключом или атрибутом. Этот ключ позволяет системе понять, что элемент на новой странице — это продолжение того же элемента с предыдущей страницы.

    <div bind:this={card} data-shared-id="card-1">
        <h2>{title}</h2>
    </div>

    Здесь data-shared-id="card-1" играет роль идентификатора, который связывает исходный и целевой элементы.

  2. Состояния и позиции Для корректной анимации необходимо определить начальное и конечное состояние элемента: размеры, позицию на экране и стиль. Это можно сделать через реактивные переменные и методы getBoundingClientRect().

    let rectStart, rectEnd;
    
    function captureStart() {
        rectStart = card.getBoundingClientRect();
    }
    
    function captureEnd() {
        rectEnd = card.getBoundingClientRect();
    }

    Разница между rectStart и rectEnd используется для расчета анимации трансформации.

  3. Использование animate и transition Svelte предоставляет встроенные функции animate:flip и transition, которые позволяют создавать плавные перемещения и трансформации элементов. Для shared element transitions чаще всего применяется техника FLIP (First, Last, Invert, Play).

    <div
        animate:flip
        style="transform-origin: top left;"
        bind:this={card}
        data-shared-id="card-1"
    >
        <h2>{title}</h2>
    </div>

    Алгоритм FLIP состоит из:

    • First: сохранение исходных координат элемента.
    • Last: сохранение конечных координат после изменения DOM.
    • Invert: вычисление разницы между исходной и конечной позицией.
    • Play: применение CSS-трансформации для плавного перехода.

Интеграция с маршрутизацией SvelteKit

Shared element transitions особенно полезны при навигации между страницами. В SvelteKit маршруты — это файлы в src/routes, а переходы между ними можно перехватывать через хук beforeNavigate или afterNavigate.

import { afterNavigate } from '$app/navigation';

afterNavigate(() => {
    captureEnd();
    animateTransition();
});
  • beforeNavigate: позволяет зафиксировать начальные координаты перед сменой страницы.
  • afterNavigate: позволяет определить конечные координаты и запустить анимацию.

Такой подход гарантирует, что элемент будет плавно переходить даже при полной смене DOM.


Работа с библиотеками UI

Некоторые библиотеки UI для SvelteKit уже предоставляют готовые компоненты для shared element transitions. Например:

  1. Svelte Motion Предоставляет реактивные анимации с поддержкой FLIP и drag. Пример использования:

    <Motion.div
        layoutId="card-1"
        transition={{ duration: 0.5, easing: cubicOut }}
    >
        <h2>{title}</h2>
    </Motion.div>

    Здесь layoutId выполняет роль идентификатора shared element.

  2. Svelte-UI Kits Некоторые UI-библиотеки включают компонент SharedTransition, который оборачивает элемент и автоматически отслеживает изменения его позиции между страницами или состояниями.

    <SharedTransition id="card-1" duration={400}>
        <Card>{title}</Card>
    </SharedTransition>

    Параметр id связывает элементы на разных страницах, а duration регулирует длительность анимации.


Практические рекомендации

  • Всегда фиксировать размеры элемента перед анимацией, чтобы избежать скачков в layout.
  • Использовать абсолютное позиционирование для плавного перемещения между различными контейнерами.
  • Минимизировать сложные стили и вложенность для анимируемого элемента, иначе вычисление координат может быть неточным.
  • При анимации списка элементов применять staggered transitions, чтобы элементы появлялись последовательно.

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

  • Ограничивать использование getBoundingClientRect() для больших списков.
  • Кешировать результаты координат для повторных переходов.
  • Использовать will-change: transform в стилях CSS для GPU-ускорения.

Расширенные сценарии

  1. Переход с динамическим контентом Если содержимое элемента изменяется, необходимо обновлять ключ (data-shared-id) или layoutId, чтобы анимация учитывала новый контент.

  2. Комбинирование с маршрутными анимациями Shared element transitions можно совмещать с fade-in/fade-out страниц, создавая эффект, что элементы «переезжают» между экранами, а фон плавно исчезает или появляется.

  3. Сложные трансформации Можно комбинировать перемещение, масштабирование и повороты. Для этого вычисляют матрицы трансформации на основе начальных и конечных координат.


Эта методика позволяет создавать интерфейсы с высоким уровнем визуальной связанности, плавными переходами и ощущением «живости» приложения без использования тяжелых сторонних анимационных фреймворков.