Отключете плавни уеб изживявания, подобни на приложение. Това изчерпателно ръководство изследва мощните CSS View Transition псевдоелементи за стилизиране на динамични преходи между страници, с практически примери и най-добри практики.
Овладяване на CSS View Transitions: Подробен поглед върху стилизирането на псевдоелементи
В постоянно развиващия се свят на уеб разработката, стремежът към безпроблемно, плавно и ангажиращо потребителско изживяване е константа. Години наред разработчиците се стремят да преодолеят разликата между уеб и нативните приложения, особено по отношение на гладкостта на преходите между страниците. Традиционната уеб навигация често води до рязко презареждане на цялата страница – празен бял екран, който за момент нарушава потапянето на потребителя. Едностраничните приложения (SPA) смекчиха това, но създаването на персонализирани, смислени преходи остана сложна и често нестабилна задача, силно зависима от JavaScript библиотеки и сложно управление на състоянието.
Тук се появява CSS View Transitions API – революционна технология, готова да промени начина, по който обработваме промените в потребителския интерфейс в уеб. Този мощен API предоставя прост, но изключително гъвкав механизъм за анимиране между различни състояния на DOM, което улеснява повече от всякога създаването на изпипаните, подобни на приложение изживявания, които потребителите очакват. В основата на силата на този API е набор от нови CSS псевдоелементи. Това не са типичните ви селектори; те са динамични, временни елементи, генерирани от браузъра, за да ви дадат детайлен контрол върху всяка фаза на прехода. Това ръководство ще ви потопи в дървото на тези псевдоелементи, изследвайки как да стилизирате всеки компонент, за да създадете зашеметяващи, производителни и достъпни анимации за глобална аудитория.
Анатомия на View Transition
Преди да можем да стилизираме преход, трябва да разберем какво се случва „под капака“, когато той се задейства. Когато инициирате view transition (например чрез извикване на document.startViewTransition()), браузърът извършва поредица от стъпки:
- Заснемане на старото състояние: Браузърът прави „снимка“ на текущото състояние на страницата.
- Актуализиране на DOM: Вашият код след това прави промените си в DOM (напр. навигиране към нов изглед, добавяне или премахване на елементи).
- Заснемане на новото състояние: След като актуализацията на DOM приключи, браузърът прави снимка на новото състояние.
- Изграждане на дървото от псевдоелементи: След това браузърът конструира временно дърво от псевдоелементи в овърлея на страницата. Това дърво съдържа заснетите изображения на старото и новото състояние.
- Анимиране: Към тези псевдоелементи се прилагат CSS анимации, създавайки плавен преход от старото към новото състояние. По подразбиране се използва просто кръстосано избледняване (cross-fade).
- Почистване: След като анимациите приключат, дървото от псевдоелементи се премахва и потребителят може да взаимодейства с новия, жив DOM.
Ключът към персонализирането е това временно дърво от псевдоелементи. Мислете за него като за набор от слоеве в инструмент за дизайн, временно поставени върху вашата страница. Имате пълен CSS контрол върху тези слоеве. Ето структурата, с която ще работите:
- ::view-transition
- ::view-transition-group(*)
- ::view-transition-image-pair(*)
- ::view-transition-old(*)
- ::view-transition-new(*)
- ::view-transition-image-pair(*)
- ::view-transition-group(*)
Нека разгледаме какво представлява всеки от тези псевдоелементи.
Участниците: Псевдоелементите
::view-transition: Това е коренът на цялата структура. Той е единичен елемент, който запълва viewport-а и седи върху цялото останало съдържание на страницата. Той действа като контейнер за всички групи в преход и е чудесно място за задаване на общи свойства на прехода като продължителност или функция за синхронизация (timing function).
::view-transition-group(*): За всеки отделен елемент в преход (идентифициран със CSS свойството view-transition-name) се създава група. Този псевдоелемент е отговорен за анимирането на позицията и размера на своето съдържание. Ако имате карта, която се движи от едната страна на екрана до другата, именно ::view-transition-group е това, което всъщност се движи.
::view-transition-image-pair(*): Вложен в групата, този елемент действа като контейнер и маска за изрязване (clipping mask) за стария и новия изглед. Основната му роля е да поддържа ефекти като border-radius или transform по време на анимацията и да управлява анимацията по подразбиране за кръстосано избледняване.
::view-transition-old(*): Това представлява „снимката“ или рендирания изглед на елемента в неговото старо състояние (преди промяната в DOM). По подразбиране той се анимира от opacity: 1 до opacity: 0.
::view-transition-new(*): Това представлява „снимката“ или рендирания изглед на елемента в неговото ново състояние (след промяната в DOM). По подразбиране той се анимира от opacity: 0 до opacity: 1.
Коренът: Стилизиране на псевдоелемента ::view-transition
Псевдоелементът ::view-transition е платното, върху което се рисува цялата ви анимация. Като контейнер от най-високо ниво, той е идеалното място за дефиниране на свойства, които трябва да се прилагат глобално за прехода. По подразбиране браузърът предоставя набор от анимации, но можете лесно да ги промените.
Например, преходът по подразбиране е кръстосано избледняване, което трае 250 милисекунди. Ако искате да промените това за всеки преход на вашия сайт, можете да насочите коренния псевдоелемент:
::view-transition {
animation-duration: 500ms;
animation-timing-function: ease-in-out;
}
Това просто правило сега кара всички избледнявания на страници по подразбиране да отнемат два пъти повече време и да използват крива 'ease-in-out', което им придава малко по-различно усещане. Въпреки че можете да прилагате сложни анимации тук, обикновено е най-добре да се използва за дефиниране на универсална синхронизация и плавност, оставяйки по-специфичните псевдоелементи да се справят с детайлната хореография.
Групиране и именуване: Силата на `view-transition-name`
По подразбиране, без допълнителна работа, View Transition API осигурява кръстосано избледняване за цялата страница. Това се обработва от една-единствена група псевдоелементи за корена. Истинската сила на API се отключва, когато искате да направите преход на конкретни, индивидуални елементи между състоянията. Например, да накарате миниатюра на продукт на страница със списък да се разрасне и премести безпроблемно в позицията на основното изображение на продукта на страница с подробности.
За да кажете на браузъра, че два елемента в различни състояния на DOM са концептуално еднакви, използвате CSS свойството view-transition-name. Това свойство трябва да бъде приложено както към началния, така и към крайния елемент.
/* В gallery-item.css */
.product-thumbnail {
view-transition-name: product-image;
}
/* В detail-page.css */
.main-product-image {
view-transition-name: product-image;
}
Като давате на двата елемента едно и също уникално име (в този случай 'product-image'), вие инструктирате браузъра: „Вместо просто да избледнее старата страница и да се появи новата, създай специален преход за този конкретен елемент.“ Сега браузърът ще генерира специална ::view-transition-group(product-image), за да обработи анимацията му отделно от избледняването на корена. Това е основната концепция, която позволява популярния ефект на преход „морфинг“ или „споделен елемент“.
Важна забележка: Във всеки даден момент по време на преход, view-transition-name трябва да бъде уникално. Не можете да имате два видими елемента с едно и също име едновременно.
Стилизиране в дълбочина: Основните псевдоелементи
След като нашите елементи са именувани, можем да се потопим в стилизирането на конкретните псевдоелементи, които браузърът генерира за тях. Тук можете да създадете наистина персонализирани и изразителни анимации.
`::view-transition-group(name)`: Движещият се
Единствената отговорност на групата е да премине от размера и позицията на стария елемент към размера и позицията на новия елемент. Тя не съдържа визуалния облик на съдържанието, а само неговата ограничителна кутия. Мислете за нея като за движеща се рамка.
По подразбиране браузърът анимира свойствата ѝ transform и width/height. Можете да промените това, за да създадете различни ефекти. Например, можете да добавите дъга към движението ѝ, като я анимирате по извита пътека, или да я накарате да се мащабира нагоре и надолу по време на пътуването си.
::view-transition-group(product-image) {
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
В този пример прилагаме специфична функция за плавност само към движението на изображението на продукта, което го кара да се чувства по-динамично и физическо, без да засяга избледняването по подразбиране на останалата част от страницата.
`::view-transition-image-pair(name)`: Изрязващият и избледняващият
Вложена в движещата се група, двойката изображения (image-pair) държи стария и новия изглед. Тя действа като маска за изрязване, така че ако вашият елемент има border-radius, image-pair гарантира, че съдържанието остава изрязано с този радиус по време на анимацията на размера и позицията. Другата ѝ основна задача е да организира кръстосаното избледняване по подразбиране между старото и новото съдържание.
Може да искате да стилизирате този елемент, за да осигурите визуална последователност или да създадете по-напреднали ефекти. Ключово свойство, което трябва да се вземе предвид, е isolation: isolate. Това е от решаващо значение, ако планирате да използвате напреднали mix-blend-mode ефекти върху децата (старо и ново), тъй като създава нов контекст на подреждане и предотвратява смесването да засяга елементи извън групата на прехода.
::view-transition-image-pair(product-image) {
isolation: isolate;
}
`::view-transition-old(name)` и `::view-transition-new(name)`: Звездите на шоуто
Това са псевдоелементите, които представляват визуалния облик на вашия елемент преди и след промяната в DOM. Тук ще се случи по-голямата част от вашата персонализирана анимационна работа. По подразбиране браузърът изпълнява проста анимация на кръстосано избледняване върху тях, използвайки opacity и mix-blend-mode. За да създадете персонализирана анимация, първо трябва да изключите тази по подразбиране.
::view-transition-old(name),
::view-transition-new(name) {
animation: none;
}
След като анимацията по подразбиране е деактивирана, сте свободни да приложите своя собствена. Нека разгледаме няколко често срещани модела.
Персонализирана анимация: Плъзгане
Вместо кръстосано избледняване, нека накараме съдържанието на контейнер да се плъзне. Например, когато навигираме между статии, искаме текстът на новата статия да се плъзне отдясно, докато старият текст се плъзга наляво.
Първо, дефинирайте ключовите кадри (keyframes):
@keyframes slide-from-right {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
@keyframes slide-to-left {
from { transform: translateX(0); }
to { transform: translateX(-100%); }
}
Сега приложете тези анимации към стария и новия псевдоелемент за именувания елемент 'article-content'.
::view-transition-old(article-content) {
animation: 300ms ease-out forwards slide-to-left;
}
::view-transition-new(article-content) {
animation: 300ms ease-out forwards slide-from-right;
}
Персонализирана анимация: 3D обръщане
За по-драматичен ефект можете да създадете 3D обръщане на карта. Това изисква анимиране на свойството transform с rotateY, както и управление на backface-visibility.
/* Групата се нуждае от 3D контекст */
::view-transition-group(card-flipper) {
transform-style: preserve-3d;
}
/* Двойката изображения също трябва да запази 3D контекста */
::view-transition-image-pair(card-flipper) {
transform-style: preserve-3d;
}
/* Старият изглед се обръща от 0 до -180 градуса */
::view-transition-old(card-flipper) {
animation: 600ms ease-in forwards flip-out;
backface-visibility: hidden;
}
/* Новият изглед се обръща от 180 до 0 градуса */
::view-transition-new(card-flipper) {
animation: 600ms ease-out forwards flip-in;
backface-visibility: hidden;
}
@keyframes flip-out {
from { transform: rotateY(0deg); }
to { transform: rotateY(-180deg); }
}
@keyframes flip-in {
from { transform: rotateY(180deg); }
to { transform: rotateY(0deg); }
}
Практически примери и напреднали техники
Теорията е полезна, но практическото приложение е мястото, където наистина учим. Нека разгледаме някои често срещани сценарии и как да ги решим с псевдоелементи за view transition.
Пример: „Морфираща“ миниатюра на карта
Това е класическият преход със споделен елемент. Представете си галерия с потребителски профили. Всеки профил е карта с аватар. Когато кликнете върху карта, навигирате до страница с подробности, където същият този аватар е показан на видно място в горната част.
Стъпка 1: Задайте имена
На страницата с галерията, изображението на аватара получава име. Името трябва да е уникално за всяка карта, например, базирано на ID на потребителя.
/* В gallery-item.css */
.card-avatar { view-transition-name: avatar-user-123; }
На страницата с подробности за профила, големият аватара в хедъра получава абсолютно същото име.
/* В profile-page.css */
.profile-header-avatar { view-transition-name: avatar-user-123; }
Стъпка 2: Персонализирайте анимацията
По подразбиране браузърът ще премести и мащабира аватара, но също така ще направи кръстосано избледняване на съдържанието. Ако изображението е идентично, това избледняване е ненужно и може да причини леко трептене. Можем да го деактивираме.
/* Звездата (*) тук е заместител за всяка именувана група */
::view-transition-image-pair(*) {
/* Деактивиране на избледняването по подразбиране */
animation-duration: 0s;
}
Чакайте, ако деактивираме избледняването, как се сменя съдържанието? За споделени елементи, където старият и новият изглед са идентични, браузърът е достатъчно умен, за да използва само един изглед за целия преход. `image-pair` по същество съдържа само едно изображение, така че деактивирането на избледняването просто разкрива тази оптимизация. За елементи, където съдържанието действително се променя, ще ви е необходима персонализирана анимация на мястото на избледняването по подразбиране.
Справяне с промени в съотношението на страните
Често срещано предизвикателство възниква, когато елемент в преход променя своето съотношение на страните. Например, миниатюра в пейзажен формат 16:9 на страница със списък може да премине към квадратен аватар 1:1 на страницата с подробности. Поведението на браузъра по подразбиране е да анимира ширината и височината независимо, което води до това, че изображението изглежда сплескано или разтегнато по време на прехода.
Решението е елегантно. Оставяме ::view-transition-group да се справи с промяната на размера и позицията, но променяме стила на старите и новите изображения в него.
Целта е да накараме старите и новите „снимки“ да запълнят своя контейнер, без да се изкривяват. Можем да направим това, като зададем тяхната ширина и височина на 100% и позволим на свойството object-fit по подразбиране на браузъра (което се наследява от оригиналния елемент) да се справи правилно с мащабирането.
::view-transition-old(hero-image),
::view-transition-new(hero-image) {
/* Предотвратяване на изкривяване чрез запълване на контейнера */
width: 100%;
height: 100%;
/* Променете кръстосаното избледняване по подразбиране, за да видите ефекта ясно */
animation: none;
}
С този CSS, `image-pair` ще анимира плавно своето съотношение на страните, а изображенията вътре ще бъдат правилно изрязани или с черни ленти (в зависимост от тяхната `object-fit` стойност), точно както биха били в нормален контейнер. След това можете да добавите свои собствени персонализирани анимации, като кръстосано избледняване, върху тази коригирана геометрия.
Отстраняване на грешки и поддръжка от браузъри
Стилизирането на елементи, които съществуват само за части от секундата, може да бъде трудно. За щастие, съвременните браузъри предоставят отлични инструменти за разработчици за това. В Chrome или Edge DevTools можете да отидете в панела „Animations“ и когато задействате view transition, можете да го поставите на пауза. С поставената на пауза анимация, можете да използвате панела „Elements“, за да инспектирате цялото дърво на псевдоелемента `::view-transition`, точно както всяка друга част от DOM. Можете да видите прилаганите стилове и дори да ги променяте в реално време, за да усъвършенствате анимациите си.
Към края на 2023 г. View Transitions API се поддържа в базирани на Chromium браузъри (Chrome, Edge, Opera). Внедрявания са в ход за Firefox и Safari. Това го прави перфектен кандидат за прогресивно подобряване. Потребителите с поддържани браузъри получават приятно, подобрено изживяване, докато потребителите на други браузъри получават стандартната, незабавна навигация. Можете да проверите за поддръжка в CSS:
@supports (view-transition: none) {
/* Всички стилове за view-transition се поставят тук */
::view-transition-old(my-element) { ... }
}
Най-добри практики за глобална аудитория
Когато внедрявате анимации, е жизненоважно да се вземат предвид разнообразния кръг от потребители и устройства по целия свят.
Производителност: Анимациите трябва да бъдат бързи и плавни. Придържайте се към анимиране на CSS свойства, които са евтини за обработка от браузъра, предимно transform и opacity. Анимирането на свойства като width, height или margin може да предизвика преизчисления на оформлението при всеки кадър, което води до накъсване и лошо изживяване, особено на по-слаби устройства.
Достъпност: Някои потребители изпитват гадене при движение или дискомфорт от анимации. Всички основни операционни системи предоставят потребителска настройка за намаляване на движението. Трябва да уважаваме това. Медийната заявка prefers-reduced-motion ви позволява да деактивирате или опростите анимациите си за тези потребители.
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
/* Пропуснете всички персонализирани анимации и използвайте бързо, просто избледняване */
animation: none !important;
}
}
Потребителско изживяване (UX): Добрите преходи са целенасочени. Те трябва да насочват вниманието на потребителя и да предоставят контекст за промяната, която се случва в потребителския интерфейс. Анимация, която е твърде бавна, може да направи приложението да се усеща мудно, докато твърде крещяща може да бъде разсейваща. Стремете се към продължителност на преходите между 200ms и 500ms. Целта е анимацията повече да се усеща, отколкото да се вижда.
Заключение: Бъдещето е плавно
CSS View Transitions API, и по-специално неговото мощно дърво от псевдоелементи, представлява монументален скок напред за уеб потребителските интерфейси. Той предоставя на разработчиците нативен, производителен и силно персонализируем набор от инструменти за създаване на плавни, състояние-съхраняващи преходи, които някога са били изключителна област на нативните приложения. Като разберете ролите на ::view-transition, ::view-transition-group и двойките old/new изображения, можете да преминете отвъд простите избледнявания и да хореографирате сложни, смислени анимации, които подобряват използваемостта и радват потребителите.
С разширяването на поддръжката от браузърите, този API ще се превърне в съществена част от инструментариума на съвременния front-end разработчик. Като възприемем неговите възможности и се придържаме към най-добрите практики за производителност и достъпност, можем да изградим уеб, който е не само по-функционален, но и по-красив и интуитивен за всички, навсякъде.