21. View Transitions
View Transitions — это мощный API браузера, который позволяет создавать плавные анимированные переходы между страницами, превращая обычный многостраничный сайт в приложение с ощущением SPA. Astro делает работу с этим API невероятно простой.
Что такое View Transitions?
Заголовок раздела «Что такое View Transitions?»Традиционная навигация между страницами сопровождается резкой перезагрузкой — браузер стирает одну страницу и рисует другую. View Transitions API позволяет браузеру сначала создать «снимок» текущего состояния, затем загрузить новую страницу, и плавно анимировать переход между двумя состояниями.
Astro поддерживает View Transitions через специальный компонент и набор директив, которые работают как в режиме MPA (Multi-Page Application), так и помогают создавать SPA-подобный опыт.
Компонент <ViewTransitions />
Заголовок раздела «Компонент <ViewTransitions />»Для включения View Transitions достаточно добавить компонент в секцию <head> вашего базового макета:
---import { ViewTransitions } from 'astro:transitions';---<html> <head> <ViewTransitions /> </head> <body> <slot /> </body></html>После этого Astro автоматически перехватывает все навигационные ссылки и применяет анимации при переходах. По умолчанию используется анимация fade — элементы плавно появляются и исчезают.
Директива transition:name — сопоставление элементов
Заголовок раздела «Директива transition:name — сопоставление элементов»Одна из самых впечатляющих возможностей — это анимация «общих элементов» между страницами. Если на двух страницах есть элементы с одинаковым transition:name, браузер создаст плавную анимацию морфинга от одного к другому:
<!-- Страница со списком статей --><img src={post.image} transition:name={`hero-${post.slug}`} />
<!-- Страница статьи --><img src={post.image} transition:name={`hero-${post.slug}`} />Теперь при переходе на страницу статьи изображение «перелетит» из своей позиции в списке на новое место — это создаёт эффект непрерывности интерфейса.
Встроенные анимации transition:animate
Заголовок раздела «Встроенные анимации transition:animate»Astro поставляется с набором готовых анимаций:
---import { fade, slide, morph } from 'astro:transitions';---
<!-- Плавное появление/исчезновение --><div transition:animate="fade">Контент</div>
<!-- Слайд слева/справа --><main transition:animate="slide">Основной контент</main>
<!-- Умный морфинг с учётом позиции --><article transition:animate={morph()}>Статья</article>
<!-- Кастомная конфигурация fade --><section transition:animate={fade({ duration: '0.5s' })}> Медленное появление</section>Кастомные анимации через CSS
Заголовок раздела «Кастомные анимации через CSS»Можно полностью управлять анимацией через CSS-переменные и @keyframes. Astro использует два псевдоэлемента ::view-transition-old и ::view-transition-new для управления переходом:
/* Пользовательская анимация "листание страниц" */@keyframes slide-from-right { from { transform: translateX(100%); opacity: 0; }}@keyframes slide-to-left { to { transform: translateX(-100%); opacity: 0; }}
::view-transition-old(main-content) { animation: 300ms ease slide-to-left;}::view-transition-new(main-content) { animation: 300ms ease slide-from-right;}Сохранение элементов с transition:persist
Заголовок раздела «Сохранение элементов с transition:persist»Некоторые элементы не должны перезагружаться при навигации — например, аудиоплеер или боковая панель с состоянием. Директива transition:persist сохраняет элемент «живым» между страницами:
<!-- Аудиоплеер не прерывается при навигации --><audio-player transition:persist> <audio src="/podcast.mp3" /></audio-player>
<!-- Сохраняем и id для точного сопоставления --><aside transition:persist="sidebar"> <Navigation /></aside>Индикатор прогресса навигации
Заголовок раздела «Индикатор прогресса навигации»Для улучшения UX можно добавить полосу прогресса загрузки. Astro предоставляет хуки жизненного цикла через события document:
document.addEventListener('astro:before-preparation', () => { document.querySelector('#progress-bar')?.classList.add('loading');});
document.addEventListener('astro:page-load', () => { document.querySelector('#progress-bar')?.classList.remove('loading');});