Çok sayfalı uygulamalar için dokümanlar arası görünüm geçişleri

İki farklı doküman arasında bir görüntüleme geçişi gerçekleştiğinde, buna dokümanlar arası görünüm geçişi adı verilir. Bu durum genellikle çok sayfalı uygulamalarda (MPA) geçerlidir. Dokümanlar arası görünüm geçişleri, Chrome 126 sürümünden itibaren Chrome'da desteklenmektedir.

Tarayıcı Desteği

  • 126
  • 126
  • x
  • x

Kaynak

Dokümanlar arası görünüm geçişleri, aynı doküman görünümü geçişleriyle aynı yapı taşlarını ve ilkeleri temel alır. Bu geçişler özellikle bilinçlidir:

  1. Tarayıcı, hem eski hem de yeni sayfada benzersiz bir view-transition-name öğesine sahip öğelerin anlık görüntülerini alır.
  2. Oluşturma işlemi engellenirken DOM güncellenir.
  3. Son olarak, geçişler CSS animasyonlarıyla desteklenir.

Aynı doküman görüntüleme geçişlerine kıyasla farklı olan şey, dokümanlar arası görünüm geçişlerinde bir görüntüleme geçişi başlatmak için document.startViewTransition yöntemini çağırmanızın gerekmemesidir. Bunun yerine, dokümanlar arası görünüm geçişinin tetikleyicisi, genellikle web sitenizin kullanıcısının bir bağlantıyı tıklayarak gerçekleştirdiği bir işlem olan, bir sayfadan diğerine aynı kaynaktan gitme işlemidir.

Diğer bir deyişle, iki doküman arasında bir görünüm geçişi başlatmak için çağrılacak bir API yoktur. Ancak yerine getirilmesi gereken iki koşul vardır:

  • Her iki doküman da aynı kaynakta bulunmalıdır.
  • Görüntüleme geçişine izin verilmesi için her iki sayfanın da etkinleştirilmesi gerekir.

Her iki koşul da bu dokümanın ilerleyen kısımlarında açıklanmaktadır.


Dokümanlar arası görünüm geçişleri, aynı kaynakta gezinmelerle sınırlıdır

Dokümanlar arası görünüm geçişleri yalnızca aynı kaynak gezinmeleriyle sınırlıdır. Her iki katılımcı sayfanın kaynağı aynıysa gezinmenin aynı kaynak olduğu kabul edilir.

Bir sayfanın kaynağı, web.dev sayfasında ayrıntılı olarak açıklandığı gibi, kullanılan şema, ana makine adı ve bağlantı noktasının bir kombinasyonudur.

Şema, ana makine adı ve bağlantı noktasının vurgulandığı örnek URL. Tüm bunlar bir araya geldiğinde başlangıç noktasını oluşturur.
Şema, ana makine adı ve bağlantı noktasının vurgulandığı örnek URL. Tüm bunlar bir araya geldiğinde başlangıç noktasını oluşturur.

Örneğin, kaynaklar aynı kaynak olduğundan developer.chrome.com konumundan developer.chrome.com/blog konumuna giderken dokümanlar arası görünüm geçişi yapabilirsiniz. developer.chrome.com sitesinden www.chrome.com sitesine giderken çapraz kaynak ve aynı site olduğundan bu geçiş işlemini gerçekleştiremezsiniz.


Dokümanlar arası görünüm geçişleri etkindir

İki doküman arasında dokümanlar arası görünüm geçişi sağlamak için, katılımcı sayfaların her ikisinde de buna izin verilmiş olması gerekir. Bu işlem, CSS'deki @view-transition kuyruklu a kuralı ile yapılır.

Dokümanlar arası, aynı kaynağa sahip gezinmelerde görünüm geçişlerini etkinleştirmek için @view-transition kuralında navigation açıklayıcıyı auto olarak ayarlayın.

@view-transition {
  navigation: auto;
}

navigation tanımlayıcısını auto olarak ayarlayarak aşağıdaki NavigationType için görünüm geçişlerinin yapılmasına izin vermiş olursunuz:

  • traverse
  • Etkinleştirme, kullanıcı tarafından tarayıcı kullanıcı arayüzü mekanizmaları aracılığıyla başlatılmadıysa push veya replace.

auto kapsamının dışında kalan gezinmeler, örneğin URL adres çubuğunu kullanarak gezinme veya bir yer işaretini tıklama gibi kullanıcı veya komut dosyası tarafından başlatılan yeniden yükleme biçimlerini kapsar.

Chrome'da gezinme işlemi çok uzun sürerse (Chrome'da dört saniyeden uzun sürerse) TimeoutError DOMException ile görüntüleme geçişi atlanır.

Dokümanlar arası görünüm geçişleri demosu

Stack Navigator demosu oluşturmak için görünüm geçişlerinin kullanıldığı aşağıdaki demoya göz atın. Burada document.startViewTransition() için çağrı yok, görünüm geçişleri bir sayfadan diğerine gidilerek tetiklenir.

Stack Navigator demosu kaydı. Chrome 126 veya sonraki sürümleri gerekir.

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ı değildir ancak onunla birlikte kullanılmak üzere tasarlanmıştır.

pageswap ve pagereveal etkinlikleri

Tarayıcı Desteği

  • 124
  • 124
  • x
  • x

Kaynak

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.

Görünüm geçişinin gerçekleşip gerçekleşmemesine bakılmaksızın, aynı kaynaklar arasında yapılan her gezinme işleminde bu iki etkinlik tetiklenir. İki sayfa arasında bir görünüm geçişi gerçekleşmek üzereyse bu etkinliklerde viewTransition özelliğini kullanarak ViewTransition nesnesine erişebilirsiniz.

  • pageswap etkinliği, sayfanın son karesi oluşturulmadan önce tetiklenir. Eski anlık görüntüler alınmadan hemen önce, giden sayfada bazı son dakika değişiklikleri yapmak için bunu kullanabilirsiniz.
  • pagereveal etkinliği başlatıldıktan veya yeniden etkinleştirildikten sonra, ancak ilk oluşturma fırsatından önce sayfada tetiklenir. Bunu kullanarak, yeni anlık görüntüler alınmadan önce yeni sayfayı özelleştirebilirsiniz.

Örneğin, bazı view-transition-name değerlerini hızlı bir şekilde ayarlamak veya değiştirmek ya da bir dokümandan diğerine veri aktarmak için bu etkinlikleri kullanabilirsiniz. Bunu yapmak için sessionStorage ürününden veri yazıp okuyarak görünüm geçişini gerçekte çalıştırmadan ö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 atlamaya karar verebilirsiniz.

window.addEventListener("pagereveal", async (e) => {
  if (e.viewTransition) {
    if (goodReasonToSkipTheViewTransition()) {
      e.viewTransition.skipTransition();
    }
  }
}

pageswap ve pagereveal içindeki ViewTransition nesnesi iki farklı nesne. Çeşitli vaatleri de farklı şekilde ele alırlar:

  • pageswap: Doküman gizlendikten sonra eski ViewTransition nesnesi atlanır. Böyle bir durumda viewTransition.ready reddeder ve viewTransition.finished çözümlenir.
  • pagereveal: updateCallBack sözü bu noktada zaten tamamlanmıştır. viewTransition.ready ve viewTransition.finished taahhütlerini kullanabilirsiniz.

Tarayıcı Desteği

  • 123
  • 123
  • x
  • x

Kaynak

Hem pageswap hem de pagereveal etkinliklerinde, eski ve yeni sayfaların URL'lerine göre de işlem yapabilirsiniz.

Örneğin, MPA Yığın Gezgini'nde kullanılacak animasyon türü gezinme yoluna bağlıdır:

  • Genel bakış sayfasından ayrıntılar sayfasına gidildiğinde yeni içeriğin sağdan sola doğru kaydırılması gerekir.
  • Ayrıntılar sayfasından genel bakış sayfasına geçildiğinde eski içeriğin soldan sağa doğru kayması gerekir.

Bunu yapmak için, pageswap örneğinde gerçekleşmek üzere olan veya az önce gerçekleşen pagereveal örneğinde gezinmeyle ilgili bilgilere ihtiyacınız vardır.

Bunun için tarayıcılar, aynı kaynakta gezinmeyle ilgili bilgileri barındıran NavigationActivation nesnelerini artık gösterebilir. Bu nesne, Gezinme API'sindeki navigation.entries() bölümünde bulunan gezinme türünü, geçerli ve son hedef geçmişi girişlerini gösterir.

Etkinleştirilmiş bir sayfada, bu nesneye navigation.activation üzerinden erişebilirsiniz. pageswap etkinliğinde, buna 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.

Böylece listedeki her öğeyi önceden view-transition-name ile dekore etmeniz gerekmez. Bunun yerine bu, JavaScript kullanılarak tam zamanında ve yalnızca gereken öğelerde gerçekleşir.

Profiller demosunun kaydı. Chrome 126 veya sonraki sürümler gerekir.

Bu 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';
    }
  }
});

Ayrıca kod, görüntüleme geçişi çalıştırıldıktan sonra view-transition-name değerlerini kaldırarak kendiliğinden temizlenir. Bu şekilde, sayfa art arda yapılan gezinmeler için hazır olur ve geçmiş geçişini de işleyebilir.

Buna yardımcı olmak için geçici olarak view-transition-name değerini ayarlayan bu yardımcı program işlevini 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 gibi 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);
    }
  }
});

Oluşturma engelleme ile içeriğin yüklenmesini bekleyin

Tarayıcı Desteği

  • 124
  • 124
  • x
  • x

Kaynak

Bazı durumlarda, yeni DOM'de belirli bir öğe mevcut olana kadar sayfanın ilk oluşturulmasını bekletmek isteyebilirsiniz. Bu şekilde yanıp sönmeyi önleyerek, canlandırmak istediğiniz durumun kararlı olmasını sağlarsınız.

<head> içinde, aşağıdaki meta etiketi kullanarak sayfanın ilk oluşturulmasından ö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 değil, öğenin DOM'da olması gerektiği anlamına gelir. Örneğin resimlerde, DOM ağacında belirtilen id ile <img> etiketinin sadece varlığı, koşulun doğru olarak değerlendirilmesi için yeterlidir. Resim hâlâ yükleniyor olabilir.

Oluşturma engellemeye başlamadan önce, artımlı oluşturmanın Web'in temel bir özelliği olduğunu unutmayın. Bu nedenle, oluşturmayı engellemeyi seçerken dikkatli olun. Oluşturmayı engellemenin etkisi her durum için ayrı ayrı değerlendirilmelidir. Varsayılan olarak, blocking=render ürününün kullanıcılarınız üzerindeki etkisini etkin bir şekilde ölçemiyorsanız ve Core Web Vitals üzerindeki etkisini ölçemiyorsanız kullanmaktan kaçının.


Dokümanlar arası görünüm geçişlerindeki 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ş türlerini de destekler.

Örneğin, bir sayfada sonraki veya önceki sayfaya giderken sıralamadan daha üst veya daha alt bir sayfaya mı gittiğinize bağlı olarak farklı animasyonlar kullanmak isteyebilirsiniz.

Bu türleri önceden ayarlamak için türleri @view-transition kuralına ekleyin:

@view-transition {
  navigation: auto;
  types: slide, forwards;
}

Türleri hemen 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 sayfanın ViewTransition nesnesine otomatik olarak taşınmaz. 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() sanal sınıf seçiciyi aynı doküman görüntüleme geçişleriyle aynı şekilde 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 bir aktif görüntüleme 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 performans gösterir.

Demo

Aşağıdaki sayfalara ayırma demosunda, sayfa içerikleri, gittiğiniz sayfa numarasına göre ileriye veya geriye doğru kaydırılır.

Sayfalara ayırma demosu (MPA) kaydı. Gittiğiniz sayfaya bağlı olarak farklı geçişler kullanır.

Kullanılacak geçiş türü, pagereveal ve pageswap etkinliklerinde gelen ve giden URL'lere 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 her zaman çok değerlidir. Paylaşmak için GitHub'da CSS Çalışma Grubu'na sorun bildiriminde bulunun, öneriler ve sorular sorun. Sorununuzun önüne [css-view-transitions] ekleyin. Bir hatayla karşılaşırsanız bunun yerine Chromium hatası bildiriminde bulunun.