View Transitions API: полное руководство по плавным переходам в браузере

в 8:15, , рубрики: CSS анимация, FLIP анимация, MPA переходы, React анимация, shared elements, Speculation Rules API, startViewTransition, view transitions api, view-transition-name, плавные переходы в браузере
View Transitions API: полное руководство по плавным переходам в браузере - 1

Помните 2015 год? Тогда Пол Льюис из Google представил концепцию FLIP (First, Last, Invert, Play) — революционный на тот момент способ делать анимации интерфейса со стабильными 60 fps. Идея была гениальной: вместо того, чтобы анимировать свойства разметки (width, top), мы измеряем начальное и конечное состояние элементов, а затем анимируем только transform.

В теории звучало отлично, но на практике реализация была трудоёмкой: требовались вычисления координат getBoundingClientRect, ручной контрольrequestAnimationFrame и бесконечные вычисления таймингов. В итоге большинство разработчиков просто отказывались от сложных переходов, потому что цена поддержки такого кода была слишком высока.

Прошло восемь лет, и в Chrome 111 появился View Transitions API. По сути, браузер наконец-то научился делать FLIP за нас. API решает те же задачи, что FLIP, но принципиально другим способом. Джейк Арчибальд на Chrome Dev Summit показал, как три строчки нативного кода заменяют сотни строк самописных скриптов.

Давайте разберёмся, как это работает, где подводные камни и можно ли использовать в рабочей среде прямо сейчас?

Зачем нам ещё один API?

Если вы когда-либо пользовались веб-приложениями на iOS или Android, вы видели этот эффект: нажимаете на карточку товара, и картинка плавно «перелетает» на страницу деталей, превращаясь в заголовок. В вебе повторить такое было мучительно сложно.

Вы можете сказать: «У нас же есть CSS Transitions и Web Animations API (WAAPI), зачем ещё один?» Разница фундаментальна: старые методы анимируют элементы, а View Transitions API анимирует состояния.

Вот наглядное сравнение:

Задача

CSS Transitions

Web Animations API

View Transitions API

Анимация layout-свойств

Дёргается

Дёргается

Плавно*

Shared elements

Невозможно

Сложно

Из коробки

Кросс-документные переходы

Нет

Нет

Да

Синхронизация состояний

Вручную

Вручную

Автоматически

*Плавно, так как анимируются снимки, а не layout

Чтобы эффективно использовать API, нужно понимать, как браузер организует переход изнутри.

Ключевые концепции: снимки, псевдоэлементы и жизненный цикл

Когда вы вызываете startViewTransition(), браузер проходит чёткую последовательность: захватывает снимки элементов с view-transition-name, выполняет ваш callback с изменениями DOM, захватывает снимки нового состояния и создаёт дерево псевдоэлементов:

::view-transition
::view-transition-group(name)
 		 ::view-transition-image-pair(name)
 ::view-transition-old(name)   /* снимок ДО */
     			 ::view-transition-new(name)   /* снимок ПОСЛЕ */
View Transitions API: полное руководство по плавным переходам в браузере - 2

Поскольку мы манипулируем «картинками» (снимками), а не живыми DOM-узлами, анимация, как правило, выполняется на уровне композитора. Отсюда и стабильная производительность.

Быстрый старт: минимальный рабочий пример

Давайте посмотрим на код, как внедрить View Transitions в существующий проект. Это на самом деле проще, чем кажется.

Представьте типичную ситуацию в SPA: у вас есть функция, которая получает новый контент и просто заменяет старый HTML в контейнере. Обычно это происходит мгновенно и резко.

Чтобы добавить плавную анимацию, нам не нужно переписывать логику рендеринга. Нам нужно всего лишь «обернуть» момент изменения DOM в вызов document.startViewTransition.

Вот универсальный паттерн, который (важно!) не сломает сайт в большинстве случаев при простом DOM-обновлении:

function updateContent(newHTML) {
  const container = document.querySelector('#container');
View Transitions API: полное руководство по плавным переходам в браузере - 3

// 1. Проверяем поддержку (Progressive Enhancement)

  // Если браузер не знает про API, просто обновляем контент по-старому:

  if (!document.startViewTransition) {
    container.innerHTML = newHTML;
    return;
  }
View Transitions API: полное руководство по плавным переходам в браузере - 4

  // 2. Если поддержка есть — запускаем магию:

  document.startViewTransition(() => {
View Transitions API: полное руководство по плавным переходам в браузере - 5

    // Внутри этого колбэка мы делаем всё то же самое:

    // просто меняем DOM. Браузер сам поймёт, что изменилось.

container.innerHTML = newHTML;
  });
}
View Transitions API: полное руководство по плавным переходам в браузере - 6

Что здесь происходит? Мы говорим браузеру: «Запиши, как всё выглядит сейчас. Потом выполни мою функцию updateContent. А затем плавно преврати старую картинку в новую».

По ссылке на демо доступен готовый рабочий пример View Transitions API, который включает HTML-структуру, CSS-оформление, JavaScript-логику:

View Transitions API: полное руководство по плавным переходам в браузере - 7

Управление процессом через промисы

startViewTransition возвращает объект с промисами, которые позволяют тонко контролировать жизненный цикл перехода. Это полезно, если вам нужно скоординировать JS-анимацию с переходом.

JavaScript

const transition = document.startViewTransition(() => updateDOM());
View Transitions API: полное руководство по плавным переходам в браузере - 8

// 1. DOM обновлён, но анимация ещё не началась:

await transition.updateCallbackDone;
View Transitions API: полное руководство по плавным переходам в браузере - 9

// 2. Псевдоэлементы созданы, анимация готова к старту:

await transition.ready;
View Transitions API: полное руководство по плавным переходам в браузере - 10

// 3. Анимация полностью завершена, псевдоэлементы удалены:

await transition.finished;
View Transitions API: полное руководство по плавным переходам в браузере - 11

Кастомизация анимаций через CSS

По умолчанию браузер делает простое перекрёстное затухание (cross-fade). Чтобы сделать красиво, нам понадобится CSS и свойство view-transition-name.

1. Связывание элементов.

Чтобы анимировать конкретный блок отдельно от остальной страницы, дайте ему уникальное имя:

CSS

card {
  view-transition-name: card-hero;
}
View Transitions API: полное руководство по плавным переходам в браузере - 12

2. Настройка анимации.

Теперь мы можем обратиться к псевдоэлементам этого блока и задать свои кейфреймы:

CSS

/* Старое состояние улетает */

::view-transition-old(card-hero) {
  animation: 300ms ease-out both fade-scale-out;
}
View Transitions API: полное руководство по плавным переходам в браузере - 13

/* Новое состояние прилетает */

::view-transition-new(card-hero) {
  animation: 300ms ease-in both fade-scale-in;
}

@keyframes fade-scale-out {
  to { opacity: 0; transform: scale(0.9); }
}

@keyframes fade-scale-in {
  from { opacity: 0; transform: scale(1.1); }
}
View Transitions API: полное руководство по плавным переходам в браузере - 14

По ссылке доступен код примера, где карточка меняет текст с кастомной анимацией масштабирования и прозрачности:

View Transitions API: полное руководство по плавным переходам в браузере - 15

Shared elements: подходы и опасные ловушки

Это та самая киллер-фича: если на одной странице у картинки view-transition-name: hero, и на новой странице у (другой!) картинки тоже view-transition-name: hero, то браузер автоматически рассчитает их позиции и размеры, плавно трансформируя первую во вторую.

И вот тут главная ловушка: view-transition-name должен быть уникальным на странице в каждый момент времени. Если вы зададите всем карточкам в списке имя hero, то переход просто сломается.

Вот какое решение у этой ситуации может быть: назначайте имя динамически перед кликом.

JavaScript

function openProduct(id) {
  const cardImg = document.querySelector(`[data-id="${id}"] img`);
View Transitions API: полное руководство по плавным переходам в браузере - 16

 // 1. Временно даём имя кликнутому элементу:

  cardImg.style.viewTransitionName = 'hero';
  
  const transition = document.startViewTransition(() => {
    renderProductPage(id);
View Transitions API: полное руководство по плавным переходам в браузере - 17

  // На новой странице у большой картинки в CSS уже должно быть прописано view-transition-name: hero

});
View Transitions API: полное руководство по плавным переходам в браузере - 18

  // 2. Убираем имя после завершения, чтобы не было конфликтов:

  transition.finished.then(() => {
    cardImg.style.viewTransitionName = '';
  });
}
View Transitions API: полное руководство по плавным переходам в браузере - 19

Продвинутые сценарии

Асинхронные обновления и Suspense

View Transition API ожидает, что callback выполнится синхронно или вернёт промис. Браузер будет держать «замороженный» экран до завершения этого промиса.

JavaScript

document.startViewTransition(async () => {
  const data = await fetchData(); // Ждём сеть
  renderContent(data);            // Обновляем DOM
});
View Transitions API: полное руководство по плавным переходам в браузере - 20

Небольшой совет: не затягивайте ожидание, иначе пользователь подумает, что интерфейс завис.

Работа с фреймворками (React)

React обновляет DOM асинхронно и пакетами (batching). Чтобы View Transitions API сработал корректно, обновление DOM должно произойти внутри колбэка.

В React для этого используем flushSync, который заставляет React применить изменения немедленно:

JavaScript

import { flushSync } from 'react-dom';
document.startViewTransition(() => {
  flushSync(() => {
    setState(newState);
  });
});
View Transitions API: полное руководство по плавным переходам в браузере - 21

Note: В экспериментальных сборках React ведётся работа над нативной интеграцией, но в стабильный релиз этот API пока не вошёл. Поэтому сейчас рабочим решением для продакшена остаётся ручной вызов нативного document.startViewTransition в связке с flushSync.

Cross-document (MPA) переходы

С версии Chrome 126+ (и Safari 18+) можно анимировать переходы между разными HTML-страницами. Больше не нужно делать SPA, чтобы получить плавные переходы!

Достаточно добавить CSS на обеих страницах:

CSS

@view-transition {
  navigation: auto;
}
View Transitions API: полное руководство по плавным переходам в браузере - 22

Для мгновенной загрузки следующей страницы используйте Speculation Rules API (prerender):

HTML

<script type="speculationrules">
{ "prerender": [{ "source": "document", "where": { "selector": "a" } }] }
</script>
View Transitions API: полное руководство по плавным переходам в браузере - 23

Чеклист перед выходом в продакшен

API мощный, но при неправильном использовании легко сломать вёрстку или доступность. Проверьте себя перед релизом:

  • Обязательно проверяйте наличие document.startViewTransition.

  • Уважайте пользователей, у которых отключена анимация. Вот пример кода, который делает переходы мгновенными для тех, кто выбрал системную настройку prefers-reduced-motion:

CSS

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation-duration: 0.01ms !important;
    }
}
View Transitions API: полное руководство по плавным переходам в браузере - 24
  • Ориентируйтесь примерно на эту производительность:

    Длительность анимации не должна превышать 300-400 мс (это влияет на метрику INP).

    Обязательно должен быть протестирован LCP на мобильных устройствах.

  • Убедитесь, что нет дублей view-transition-name.

  • Проверьте отсутствие сдвигов контента и при проблемах с изображениями используйте object-fit: cover.

Итог

View Transitions API — это тот редкий случай, когда веб-стандарты догнали веб-приложения. Мы получили инструмент, который берёт на себя самую сложную математику анимаций (синхронизацию, позиционирование, композитинг) и оставляет нам чистое творчество.

Статус поддержки:

  • Chrome/Edge — полная поддержка (v111+).

  • Safari — поддержка появляется в Safari 18, на момент написания частично и с ограничениями.

  • Firefox — в активной разработке. API уже доступен в экспериментальных сборках (Nightly), но в стабильный релиз пока не вошёл.

С правильным фолбэком эту технологию можно и нужно использовать уже сегодня, чтобы делать веб чуть более приятным местом.

Автор: sergodeem

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js