View Transitions
Переход transition анимирует смену свойства: цвета, размера. Но когда содержимое страницы заменяется целиком — новый текст, другой порядок карточек, другая страница, — анимировать нечего. View Transitions (переходы между видами) решают это: браузер запоминает, как страница выглядела до изменения и после, и плавно превращает одно в другое.
Первый переход
Переход запускают из JavaScript методом document.startViewTransition(). Ему передают функцию, которая меняет страницу, — остальное браузер делает сам. Нажмите на кнопку:
<style>
.tip { padding: 16px; background: lavender; }
</style>
<button onclick="nextTip()">Следующий совет</button>
<p class="tip" id="tip">Пейте больше воды.</p>
<script>
const tips = ["Пейте больше воды.", "Делайте перерывы.", "Гуляйте каждый день."];
let index = 0;
function nextTip() {
document.startViewTransition(() => {
index = (index + 1) % tips.length;
document.querySelector("#tip").textContent = tips[index];
});
}
</script>Старый текст тает, новый проявляется. Без перехода он сменился бы мгновенно.
Псевдоэлементы перехода
На время перехода браузер строит над страницей дерево псевдоэлементов — снимков старого и нового вида. Их оформляют в CSS, как обычные элементы:
| Псевдоэлемент | Что это |
|---|---|
::view-transition | слой над страницей, в котором идёт переход |
::view-transition-group(имя) | группа одного элемента, двигает и меняет размер |
::view-transition-old(имя) | снимок до изменения |
::view-transition-new(имя) | снимок после изменения |
Вся страница по умолчанию называется root. Замедлим переход из прошлого примера до секунды:
<style>
.tip { padding: 16px; background: lavender; }
::view-transition-old(root),
::view-transition-new(root) { animation-duration: 1s; }
</style>
<button onclick="document.startViewTransition(() => document.querySelector('#tip').textContent = 'Делайте перерывы.')">Сменить совет</button>
<p class="tip" id="tip">Пейте больше воды.</p>view-transition-name
Свойство view-transition-name даёт элементу собственное имя. Такой элемент анимируется отдельно от страницы: браузер плавно переносит его со старого места на новое и меняет размер:
<style>
.stage { display: flex; height: 120px; padding: 8px; background: whitesmoke; }
.ball { width: 40px; height: 40px; border-radius: 50%; background: tomato; view-transition-name: ball; }
.stage.moved { justify-content: flex-end; align-items: flex-end; }
.stage.moved .ball { width: 80px; height: 80px; }
</style>
<button onclick="document.startViewTransition(() => document.querySelector('#stage').classList.toggle('moved'))">Переместить</button>
<div class="stage" id="stage"><div class="ball"></div></div>Класс переключается мгновенно, а мяч всё равно летит и растёт — эту анимацию построил браузер.
Поддержка браузерами
Переходы внутри страницы работают во всех основных браузерах с конца 2025 года — Firefox добавил их последним. Для старых браузеров проверяют, есть ли метод, и меняют страницу без анимации:
<button onclick="update()">Обновить</button>
<p id="text">Было</p>
<script>
function update() {
const change = () => document.querySelector("#text").textContent = "Стало";
if (!document.startViewTransition) {
change();
return;
}
document.startViewTransition(change);
}
</script>Переходы между страницами
Тот же эффект возможен при переходе по обычной ссылке на другую страницу сайта — без JavaScript. Для этого в CSS обеих страниц пишут правило @view-transition { navigation: auto; }. Браузер свяжет старую и новую страницу, а элементы с одинаковым view-transition-name перелетят на новое место. Показать это в окне примера нельзя: нужны две настоящие страницы одного сайта. На момент написания такие переходы поддерживают Chrome, Edge и Safari, а в остальных браузерах страница просто открывается без анимации.
Частая ошибка
Одно имя у двух элементов. view-transition-name должно быть уникальным на странице: если имя повторяется, браузер отменяет анимацию, и страница меняется рывком:
<style>
.box { width: 60px; height: 40px; margin: 4px 0; background: steelblue; view-transition-name: box; }
.wide { width: 200px; }
</style>
<button onclick="document.startViewTransition(() => document.querySelectorAll('.box').forEach(b => b.classList.toggle('wide')))">Изменить</button>
<div class="box"></div>
<div class="box"></div>Блоки расширяются без анимации. Если элементов много, дайте каждому своё имя: card-1, card-2 и так далее.