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

İki farklı doküman arasında görünüm geçişi olduğunda buna dokümanlar arası görünüm geçişi denir. Bu durum genellikle çok sayfalı uygulamalarda (MPA) geçerlidir. Belgeler arası görünüm geçişleri, Chrome 126'dan itibaren Chrome'da desteklenir.

Tarayıcı Desteği

  • Chrome: 126.
  • Edge: 126.
  • Firefox: Desteklenmez.
  • Safari Technology Preview: desteklenir.

Doküman içi görünüm geçişleri, aynı doküman görünüm geçişleri ile aynı yapı taşları ve ilkelerden yararlanır. Bu, bilinçli olarak yapılan bir tercihtir:

  1. Tarayıcı, hem eski hem de yeni sayfada benzersiz bir view-transition-name değerine 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ünüm geçişleriyle karşılaştırıldığında, doküman arası görünüm geçişlerinde görünüm geçişini başlatmak için document.startViewTransition çağrısını yapmanız gerekmez. Bunun yerine, belgeler arası görüntüleme geçişinin tetikleyicisi, bir sayfadan diğerine aynı kaynaktan gezinmedir. Bu işlem genellikle web sitenizin kullanıcısı tarafından bir bağlantı tıklanarak gerçekleştirilir.

Diğer bir deyişle, iki doküman arasında görüntü geçişi başlatmak için çağrılacak bir API yoktur. Ancak bunun için iki koşulun karşılanması gerekir:

  • Her iki dokümanın da aynı kaynakta bulunması gerekir.
  • Görüntüleme 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 bölümlerinde açıklanmaktadır.


Doküman içi görünüm geçişleri aynı kaynaktaki gezinmelerle sınırlıdır

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

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

Şema, ana makine adı ve bağlantı noktasının vurgulandığı örnek bir URL. Bunlar birlikte kaynağı oluşturur.
Şema, ana makine adı ve bağlantı noktasının vurgulandığı örnek bir URL. Bu iki içerik birleştirildiğinde kökeni oluşturur.

Örneğin, aynı kaynaktan oldukları için developer.chrome.com adresinden developer.chrome.com/blog adresine giderken dokümanlar arası görüntü geçişi yapabilirsiniz. Bunlar çapraz kaynak ve aynı site olduğundan developer.chrome.com'ten www.chrome.com'e giderken bu geçişi yapamazsınız.


Doküman arası görüntüleme geçişleri etkinleştirilmelidir.

İki belge arasında belgeler arası görüntüleme geçişi yapılabilmesi için katılımcı sayfaların ikisinin 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, navigation tanımlayıcısını auto olarak ayarlayarak dokümanlar arası, aynı kaynaktaki gezinmelerde görünüm geçişlerini etkinleştirin.

@view-transition {
  navigation: auto;
}

navigation tanımlayıcısını auto olarak ayarlayarak aşağıdaki NavigationType türleri için görüntü 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ından hariç tutulan gezinmeler arasında, URL adres çubuğunu kullanarak gezinme veya yer işaretini tıklama gibi işlemler ve kullanıcı veya komut dosyası tarafından başlatılan tüm yeniden yükleme işlemleri yer alır.

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

kullanılarak etkinleştirmektir.

Doküman arası görüntü geçişleri demosu

Stack Navigator demosu oluşturmak için görüntü geçişlerini kullanan aşağıdaki demoya göz atın. Burada document.startViewTransition() çağrısı yoktur. Görüntüleme geçişleri, bir sayfadan diğerine gidilerek tetiklenir.

Stack Navigator demosunun kaydı. Chrome 126 veya sonraki bir sürüm gereklidir.

Dokümanlar arası görünüm geçişlerini özelleştirme

Belgeler 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

  • Chrome: 124.
  • Edge: 124.
  • Firefox: Desteklenmez.
  • Safari: Desteklenmez.

Kaynak

HTML spesifikasyonu, dokümanlar arası görünüm geçişlerini özelleştirmenize olanak tanımak için kullanabileceğiniz iki yeni etkinlik içerir: pageswap ve pagereveal.

Bu iki etkinlik, görünüm geçişi gerçekleşip gerçekleşmeyeceğinden bağımsız olarak her aynı kaynaktan belgeler arası gezinme için tetiklenir. İki sayfa arasında bir görüntüleme geçişi gerçekleşmek üzereyse bu etkinliklerde viewTransition mülkünü kullanarak ViewTransition nesnesine erişebilirsiniz.

  • pageswap etkinliği, bir sayfanın son karesi oluşturulmadan önce tetiklenir. Bunu, eski anlık görüntüler alınmadan hemen önce, giden sayfada bazı son dakika değişiklikleri yapmak için kullanabilirsiniz.
  • pagereveal etkinliği, bir sayfa ilk kez oluşturulduktan veya yeniden etkinleştirildikten sonra ancak ilk oluşturma fırsatından önce tetiklenir. Bu sayede, yeni anlık görüntüler alınmadan önce yeni sayfayı özelleştirebilirsiniz.

Örneğin, görünüm geçişini gerçekten çalıştırmadan önce özelleştirmek için bu etkinlikleri kullanarak bazı view-transition-name değerlerini hızlıca ayarlayabilir veya değiştirebilir ya da sessionStorage'den veri yazarak ve okuyarak verileri bir dokümandan diğerine aktarabilirsiniz.

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 geçişi her iki etkinlikte de atlayabilirsiniz.

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 vaatleri farklı şekilde ele alırlar:

  • pageswap: Belge gizlendikten sonra eski ViewTransition nesnesi atlanır. Bu durumda viewTransition.ready işlemi reddeder ve viewTransition.finished işlemi sonlandırır.
  • pagereveal: updateCallBack vaadi bu aşamada zaten çözüme ulaştırılmıştır. viewTransition.ready ve viewTransition.finished taahhütlerini kullanabilirsiniz.

Tarayıcı Desteği

  • Chrome: 123.
  • Edge: 123.
  • Firefox: Desteklenmez.
  • Safari: Desteklenmez.

Kaynak

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

Örneğin, MPA Stack Navigator'da kullanılacak animasyon türü gezinme yoluna bağlıdır:

  • Genel bakış sayfasından ayrıntılar sayfasına giderken yeni içeriğin sağdan sola kaydırılması gerekir.
  • Ayrıntılar sayfasından genel bakış sayfasına geçiş yapıldığında eski içeriğin soldan sağa kayması gerekir.

Bunu yapmak için, pageswap söz konusu olduğunda gerçekleşmek üzere olan veya pagereveal durumunda gerçekleşmiş olan navigasyon hakkında bilgilere ihtiyacınız vardır.

Bunun için tarayıcılar artık aynı kaynaktan gezinme hakkında bilgi içeren NavigationActivation nesneleri gösterebilir. Bu nesne, Navigation API'deki navigation.entries() öğesinde bulunan kullanılan gezinme türünü, mevcut ve nihai hedef geçmişi girişlerini gösterir.

Etkinleştirilmiş bir sayfada bu nesneye navigation.activation üzerinden erişebilirsiniz. pageswap etkinliğinde bu bilgilere e.activation üzerinden erişebilirsiniz.

Görüntüleme geçişine katılması gereken öğelerdeki view-transition-name değerlerini ayarlamak için pageswap ve pagereveal etkinliklerindeki NavigationActivation bilgilerini kullanan bu Profiller demosuna göz atın.

Bu şekilde, listedeki her bir öğeyi önceden view-transition-name ile süslemeniz gerekmez. Bunun yerine, JavaScript kullanılarak yalnızca ihtiyaç duyulan öğelerde anında gerçekleşir.

Profiller demosunun kaydı. Chrome 126 veya sonraki bir sürüm gereklidir.

Kod şu şekildedir:

// 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üntüleme geçişi çalıştıktan sonra view-transition-name değerlerini kaldırarak da temizlik yapar. Bu şekilde, sayfa art arda gezinme için hazır olur ve geçmiş gezinmesini de işleyebilir.

Bu konuda size yardımcı olması için view-transition-name değerlerini geçici olarak ayarlayan bu yardımcı programı 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);
    }
  }
});

Oluşturma engellemeyle içeriğin yüklenmesini bekleme

Tarayıcı desteği

  • Chrome: 124.
  • Edge: 124.
  • Firefox: Desteklenmez.
  • Safari: Desteklenmez.

Bazı durumlarda, yeni DOM'da belirli bir öğe bulunana kadar sayfanın ilk oluşturulmasını beklemek isteyebilirsiniz. Bu yöntem, yanıp sönmenin önüne geçer ve animasyon uyguladığınız durumun sabit olduğundan emin olur.

<head> içinde, sayfa ilk kez oluşturulmadan önce mevcut olması gereken bir veya daha fazla öğe kimliği tanımlamak için aşağıdaki meta etiketini kullanın.

<link rel="expect" blocking="render" href="#section1">

Bu meta etiket, içeriğin yüklenmesi 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. Resim hâlâ yükleniyor olabilir.

Oluşturma işlemini engelleme konusunda karar vermeden önce, artımlı oluşturmanın web'in temel bir yönü olduğunu unutmayın. Bu nedenle, oluşturma işlemini engellemeyi seçerken dikkatli olun. Oluşturmayı engellemenin etkisinin durum bazında değerlendirilmesi gerekir. Önemli Web Verileri'niz üzerindeki etkisini ölçerek kullanıcılarınız üzerindeki etkisini aktif olarak ölçemediğiniz ve ölçemediğiniz sürece varsayılan olarak blocking=render kullanmamaya çalışın.


Belgeler arası görüntü geçişlerinde geçiş türlerini görüntüleme

Belgeler 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 ayrılmış bir sıralamada bir sonraki veya önceki sayfaya giderken, sıralamada daha üst bir sayfaya mı yoksa daha alt bir sayfaya mı gittiğinize bağlı olarak farklı animasyonlar kullanmak isteyebilirsiniz.

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

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

Türleri anında ayarlamak için e.viewTransition.types değerini değiştirmek üzere pageswap ve pagereveal etkinliklerini kullanın.

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() sözde sınıf seçiciyi aynı belgedeki 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üntüleme geçişi için geçerli olduğundan, görüntüleme geçişi sona erdiğinde türler otomatik olarak temizlenir. Bu nedenle, türler BFCache gibi özelliklerle sorunsuz çalışır.

Demo

Aşağıdaki sayfalara ayırma denemesinde, sayfa içerikleri, gittiğiniz sayfa numarasına göre ileri veya geri kayar.

Sayfalandırma demosunun (MPA) kaydı. Geçişler, hangi sayfaya gittiğinize bağlı olarak farklı olur.

Kullanılacak geçiş türü, pagereveal ve pageswap etkinliklerinde, "gelen" ve "giden" 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ştirici geri bildirimleri bizim için çok değerli. Paylaşmak için öneri ve sorularınızı GitHub'daki CSS Çalışma Grubu ile ilgili bir sorun olarak gönderin. Sorununuzun önüne [css-view-transitions] ekleyin. Hatayla karşılaşırsanız bunun yerine Chromium hata bildiriminde bulunun.