- PVSM.RU - https://www.pvsm.ru -

Помните 2015 год? Тогда Пол Льюис из Google представил концепцию FLIP [1] (First, Last, Invert, Play) — революционный на тот момент способ делать анимации интерфейса со стабильными 60 fps. Идея была гениальной: вместо того, чтобы анимировать свойства разметки (width, top), мы измеряем начальное и конечное состояние элементов, а затем анимируем только transform.
В теории звучало отлично, но на практике реализация была трудоёмкой: требовались вычисления координат getBoundingClientRect, ручной контрольrequestAnimationFrame и бесконечные вычисления таймингов. В итоге большинство разработчиков просто отказывались от сложных переходов, потому что цена поддержки такого кода была слишком высока.
Прошло восемь лет, и в Chrome 111 появился View Transitions API [2]. По сути, браузер наконец-то научился делать FLIP за нас. API решает те же задачи, что FLIP, но принципиально другим способом. Джейк Арчибальд на Chrome Dev Summit [3] показал, как три строчки нативного кода заменяют сотни строк самописных скриптов.
Давайте разберёмся, как это работает, где подводные камни и можно ли использовать в рабочей среде прямо сейчас?
Если вы когда-либо пользовались веб-приложениями на 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) /* снимок ПОСЛЕ */
Поскольку мы манипулируем «картинками» (снимками), а не живыми DOM-узлами, анимация, как правило, выполняется на уровне композитора. Отсюда и стабильная производительность.
Давайте посмотрим на код, как внедрить View Transitions в существующий проект. Это на самом деле проще, чем кажется.
Представьте типичную ситуацию в SPA: у вас есть функция, которая получает новый контент и просто заменяет старый HTML в контейнере. Обычно это происходит мгновенно и резко.
Чтобы добавить плавную анимацию, нам не нужно переписывать логику рендеринга. Нам нужно всего лишь «обернуть» момент изменения DOM в вызов document.startViewTransition.
Вот универсальный паттерн, который (важно!) не сломает сайт в большинстве случаев при простом DOM-обновлении:
function updateContent(newHTML) {
const container = document.querySelector('#container');
// 1. Проверяем поддержку (Progressive Enhancement)
// Если браузер не знает про API, просто обновляем контент по-старому:
if (!document.startViewTransition) {
container.innerHTML = newHTML;
return;
}
// 2. Если поддержка есть — запускаем магию:
document.startViewTransition(() => {
// Внутри этого колбэка мы делаем всё то же самое:
// просто меняем DOM. Браузер сам поймёт, что изменилось.
container.innerHTML = newHTML;
});
}
Что здесь происходит? Мы говорим браузеру: «Запиши, как всё выглядит сейчас. Потом выполни мою функцию
updateContent. А затем плавно преврати старую картинку в новую».
По ссылке [5] на демо доступен готовый рабочий пример View Transitions API, который включает HTML-структуру, CSS-оформление, JavaScript-логику:

startViewTransition возвращает объект с промисами, которые позволяют тонко контролировать жизненный цикл перехода. Это полезно, если вам нужно скоординировать JS-анимацию с переходом.
JavaScript
const transition = document.startViewTransition(() => updateDOM());
// 1. DOM обновлён, но анимация ещё не началась:
await transition.updateCallbackDone;
// 2. Псевдоэлементы созданы, анимация готова к старту:
await transition.ready;
// 3. Анимация полностью завершена, псевдоэлементы удалены:
await transition.finished;
По умолчанию браузер делает простое перекрёстное затухание (cross-fade). Чтобы сделать красиво, нам понадобится CSS и свойство view-transition-name.
Чтобы анимировать конкретный блок отдельно от остальной страницы, дайте ему уникальное имя:
CSS
card {
view-transition-name: card-hero;
}
Теперь мы можем обратиться к псевдоэлементам этого блока и задать свои кейфреймы:
CSS
/* Старое состояние улетает */
::view-transition-old(card-hero) {
animation: 300ms ease-out both fade-scale-out;
}
/* Новое состояние прилетает */
::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); }
}
По ссылке [6] доступен код примера, где карточка меняет текст с кастомной анимацией масштабирования и прозрачности:

Это та самая киллер-фича: если на одной странице у картинки view-transition-name: hero, и на новой странице у (другой!) картинки тоже view-transition-name: hero, то браузер автоматически рассчитает их позиции и размеры, плавно трансформируя первую во вторую.
И вот тут главная ловушка: view-transition-name должен быть уникальным на странице в каждый момент времени. Если вы зададите всем карточкам в списке имя hero, то переход просто сломается.
Вот какое решение у этой ситуации может быть: назначайте имя динамически перед кликом.
JavaScript
function openProduct(id) {
const cardImg = document.querySelector(`[data-id="${id}"] img`);
// 1. Временно даём имя кликнутому элементу:
cardImg.style.viewTransitionName = 'hero';
const transition = document.startViewTransition(() => {
renderProductPage(id);
// На новой странице у большой картинки в CSS уже должно быть прописано view-transition-name: hero
});
// 2. Убираем имя после завершения, чтобы не было конфликтов:
transition.finished.then(() => {
cardImg.style.viewTransitionName = '';
});
}
View Transition API ожидает, что callback выполнится синхронно или вернёт промис. Браузер будет держать «замороженный» экран до завершения этого промиса.
JavaScript
document.startViewTransition(async () => {
const data = await fetchData(); // Ждём сеть
renderContent(data); // Обновляем DOM
});
Небольшой совет: не затягивайте ожидание, иначе пользователь подумает, что интерфейс завис.
React обновляет DOM асинхронно и пакетами (batching). Чтобы View Transitions API сработал корректно, обновление DOM должно произойти внутри колбэка.
В React для этого используем flushSync, который заставляет React применить изменения немедленно:
JavaScript
import { flushSync } from 'react-dom';
document.startViewTransition(() => {
flushSync(() => {
setState(newState);
});
});
Note: В экспериментальных сборках React ведётся работа над нативной интеграцией, но в стабильный релиз этот API пока не вошёл. Поэтому сейчас рабочим решением для продакшена остаётся ручной вызов нативного
document.startViewTransitionв связке сflushSync.
С версии Chrome 126+ (и Safari 18+) можно анимировать переходы между разными HTML-страницами. Больше не нужно делать SPA, чтобы получить плавные переходы!
Достаточно добавить CSS на обеих страницах:
CSS
@view-transition {
navigation: auto;
}
Для мгновенной загрузки следующей страницы используйте Speculation Rules API (prerender):
HTML
<script type="speculationrules">
{ "prerender": [{ "source": "document", "where": { "selector": "a" } }] }
</script>
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;
}
}
Ориентируйтесь примерно на эту производительность:
Длительность анимации не должна превышать 300-400 мс (это влияет на метрику INP).
Обязательно должен быть протестирован LCP на мобильных устройствах.
Убедитесь, что нет дублей view-transition-name.
Проверьте отсутствие сдвигов контента и при проблемах с изображениями используйте object-fit: cover.
View Transitions API — это тот редкий случай, когда веб-стандарты догнали веб-приложения. Мы получили инструмент, который берёт на себя самую сложную математику анимаций (синхронизацию, позиционирование, композитинг) и оставляет нам чистое творчество.
Статус поддержки:
Chrome/Edge — полная поддержка (v111+).
Safari — поддержка появляется в Safari 18, на момент написания частично и с ограничениями.
Firefox — в активной разработке. API уже доступен в экспериментальных сборках (Nightly), но в стабильный релиз пока не вошёл.
С правильным фолбэком эту технологию можно и нужно использовать уже сегодня, чтобы делать веб чуть более приятным местом.
Автор: sergodeem
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/shared-elements/444716
Ссылки в тексте:
[1] Пол Льюис из Google представил концепцию FLIP: https://youtube.com/watch?v=RCFQu0hK6bU&embeds_referring_euri=https%3A%2F%2Faerotwist.com%2F&source_ve_path=OTY3MTQ
[2] в Chrome 111 появился View Transitions API: https://habr.com/ru/articles/721292/
[3] Джейк Арчибальд на Chrome Dev Summit: https://developer.chrome.com/docs/web-platform/view-transitions?hl=ru#:~:text=Transitions%20created%20with%20the%20View,same%20building%20blocks%20and%20principles:
[4] Image: https://sourcecraft.dev/
[5] ссылке: https://codepen.io/xdkbnvns-the-sasster/pen/pvbrexN
[6] ссылке: https://codepen.io/xdkbnvns-the-sasster/pen/GgqvWzb
[7] Источник: https://habr.com/ru/articles/996498/?utm_source=habrahabr&utm_medium=rss&utm_campaign=996498
Нажмите здесь для печати.