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

İki farklı doküman arasında görüntüleme geçişi gerçekleştiğinde buna dokümanlar arası görüntüleme 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 Chrome'da desteklenir.

Tarayıcı Desteği

  • Chrome: 126..
  • Kenar: 126..
  • Firefox: Desteklenmez..
  • Safari: desteklenmez..

Belgeler arası görünüm geçişleri, aynı doküman görüntüleme geçişleriyle tamamen aynı yapı taşlarını ve ilkeleri temel alır. Bu geçişlerin yapılması planlı olarak gerçekleştirilir:

  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 atlanırken DOM güncellenir.
  3. Son olarak, geçişler CSS animasyonları tarafından desteklenir.
ziyaret edin.

Aynı doküman görüntüleme geçişleriyle karşılaştırıldığında farklı olan, dokümanlar arası görünüm geçişlerinde bir görüntüleme geçişi başlatmak için document.startViewTransition çağrısına gerek olmamasıdır. Bunun yerine, belgeler arası görüntüleme geçişi tetikleyicisi, bir sayfadan diğerine yapılan aynı kaynak gezinmedir. Bu işlem, genellikle web sitenizdeki bir kullanıcının bir bağlantıyı tıkladığında gerçekleştirilir.

Diğer 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ı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.


Belgeler arası görünüm geçişleri, aynı kaynak gezinmelerle sınırlıdır

Belgeler arası görünüm geçişleri yalnızca aynı kaynak gezinmeleriyle 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ılarıyla açıklandığı üzere kullanılan şema, ana makine adı ve bağlantı noktasının birleşimidir.

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

Örneğin, aynı kaynaklar olduğu için developer.chrome.com ürününden developer.chrome.com/blog ürününe giderken belgeler arası bir görünüm geçişiniz olabilir. Kaynaklar arası ve aynı site olduğundan developer.chrome.com sitesinden www.chrome.com sitesine giderken bu geçişi yapamazsınız.


Belgeler arası görünüm geçişleri isteğe bağlıdır

İki doküman arasında belgeler arası görüntüleme geçişi için, her iki katılımcı sayfanın da buna izin vermesi gerekir. Bu işlem, CSS'de @view-transition kuralı ile yapılır.

@view-transition üt kuralında navigation tanımlayıcıyı auto olarak ayarlayarak belgeler arası, aynı kaynaklı gezinmeler için görüntüleme geçişlerini etkinleştirin.

@view-transition {
  navigation: auto;
}

navigation açıklayıcıyı auto olarak ayarladığınızda, aşağıdaki NavigationType'larda görüntüleme geçişlerinin yapılmasına izin vermiş olursunuz:

  • traverse
  • Etkinleştirme işlemi kullanıcı tarafından tarayıcı kullanıcı arayüzü mekanizmaları ile başlatılmadıysa push veya replace.

auto dışında tutulan gezinmeler, örneğin, URL adres çubuğunu kullanarak gezinme veya bir yer işaretini tıklayarak gezinmenin yanı sıra kullanıcı veya komut dosyası tarafından başlatılan yeniden yükleme biçimindedir.

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.

Belgeler arası görüntüleme 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. Bir sayfadan diğerine gidildiğinde görünüm geçişleri tetiklenir.

Stack Navigator demosu kaydı. Chrome 126 veya sonraki bir sürüm gereklidir.
'nı inceleyin.

Belgeler 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..
  • Kenar: 124..
  • Firefox: Desteklenmez..
  • Safari: desteklenmez..

Kaynak

Belgeler arası görünüm geçişlerini özelleştirmenizi sağlamak için HTML spesifikasyonu kullanabileceğiniz iki yeni etkinlik içeriyor: pageswap ve pagereveal.

Bu iki etkinlik, bir görüntüleme geçişi gerçekleşmek üzere olup olmadığına bakılmaksızın, aynı kaynaktan belgeler arası her gezinme için 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, 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, başlatıldıktan veya yeniden etkinleştirildikten sonra ancak ilk oluşturma fırsatından önce bir sayfada 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ışmadan önce özelleştirmek amacıyla bazı view-transition-name değerlerini hızlı bir şekilde ayarlamak veya değiştirmek ya da sessionStorage ürünündeki verileri yazıp okuyarak bir dokümandan diğerine veri aktarmak için bu etkinlikleri kullanabilirsiniz.

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ı nesnedir. Ayrıca çeşitli işlemleri farklı şekilde ele alırlar:

  • pageswap: Doküman gizlendikten sonra eski ViewTransition nesnesi atlanır. Bu durumda viewTransition.ready işlemi reddeder ve viewTransition.finished işlemi sonlandırır.
  • pagereveal: updateCallBack taahhüdü şu anda zaten gerçekleştirildi. viewTransition.ready ve viewTransition.finished taahhütlerini kullanabilirsiniz.

Tarayıcı Desteği

  • Chrome: 123..
  • Kenar: 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 giderken eski içeriğin soldan sağa doğru kaydırılması 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ı kaynak gezinmeyle ilgili bilgi içeren NavigationActivation nesnelerini gösterebilir. Bu nesne, kullanılan gezinme türünü, geçerli ve son hedef geçmişi girişlerini Gezinme API'sindeki navigation.entries() içinde bulunduğu şekliyle 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 bilgilerinin kullanıldığı bu Profil demosuna göz atın.

Bu şekilde, listedeki her bir öğeyi önceden view-transition-name ile süslemeniz gerekmez. Bunun yerine, bu tam zamanında JavaScript kullanılarak, yalnızca gerektiğinde öğelerde gerçekleştirilir.

Profiller demosu kaydı. Chrome 126 veya sonraki bir sürüm gereklidir.
'nı inceleyin.

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

Bu konuda yardımcı olmak için, geçici olarak view-transition-name öğelerini 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 ş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 engelleme işleviyle içeriğin yüklenmesini bekleyin

Tarayıcı Desteği

  • Chrome: 124..
  • Kenar: 124..
  • Firefox: Desteklenmez..
  • Safari: desteklenmez..

Bazı durumlarda, belirli bir öğe yeni DOM'de mevcut olana kadar bir sayfanın ilk oluşturulmasını bekletmek 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, aşağıdaki meta etiketi kullanarak sayfa ilk kez oluşturulmadan önce mevcut olması 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ği değil, öğenin DOM'de mevcut olması gerektiği anlamına gelir. Örneğin, resimlerde DOM ağacında belirtilen id ile birlikte <img> etiketinin sadece olması, koşulun doğru olarak değerlendirilmesi için yeterlidir. Resmin kendisi hâlâ yükleniyor olabilir.

Oluşturmayı engellemeye başlamadan önce, artımlı oluşturmanın web'in temel bir yönü olduğunu unutmayın. Bu nedenle, oluşturmayı engellemeyi seçerken dikkatli olun. Oluşturmayı engellemenin etkisi, tek tek örnekler ele alınarak değerlendirilmelidir. Varsayılan olarak Core Web Vitals etkisini ölçerek kullanıcılarınız üzerindeki etkisini etkin bir şekilde ölçemiyorsanız blocking=render kullanmaktan kaçının.


Belgeler arası görünüm 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ş türlerini de destekler.

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

Bu türleri önceden ayarlamak için @view-transition kuralında türleri 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 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ı belge 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 aktif görüntüleme geçişine uygulandığından, görünüm geçişi tamamlandığında türler otomatik olarak temizlenir. Bu nedenle, türler BFCache gibi özelliklerle sorunsuz çalışır.

Demo

Aşağıdaki sayfalandırma demosunda sayfa içerikleri, ziyaret ettiğiniz sayfa numarasına göre ileri veya geri kaydırılır.

Sayfaları numaralandı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 URL'lere ve URL'lerden verilere 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 öneriler ve sorularla GitHub'da CSS Çalışma Grubu'na bir sorun bildirin. Sorununuzun önüne [css-view-transitions] ekleyin. Bir hatayla karşılaşırsanız bunun yerine Chromium hatası bildirin.