İki farklı belge arasında görünüm geçişi gerçekleştiğinde buna belgeler arası görünüm geçişi denir. Bu durum genellikle çok sayfalı uygulamalarda (MPA) görülür. Chrome 126'dan itibaren Chrome'da dokümanlar arası görünüm geçişleri desteklenir.
Dokümanlar arası görünüm geçişleri, aynı dokümandaki görünüm geçişleriyle aynı yapı taşlarını ve ilkeleri kullanır. Bu durum, özellikle bu şekilde tasarlanmıştır:
- Tarayıcı, hem eski hem de yeni sayfada benzersiz bir
view-transition-nameolan öğelerin anlık görüntülerini alır. - Oluşturma işlemi bastırılırken DOM güncellenir.
- Son olarak, geçişler CSS animasyonlarıyla desteklenir.
Aynı doküman görünümü geçişleriyle karşılaştırıldığında, dokümanlar arası görünüm geçişlerinde görünüm geçişini başlatmak için document.startViewTransition işlevini çağırmanız gerekmez. Bunun yerine, belgeler arası görünüm geçişinin tetikleyicisi, bir sayfadan diğerine yapılan aynı kaynaklı gezinmedir. Bu işlem genellikle web sitenizin kullanıcısı tarafından bir bağlantı tıklanarak gerçekleştirilir.
Başka bir deyişle, iki doküman arasında görünüm geçişi başlatmak için çağrılacak bir API yoktur. Ancak karşılanması gereken iki koşul vardır:
- Her iki doküman da aynı kaynakta bulunmalıdır.
- Görünüm geçişine izin vermek için her iki sayfanın da etkinleştirilmesi gerekir.
Bu koşulların her ikisi de bu belgenin ilerleyen kısımlarında açıklanmaktadır.
Dokümanlar arası görünüm geçişleri, aynı kaynaklı gezinmelerle sınırlıdır.
Dokümanlar arası görünüm geçişleri yalnızca aynı kaynaklı gezinmelerle sınırlıdır. Her iki sayfanın kaynağı aynıysa gezinme aynı kaynaklı olarak kabul edilir.
Bir sayfanın kaynağı, kullanılan şema, ana makine adı ve bağlantı noktasının birleşimidir. Bu konuyla ilgili web.dev adresinde ayrıntılı bilgi verilmektedir.
Örneğin, developer.chrome.com adresinden developer.chrome.com/blog adresine giderken dokümanlar arası görüntüleme geçişi yapabilirsiniz. Çünkü bu iki adres aynı kaynaklıdır.
developer.chrome.com ile www.chrome.com arasında gezinirken bu geçişi kullanamazsınız. Çünkü bunlar kaynaklar arası ve aynı sitedir.
Belgeler arası görünüm geçişleri isteğe bağlıdır
İki belge arasında belge içi görünüm geçişi olması için her iki belgenin de bu geçişe izin vermesi gerekir. Bu işlem, CSS'deki @view-transition at-rule ile yapılır.
@view-transition at-kuralında, belgeler arası aynı kaynaklı gezinmeler için görünüm geçişlerini etkinleştirmek üzere navigation tanımlayıcısını auto olarak ayarlayın.
@view-transition {
navigation: auto;
}
navigation tanımlayıcısını auto olarak ayarlayarak aşağıdaki NavigationType'lar için görünüm geçişlerinin gerçekleşmesine izin vermeyi kabul edersiniz:
traverse- Etkinleştirme, kullanıcı tarafından tarayıcı kullanıcı arayüzü mekanizmaları aracılığıyla başlatılmadıysa
pushveyareplace.
auto'ya dahil edilmeyen gezinme işlemleri arasında, örneğin URL adres çubuğunu kullanarak gezinme veya yer işaretini tıklama ile kullanıcı ya da komut dosyası tarafından başlatılan yeniden yükleme işlemleri yer alır.
Bir gezinme çok uzun sürerse (Chrome'da dört saniyeden fazla) görüntüleme geçişi TimeoutError DOMException ile atlanır.
Belgeler arası görünüm geçişleri demosu
Görünüm geçişlerini kullanarak bir Stack Navigator demosu oluşturma hakkında bilgi edinmek için aşağıdaki demoyu inceleyin. Burada document.startViewTransition() çağrısı yapılmaz. Görünüm geçişleri, bir sayfadan diğerine gidilerek tetiklenir.
Dokümanlar arası görünüm geçişlerini özelleştirme
Dokümanlar arası görünüm geçişlerini özelleştirmek için kullanabileceğiniz bazı web platformu özellikleri vardır.
Bu özellikler, View Transition API spesifikasyonunun bir parçası olmasa da bu spesifikasyonla birlikte kullanılmak üzere tasarlanmıştır.
pageswap ve pagereveal etkinlikleri
Dokümanlar arası görünüm geçişlerini özelleştirmenize olanak tanımak için HTML spesifikasyonu, kullanabileceğiniz iki yeni etkinlik içerir: pageswap ve pagereveal.
Bu iki etkinlik, görünüm geçişinin gerçekleşip gerçekleşmeyeceğinden bağımsız olarak aynı kaynaklı belgeler arası her gezinme için tetiklenir. İki sayfa arasında bir görünüm geçişi gerçekleşmek üzereyse bu etkinliklerdeki viewTransition özelliğini kullanarak ViewTransition nesnesine erişebilirsiniz.
pageswapetkinliği, bir sayfanın son karesi oluşturulmadan önce tetiklenir. Bu özelliği, eski anlık görüntüler alınmadan hemen önce giden sayfada son dakika değişiklikleri yapmak için kullanabilirsiniz.pagerevealetkinliği, bir sayfa başlatıldıktan veya yeniden etkinleştirildikten sonra ancak ilk oluşturma fırsatından önce tetiklenir. Bu araçla, yeni anlık görüntüler alınmadan önce yeni sayfayı özelleştirebilirsiniz.
Örneğin, bu etkinlikleri kullanarak bazı view-transition-name değerlerini hızlıca ayarlayabilir veya değiştirebilir ya da sessionStorage'dan veri yazıp okuyarak görünüm geçişini çalışmadan önce özelleştirebilirsiniz.
let lastClickX, lastClickY;
document.addEventListener('click', (event) => {
if (event.target.tagName.toLowerCase() === 'a') return;
lastClickX = event.clientX;
lastClickY = event.clientY;
});
// Write position to storage on old page
window.addEventListener('pageswap', (event) => {
if (event.viewTransition && lastClick) {
sessionStorage.setItem('lastClickX', lastClickX);
sessionStorage.setItem('lastClickY', lastClickY);
}
});
// Read position from storage on new page
window.addEventListener('pagereveal', (event) => {
if (event.viewTransition) {
lastClickX = sessionStorage.getItem('lastClickX');
lastClickY = sessionStorage.getItem('lastClickY');
}
});
İsterseniz her iki etkinlikte de geçişi atlamayı seçebilirsiniz.
window.addEventListener("pagereveal", async (e) => {
if (e.viewTransition) {
if (goodReasonToSkipTheViewTransition()) {
e.viewTransition.skipTransition();
}
}
}
pageswap ve pagereveal içindeki ViewTransition nesnesi iki farklı nesnedir. Ayrıca çeşitli sözleri farklı şekilde ele alırlar:
pageswap: Belge gizlendikten sonra eskiViewTransitionnesnesi atlanır. Bu durumdaviewTransition.readyreddeder veviewTransition.finishedçözer.pagereveal:updateCallBacksözü bu noktada zaten çözülmüştür.viewTransition.readyveviewTransition.finishedsözlerini kullanabilirsiniz.
Navigasyon etkinleştirme bilgileri
Hem pageswap hem de pagereveal etkinliklerinde, eski ve yeni sayfaların URL'lerine göre de işlem yapabilirsiniz.
Örneğin, MPA Stack Navigator'da kullanılacak animasyon türü, gezinme yoluna bağlıdır:
- Genel bakış sayfasından bir ayrıntılar sayfasına giderken yeni içerik sağdan sola doğru kaydırılmalıdır.
- Ayrıntılar sayfasından genel bakış sayfasına giderken eski içeriğin soldan sağa doğru kayması gerekir.
Bunu yapmak için pageswap durumunda gerçekleşmek üzere olan veya pagereveal durumunda yeni gerçekleşmiş olan gezinme hakkında bilgiye ihtiyacınız vardır.
Bu amaçla tarayıcılar artık aynı kaynaklı gezinme hakkında bilgi içeren NavigationActivation nesnelerini kullanıma sunabilir. Bu nesne, kullanılan gezinme türünü, geçerli ve son hedef geçmişi girişlerini navigation.entries() Navigation API'den alındığı şekilde gösterir.
Etkinleştirilmiş bir sayfada, bu nesneye navigation.activation üzerinden erişebilirsiniz. pageswap etkinliğinde, bu içeriğe e.activation üzerinden erişebilirsiniz.
Görünüm geçişine katılması gereken öğelerde view-transition-name değerlerini ayarlamak için pageswap ve pagereveal etkinliklerinde NavigationActivation bilgilerini kullanan bu Profiller demosuna göz atın.
Bu sayede, listedeki her öğeyi önceden view-transition-name ile süslemeniz gerekmez. Bunun yerine, bu işlem yalnızca ihtiyaç duyulan öğelerde JavaScript kullanılarak tam zamanında gerçekleşir.
Kod aşağıdaki gibidir:
// OLD PAGE LOGIC
window.addEventListener('pageswap', async (e) => {
if (e.viewTransition) {
const targetUrl = new URL(e.activation.entry.url);
// Navigating to a profile page
if (isProfilePage(targetUrl)) {
const profile = extractProfileNameFromUrl(targetUrl);
// Set view-transition-name values on the clicked row
document.querySelector(`#${profile} span`).style.viewTransitionName = 'name';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar';
// Remove view-transition-names after snapshots have been taken
// (this to deal with BFCache)
await e.viewTransition.finished;
document.querySelector(`#${profile} span`).style.viewTransitionName = 'none';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'none';
}
}
});
// NEW PAGE LOGIC
window.addEventListener('pagereveal', async (e) => {
if (e.viewTransition) {
const fromURL = new URL(navigation.activation.from.url);
const currentURL = new URL(navigation.activation.entry.url);
// Navigating from a profile page back to the homepage
if (isProfilePage(fromURL) && isHomePage(currentURL)) {
const profile = extractProfileNameFromUrl(currentURL);
// Set view-transition-name values on the elements in the list
document.querySelector(`#${profile} span`).style.viewTransitionName = 'name';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar';
// Remove names after snapshots have been taken
// so that we're ready for the next navigation
await e.viewTransition.ready;
document.querySelector(`#${profile} span`).style.viewTransitionName = 'none';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'none';
}
}
});
Kod, görünüm geçişi çalıştırıldıktan sonra view-transition-name değerlerini kaldırarak kendi kendini de temizler. Bu sayede sayfa, sonraki gezinme işlemleri için hazır olur ve geçmişte gezinme işlemlerini de gerçekleştirebilir.
Bu konuda yardımcı olması için view-transition-name değerlerini geçici olarak ayarlayan bu yardımcı işlevi kullanın.
const setTemporaryViewTransitionNames = async (entries, vtPromise) => {
for (const [$el, name] of entries) {
$el.style.viewTransitionName = name;
}
await vtPromise;
for (const [$el, name] of entries) {
$el.style.viewTransitionName = '';
}
}
Önceki kod artık aşağıdaki şekilde basitleştirilebilir:
// OLD PAGE LOGIC
window.addEventListener('pageswap', async (e) => {
if (e.viewTransition) {
const targetUrl = new URL(e.activation.entry.url);
// Navigating to a profile page
if (isProfilePage(targetUrl)) {
const profile = extractProfileNameFromUrl(targetUrl);
// Set view-transition-name values on the clicked row
// Clean up after the page got replaced
setTemporaryViewTransitionNames([
[document.querySelector(`#${profile} span`), 'name'],
[document.querySelector(`#${profile} img`), 'avatar'],
], e.viewTransition.finished);
}
}
});
// NEW PAGE LOGIC
window.addEventListener('pagereveal', async (e) => {
if (e.viewTransition) {
const fromURL = new URL(navigation.activation.from.url);
const currentURL = new URL(navigation.activation.entry.url);
// Navigating from a profile page back to the homepage
if (isProfilePage(fromURL) && isHomePage(currentURL)) {
const profile = extractProfileNameFromUrl(currentURL);
// Set view-transition-name values on the elements in the list
// Clean up after the snapshots have been taken
setTemporaryViewTransitionNames([
[document.querySelector(`#${profile} span`), 'name'],
[document.querySelector(`#${profile} img`), 'avatar'],
], e.viewTransition.ready);
}
}
});
İçeriğin oluşturmayı engelleyen kaynaklarla yüklenmesini bekleme
Browser Support
Bazı durumlarda, yeni DOM'da belirli bir öğe mevcut olana kadar sayfanın ilk oluşturulmasını ertelemek isteyebilirsiniz. Bu sayede titreme önlenir ve animasyon oluşturduğunuz durumun kararlı olması sağlanır.
<head> içinde, aşağıdaki meta etiketini kullanarak sayfa ilk oluşturulmadan önce bulunması gereken bir veya daha fazla öğe kimliği tanımlayın.
<link rel="expect" blocking="render" href="#section1">
Bu meta etiket, içeriğin yüklenmesi gerektiğini değil, öğenin DOM'da bulunması gerektiğini belirtir. Örneğin, resimlerde koşulun doğru olarak değerlendirilmesi için DOM ağacında belirtilen id ile <img> etiketinin bulunması yeterlidir. Resmin kendisi yükleniyor olabilir.
Render engelleme özelliğini kullanmadan önce, artımlı render işlemenin web'in temel bir yönü olduğunu unutmayın. Bu nedenle, render engellemeyi seçerken dikkatli olun. Oluşturmanın engellenmesinin etkisi, her bir durum için ayrı ayrı değerlendirilmelidir. Önemli Web Verileriniz üzerindeki etkiyi ölçerek kullanıcılarınız üzerindeki etkisini aktif olarak ölçüp değerlendiremediğiniz sürece varsayılan olarak blocking=render kullanmaktan kaçının.
Belgeler arası görünüm geçişlerinde geçiş türlerini görüntüleme
Dokümanlar arası görünüm geçişleri, animasyonları ve hangi öğelerin yakalanacağını özelleştirmek için görünüm geçişi türlerini de destekler.
Örneğin, sayfalara ayırma işleminde bir sonraki veya önceki sayfaya giderken, dizide daha yüksek bir sayfaya mı yoksa daha düşük bir sayfaya mı gittiğinize bağlı olarak farklı animasyonlar kullanmak isteyebilirsiniz.
Bu türleri önceden ayarlamak için türleri @view-transition at-rule'a ekleyin:
@view-transition {
navigation: auto;
types: slide, forwards;
}
Türleri anında ayarlamak için pageswap ve pagereveal etkinliklerini kullanarak e.viewTransition.types değerini değiştirin.
window.addEventListener("pagereveal", async (e) => {
if (e.viewTransition) {
const transitionType = determineTransitionType(navigation.activation.from, navigation.activation.entry);
e.viewTransition.types.add(transitionType);
}
});
Türler, eski sayfadaki ViewTransition nesnesinden yeni sayfadaki ViewTransition nesnesine otomatik olarak aktarılmaz. Animasyonların beklendiği gibi çalışması için en azından yeni sayfada kullanılacak türleri belirlemeniz gerekir.
Bu türlere yanıt vermek için :active-view-transition-type() sözde sınıf seçiciyi aynı belge görünümü geçişlerinde olduğu gibi kullanın.
/* Determine what gets captured when the type is forwards or backwards */
html:active-view-transition-type(forwards, backwards) {
:root {
view-transition-name: none;
}
article {
view-transition-name: content;
}
.pagination {
view-transition-name: pagination;
}
}
/* Animation styles for forwards type only */
html:active-view-transition-type(forwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-left;
}
&::view-transition-new(content) {
animation-name: slide-in-from-right;
}
}
/* Animation styles for backwards type only */
html:active-view-transition-type(backwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-right;
}
&::view-transition-new(content) {
animation-name: slide-in-from-left;
}
}
/* Animation styles for reload type only */
html:active-view-transition-type(reload) {
&::view-transition-old(root) {
animation-name: fade-out, scale-down;
}
&::view-transition-new(root) {
animation-delay: 0.25s;
animation-name: fade-in, scale-up;
}
}
Türler yalnızca etkin bir görünüm geçişi için geçerli olduğundan, görünüm geçişi tamamlandığında türler otomatik olarak temizlenir. Bu nedenle, türler BFCache gibi özelliklerle iyi çalışır.
Demo
Aşağıdaki sayfalara ayırma demosunda, sayfa içerikleri, gittiğiniz sayfa numarasına göre ileri veya geri kaydırılır.
Kullanılacak geçiş türü, pagereveal ve pageswap etkinliklerinde "to" ve "from" URL'lerine bakılarak belirlenir.
const determineTransitionType = (fromNavigationEntry, toNavigationEntry) => {
const currentURL = new URL(fromNavigationEntry.url);
const destinationURL = new URL(toNavigationEntry.url);
const currentPathname = currentURL.pathname;
const destinationPathname = destinationURL.pathname;
if (currentPathname === destinationPathname) {
return "reload";
} else {
const currentPageIndex = extractPageIndexFromPath(currentPathname);
const destinationPageIndex = extractPageIndexFromPath(destinationPathname);
if (currentPageIndex > destinationPageIndex) {
return 'backwards';
}
if (currentPageIndex < destinationPageIndex) {
return 'forwards';
}
return 'unknown';
}
};
Geri bildirim
Geliştiricilerin geri bildirimleri bizim için çok değerlidir. Paylaşmak için GitHub'da CSS Çalışma Grubu ile ilgili bir sorun bildirin. Önerilerinizi ve sorularınızı da ekleyin. Sorununuzun başına [css-view-transitions] ekleyin.
Bir hatayla karşılaşırsanız bunun yerine Chromium hata bildiriminde bulunun.