Tek bir dokümanda görüntüleme geçişinin çalıştırılmasına aynı doküman görüntüleme geçişi adı verilir. Bu durum genellikle DOM'yi güncellemek için JavaScript'in kullanıldığı tek sayfalık uygulamalarda (SPA'lar) geçerlidir. Aynı belgedeki görünüm geçişleri, Chrome 111'den itibaren Chrome'da desteklenmektedir.
Aynı doküman görüntüleme geçişini tetiklemek için document.startViewTransition
komutunu çağırın:
function handleClick(e) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow();
return;
}
// With a View Transition:
document.startViewTransition(() => updateTheDOMSomehow());
}
Tetiklendiğinde tarayıcı, üzerinde view-transition-name
CSS mülkünün tanımlandığı tüm öğelerin anlık görüntülerini otomatik olarak yakalar.
Ardından, DOM'yi güncelleyen, iletilen geri çağırmayı yürütür ve ardından yeni durumun anlık görüntülerini alır.
Daha sonra bu anlık görüntüler, yapay öğelerden oluşan bir ağaçta düzenlenir ve CSS animasyonlarının gücü kullanılarak canlandırılır. Eski ve yeni durumdaki anlık görüntü çiftleri, eski konum ve boyutlarından yeni konumlarına sorunsuz bir şekilde geçiş yapar. Bu sırada içerikleri de yumuşak bir şekilde geçiş yapar. Dilerseniz animasyonlarınızı CSS kullanarak özelleştirebilirsiniz.
Varsayılan geçiş: Geçiş efekti
Varsayılan görüntü geçişi, geçiş efekti olduğundan API'ye güzel bir giriş niteliğindedir:
function spaNavigate(data) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
// With a transition:
document.startViewTransition(() => updateTheDOMSomehow(data));
}
Burada updateTheDOMSomehow
, DOM'yi yeni duruma değiştirir. Bunu istediğiniz gibi yapabilirsiniz. Örneğin, öğe ekleyip kaldırabilir, sınıf adlarını veya stilleri değiştirebilirsiniz.
Tam da bu şekilde sayfalar çapraz geçiş yapıyor:
Tamam, geçiş çok etkileyici değil. Geçişler özelleştirilebilir. Ancak önce bu temel geçişin nasıl çalıştığını anlamanız gerekir.
Bu geçişlerin işleyiş şekli
Önceki kod örneğini güncelleyelim.
document.startViewTransition(() => updateTheDOMSomehow(data));
.startViewTransition()
çağrıldığında API, sayfanın mevcut durumunu yakalar. Buna anlık görüntü almak da dahildir.
İşlem tamamlandığında .startViewTransition()
adresine iletilen geri çağırma işlevi çağrılır. DOM burada değiştirilir. Ardından, API sayfanın yeni durumunu yakalar.
Yeni durum yakalandıktan sonra API aşağıdaki gibi bir sözde öğe ağacı oluşturur:
::view-transition
└─ ::view-transition-group(root)
└─ ::view-transition-image-pair(root)
├─ ::view-transition-old(root)
└─ ::view-transition-new(root)
::view-transition
, sayfadaki diğer her şeyin üzerinde yer alan bir yer paylaşımında bulunur. Bu, geçiş için bir arka plan rengi ayarlamak isterseniz yararlı olur.
::view-transition-old(root)
, eski görünümün ekran görüntüsüdür ve ::view-transition-new(root)
, yeni görünümün canlı temsilidir. Her ikisi de CSS "yerine geçen içerik" olarak oluşturulur (<img>
gibi).
Eski görünüm opacity: 1
'ten opacity: 0
'e, yeni görünüm ise opacity: 0
'ten opacity: 1
'e animasyonla geçerek geçiş oluşturur.
Animasyonun tamamı CSS animasyonları kullanılarak gerçekleştirilir, böylece CSS ile özelleştirilebilir.
Geçişi özelleştirme
Görünüm geçişi sözde öğelerinin tümü CSS ile hedeflenebilir ve animasyonlar CSS kullanılarak tanımlandığından, bunları mevcut CSS animasyon özelliklerini kullanarak değiştirebilirsiniz. Örneğin:
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 5s;
}
Bu tek değişiklikle, geçiş artık gerçekten çok yavaş:
Tamam, yine de etkileyici değil. Bunun yerine aşağıdaki kod, Material Design'ın paylaşılan eksen geçişini uygular:
@keyframes fade-in {
from { opacity: 0; }
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes slide-from-right {
from { transform: translateX(30px); }
}
@keyframes slide-to-left {
to { transform: translateX(-30px); }
}
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
Sonuç şöyle olur:
Birden fazla öğenin geçişini yapma
Önceki demoda, paylaşılan eksen geçişinde sayfanın tamamı yer almaktadır. Bu yöntem sayfanın büyük bir kısmında işe yarar, ancak başlık tekrar içeri kaydırılacağı için dışarı çıktığından başlık için pek doğru sayılmaz.
Bu durumu önlemek için üstbilgiyi sayfanın geri kalanından ayıklayıp ayrı olarak animasyona ekleyebilirsiniz. Bu işlem, öğeye bir view-transition-name
atanarak yapılır.
.main-header {
view-transition-name: main-header;
}
view-transition-name
değeri istediğiniz gibi olabilir (none
dışında, geçiş adı yoktur). Geçiş boyunca öğeyi benzersiz bir şekilde tanımlamak için kullanılır.
Bunun sonucu da şudur:
Şimdi başlık yerinde kalıyor ve çapraz geçiş yapıyor.
Bu CSS bildirimi, sözde öğe ağacının değişmesine neden oldu:
::view-transition
├─ ::view-transition-group(root)
│ └─ ::view-transition-image-pair(root)
│ ├─ ::view-transition-old(root)
│ └─ ::view-transition-new(root)
└─ ::view-transition-group(main-header)
└─ ::view-transition-image-pair(main-header)
├─ ::view-transition-old(main-header)
└─ ::view-transition-new(main-header)
Artık iki geçiş grubu vardır. Biri başlık, diğeri ise geri kalanı içindir. Bunlar CSS ile bağımsız olarak hedeflenerek farklı geçişler verilebilir. Ancak bu örnekte main-header
, çapraz geçiş olan varsayılan geçişte kalmıştır.
Tamam. Varsayılan geçiş yalnızca çapraz geçişten ibaret değildir. ::view-transition-group
ürününde de geçiş yapılır:
- Konumlandırma ve dönüştürme (
transform
kullanarak) - Genişlik
- Boy
Başlık, DOM değişikliğinin her iki tarafında da aynı boyutta ve konumda olduğundan bu durum şimdiye kadar önemli olmamıştı. Ancak üstbilgideki metni de ayıklayabilirsiniz:
.main-header-text {
view-transition-name: main-header-text;
width: fit-content;
}
fit-content
kullanılır. Böylece öğe, kalan genişliğe uzatmak yerine metnin boyutuyla kapsanır. Bu olmadan, geri oku her iki sayfada aynı boyuta sahip olmak yerine başlık metin öğesinin boyutunu küçültür.
Şimdi üç bölümle çalışacağız:
::view-transition
├─ ::view-transition-group(root)
│ └─ …
├─ ::view-transition-group(main-header)
│ └─ …
└─ ::view-transition-group(main-header-text)
└─ …
Yine varsayılan değerleri kullanın:
Şimdi başlık metni, geri düğmesine yer açmak için kaydırılıyor.
view-transition-class
ile birden fazla sözde öğeyi aynı şekilde animasyonlu hale getirme
Tarayıcı Desteği
- .
Birçok kartın olduğu bir görüntüleme geçişinizin olduğunu ve aynı zamanda sayfada bir başlık bulunduğunu varsayalım. Başlık hariç tüm kartlara animasyon eklemek için her bir kartı hedefleyen bir seçici yazmanız gerekir.
h1 {
view-transition-name: title;
}
::view-transition-group(title) {
animation-timing-function: ease-in-out;
}
#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
…
#card20 { view-transition-name: card20; }
::view-transition-group(card1),
::view-transition-group(card2),
::view-transition-group(card3),
::view-transition-group(card4),
…
::view-transition-group(card20) {
animation-timing-function: var(--bounce);
}
20 öğeniz var mı? Bu, yazmanız gereken 20 seçici demektir. Yeni bir öğe mi ekleyeceksiniz? Ardından, animasyon stillerini uygulayan seçiciyi büyütmeniz gerekir. Tam olarak ölçeklenebilir değil.
view-transition-class
, aynı stil kuralını uygulamak için görünüm geçişi sözde öğelerinde kullanılabilir.
#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
#card5 { view-transition-name: card5; }
…
#card20 { view-transition-name: card20; }
#cards-wrapper > div {
view-transition-class: card;
}
html::view-transition-group(.card) {
animation-timing-function: var(--bounce);
}
Aşağıdaki kart örneğinde önceki CSS snippet'inden yararlanılmıştır. Yeni eklenen kartlar da dahil olmak üzere tüm kartlara tek bir seçiciyle aynı zamanlama uygulanır: html::view-transition-group(.card)
.
Hata ayıklama geçişleri
Görüntüleme geçişleri CSS animasyonlarının üzerine oluşturulduğundan Chrome Geliştirici Araçları'ndaki Animasyonlar paneli, geçişlerde hata ayıklamak için mükemmel bir seçenektir.
Animasyonlar panelini kullanarak bir sonraki animasyonu duraklatabilir, ardından animasyonda ileri ve geri gidebilirsiniz. Bu sırada geçiş sözde öğelerini Öğeler panelinde bulabilirsiniz.
Geçiş öğelerinin aynı DOM öğesi olması gerekmez
Başlık ve başlıktaki metin için ayrı geçiş öğeleri oluşturmak üzere şimdiye kadar view-transition-name
kullandık. Bunlar, kavram olarak DOM değişikliğinden önce ve sonra aynı öğedir ancak böyle olmadığı durumlarda geçişler oluşturabilirsiniz.
Örneğin, ana video yerleştirme için bir view-transition-name
verilebilir:
.full-embed {
view-transition-name: full-embed;
}
Ardından, küçük resim tıklandığında yalnızca geçiş süresi boyunca aynı view-transition-name
verilebilir:
thumbnail.onclick = async () => {
thumbnail.style.viewTransitionName = 'full-embed';
document.startViewTransition(() => {
thumbnail.style.viewTransitionName = '';
updateTheDOMSomehow();
});
};
Sonuç:
Küçük resim, ana resme geçiş yapar. Kavramsal olarak (ve kelimenin tam anlamıyla) farklı öğeler olsalar da geçiş API'si, aynı view-transition-name
öğesini paylaştıkları için bunları aynı öğe olarak değerlendirir.
Bu geçişin gerçek kodu, önceki örnekten biraz daha karmaşıktır. Çünkü bu kod, küçük resim sayfasına geri geçişi de gerçekleştirir. Uygulamanın tamamı için kaynağa bakın.
Özel giriş ve çıkış geçişleri
Aşağıdaki örneğe bakın:
Kenar çubuğu da geçişin bir parçasıdır:
.sidebar {
view-transition-name: sidebar;
}
Ancak, önceki örnekte verilen üstbilgiden farklı olarak kenar çubuğu tüm sayfalarda görünmez. Her iki durumda da kenar çubuğu varsa geçiş sözde öğeleri şöyle görünür:
::view-transition
├─ …other transition groups…
└─ ::view-transition-group(sidebar)
└─ ::view-transition-image-pair(sidebar)
├─ ::view-transition-old(sidebar)
└─ ::view-transition-new(sidebar)
Bununla birlikte, kenar çubuğu yalnızca yeni sayfadaysa ::view-transition-old(sidebar)
sözde öğesi orada bulunmaz. Kenar çubuğu için "eski" resim olmadığından resim çiftinde yalnızca ::view-transition-new(sidebar)
bulunur. Benzer şekilde, kenar çubuğu yalnızca eski sayfadaysa resim çiftinde yalnızca bir ::view-transition-old(sidebar)
olacaktır.
Önceki demoda kenar çubuğu, her iki durumda da giriş, çıkış veya mevcut olmasına bağlı olarak farklı şekilde geçiş yapar. Sağdan kaydırılarak ve yavaşça belirginleşerek girer, sağa kaydırılarak ve yavaşça kaybolarak çıkar ve her iki durumda da yerinde kalır.
Belirli giriş ve çıkış geçişleri oluşturmak için, resim çiftindeki tek alt öğe olduğunda eski veya yeni sözde öğeleri hedeflemek üzere :only-child
sözde sınıfını kullanabilirsiniz:
/* Entry transition */
::view-transition-new(sidebar):only-child {
animation: 300ms cubic-bezier(0, 0, 0.2, 1) both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
/* Exit transition */
::view-transition-old(sidebar):only-child {
animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;
}
Bu durumda, varsayılan ayar mükemmel olduğundan kenar çubuğu her iki durumda da mevcut olduğunda belirli bir geçiş yoktur.
Eşzamansız DOM güncellemeleri ve içerik bekleme
.startViewTransition()
işlevine iletilen geri çağırma işlevi, bir promise döndürebilir. Bu promise, DOM'un eşzamansız olarak güncellenmesine ve önemli içeriğin hazır olmasını beklemeye olanak tanır.
document.startViewTransition(async () => {
await something;
await updateTheDOMSomehow();
await somethingElse;
});
Geçiş, taahhüt yerine getirilene kadar başlamaz. Bu süre zarfında sayfa donduğu için buradaki gecikmeler minimum düzeyde tutulmalıdır. Ağ getirme işlemleri, .startViewTransition()
geri çağırma işleminin bir parçası olarak değil, sayfa hâlâ tamamen etkileşimli durumdayken .startViewTransition()
çağrılmadan önce yapılmalıdır.
Resimlerin veya yazı tiplerinin hazır olmasını beklemeye karar verirseniz agresif bir zaman aşımı kullandığınızdan emin olun:
const wait = ms => new Promise(r => setTimeout(r, ms));
document.startViewTransition(async () => {
updateTheDOMSomehow();
// Pause for up to 100ms for fonts to be ready:
await Promise.race([document.fonts.ready, wait(100)]);
});
Ancak bazı durumlarda gecikmeden tamamen kaçınmak ve sahip olduğunuz içeriği kullanmak daha iyidir.
Mevcut içeriklerinizden en iyi şekilde yararlanın
Küçük resim daha büyük bir resme geçtiğinde:
Varsayılan geçiş, geçiş efektidir. Bu durumda küçük resim, henüz yüklenmemiş tam resimle geçiş efekti alabilir.
Bu sorunu çözmenin bir yolu, geçişi başlatmadan önce resmin tamamını yüklemesini beklemektir. İdeal olarak bu, .startViewTransition()
çağrılmadan önce yapılır. Böylece sayfa etkileşimli kalır ve kullanıcıya öğelerin yüklendiğini göstermek için bir döner simge gösterilebilir. Ancak bu durumda daha iyi bir yol vardır:
::view-transition-old(full-embed),
::view-transition-new(full-embed) {
/* Prevent the default animation,
so both views remain opacity:1 throughout the transition */
animation: none;
/* Use normal blending,
so the new view sits on top and obscures the old view */
mix-blend-mode: normal;
}
Artık küçük resim kaybolmuyor, tam resmin altında kalıyor. Bu, yeni görünüm yüklenmediyse geçiş boyunca küçük resmin görünür olacağı anlamına gelir. Bu sayede geçiş hemen başlayabilir ve resmin tamamı kendi zamanında yüklenebilir.
Yeni görünümde şeffaflık olsa bu yöntem işe yaramazdı. Ancak bu durumda öyle olmadığını biliyoruz. Dolayısıyla bu optimizasyonu yapabiliriz.
En boy oranında yapılan değişiklikleri işleme
Şimdiye kadarki tüm geçişler aynı en boy oranına sahip öğelere yapılmıştır ancak bu her zaman böyle olmaz. Küçük resim 1:1, ana resim ise 16:9 ise ne olur?
Varsayılan geçişte grup, önceki boyuttan sonraki boyuta animasyonla geçer. Eski ve yeni görünümler, grubun %100 genişliğinde ve otomatik yüksekliktedir. Yani grup boyutundan bağımsız olarak en boy oranlarını korurlar.
Bu iyi bir varsayılan ayardır ancak bu durumda istenen ayar değildir. Bu durumda:
::view-transition-old(full-embed),
::view-transition-new(full-embed) {
/* Prevent the default animation,
so both views remain opacity:1 throughout the transition */
animation: none;
/* Use normal blending,
so the new view sits on top and obscures the old view */
mix-blend-mode: normal;
/* Make the height the same as the group,
meaning the view size might not match its aspect-ratio. */
height: 100%;
/* Clip any overflow of the view */
overflow: clip;
}
/* The old view is the thumbnail */
::view-transition-old(full-embed) {
/* Maintain the aspect ratio of the view,
by shrinking it to fit within the bounds of the element */
object-fit: contain;
}
/* The new view is the full image */
::view-transition-new(full-embed) {
/* Maintain the aspect ratio of the view,
by growing it to cover the bounds of the element */
object-fit: cover;
}
Yani genişlik genişledikçe küçük resim öğenin ortasında kalır ancak tam resim 1:1'den 16:9'a geçerken "kırpılmaz".
Daha ayrıntılı bilgi için Geçişleri görüntüleme: En boy oranı değişikliklerini işleme başlıklı makaleyi inceleyin.
Farklı cihaz durumlarında geçişleri değiştirmek için medya sorgularını kullanma
Mobilde ve masaüstünde farklı geçişler kullanmak isteyebilirsiniz. Örneğin, mobilde tam slaytın yan tarafından tam slayt gösterildiği, masaüstünde ise daha küçük bir slaytın gösterildiği şu örnekteki gibi:
Bu, normal medya sorguları kullanılarak yapılabilir:
/* Transitions for mobile */
::view-transition-old(root) {
animation: 300ms ease-out both full-slide-to-left;
}
::view-transition-new(root) {
animation: 300ms ease-out both full-slide-from-right;
}
@media (min-width: 500px) {
/* Overrides for larger displays.
This is the shared axis transition from earlier in the article. */
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
}
Eşleşen medya sorgularına bağlı olarak hangi öğelere view-transition-name
atadığınızı da değiştirebilirsiniz.
"Düşük hareket" tercihine tepki verme
Kullanıcılar, işletim sistemleri üzerinden azaltılmış hareketi tercih ettiklerini belirtebilir ve bu tercih CSS'de gösterilir.
Bu kullanıcıların geçişlerini önlemeyi seçebilirsiniz:
@media (prefers-reduced-motion) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
Ancak, "azaltılmış hareket" tercihi kullanıcının hareket görmek istemediği anlamına gelmez. Önceki snippet yerine, öğeler arasındaki ilişkiyi ve veri akışını yansıtan, daha incelikli bir animasyon seçebilirsiniz.
Görünüm geçiş türleriyle birden fazla görünüm geçiş stilini işleme
Tarayıcı desteği
- .
- .
Bazen belirli bir görünümden diğerine geçiş için özel olarak tasarlanmış bir geçiş gerekir. Örneğin, sayfalara ayırma sırasına göre sonraki veya önceki sayfaya giderken, dizideki daha üst veya daha alt bir sayfaya mı gittiğinize bağlı olarak içerikleri farklı bir yönde kaydırmak isteyebilirsiniz.
Bunun için, etkin bir görüntüleme geçişine bir veya daha fazla tür atamanıza olanak tanıyan görüntüleme geçişi türlerini kullanabilirsiniz. Örneğin, sayfalara ayırma sırasına göre daha üst bir sayfaya geçerken forwards
türünü, daha alt bir sayfaya geçerken backwards
türünü kullanın. Bu türler yalnızca bir geçiş yakalanırken veya gerçekleştirilirken etkindir ve her tür farklı animasyonlar kullanmak için CSS aracılığıyla özelleştirilebilir.
Aynı doküman görünümü geçişinde türleri kullanmak için types
öğesini startViewTransition
yöntemine iletirsiniz. Buna izin vermek için document.startViewTransition
bir nesneyi de kabul eder: update
DOM'yi güncelleyen geri çağırma işlevidir, types
ise türlere sahip bir dizidir.
const direction = determineBackwardsOrForwards();
const t = document.startViewTransition({
update: updateTheDOMSomehow,
types: ['slide', direction],
});
Bu türlere yanıt vermek için :active-view-transition-type()
seçicisini kullanın. Hedeflemek istediğiniz type
öğesini seçiciye iletin. Böylece, birinin bildirimleri diğerinin bildirimlerini etkilemeden birden fazla görünüm geçişinin stillerini birbirinden ayrı tutabilirsiniz.
Türler yalnızca geçiş yakalanırken veya gerçekleştirilirken geçerli olduğundan, yalnızca bu türe sahip görüntüleme geçişinde bir öğede view-transition-name
değerini ayarlamak (veya ayarını kaldırmak) için seçiciyi kullanabilirsiniz.
/* 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 (using the default root snapshot) */
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;
}
}
Aşağıdaki sayfalandırma demosunda sayfa içerikleri, ziyaret ettiğiniz sayfa numarasına göre ileriye veya geriye doğru kaydırılır. Türler, document.startViewTransition
içine geçirildikleri tıklamayla belirlenir.
Türünden bağımsız olarak tüm etkin görünüm geçişlerini hedeflemek için bunun yerine :active-view-transition
sözde sınıf seçicisini kullanabilirsiniz.
html:active-view-transition {
…
}
Görünüm geçişi kökünde bir sınıf adıyla birden çok görünüm geçiş stilini işleme
Bazen belirli bir görüntüleme türünden diğerine geçiş için özel olarak tasarlanmış bir geçiş gerekir. "Geri" gezinme düğmesi de "ileri" gezinme düğmesinden farklı olmalıdır.
Geçiş türlerinden önce, bu destek kayıtları yalnızca geçiş kökü için geçici bir sınıf adı belirlemekteydi. document.startViewTransition
çağrılırken bu geçiş kökü, JavaScript'te document.documentElement
kullanılarak erişilebilen <html>
öğesidir:
if (isBackNavigation) {
document.documentElement.classList.add('back-transition');
}
const transition = document.startViewTransition(() =>
updateTheDOMSomehow(data)
);
try {
await transition.finished;
} finally {
document.documentElement.classList.remove('back-transition');
}
Geçiş tamamlandıktan sonra sınıfları kaldırmak için bu örnekte, geçiş son durumuna ulaştığında çözülen bir söz olan transition.finished
kullanılır. Bu nesnenin diğer özellikleri API referansında ele alınmıştır.
Artık geçişi değiştirmek için CSS'nizde bu sınıf adını kullanabilirsiniz:
/* 'Forward' transitions */
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in, 300ms
cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
/* Overrides for 'back' transitions */
.back-transition::view-transition-old(root) {
animation-name: fade-out, slide-to-right;
}
.back-transition::view-transition-new(root) {
animation-name: fade-in, slide-from-left;
}
Medya sorgularında olduğu gibi, bu sınıfların varlığı, hangi öğelerin view-transition-name
alacağını değiştirmek için de kullanılabilir.
Geçişleri diğer animasyonları dondurmadan çalıştır
Video geçiş konumunu gösteren bu demoya göz atın:
Bir sorunla karşılaştınız mı? Aksi halde merak etmeyin. Burada yavaşlayabilir:
Geçiş sırasında video donmuş gibi görünür, ardından videonun oynatma sürümü yavaş yavaş başlar. Çünkü ::view-transition-old(video)
görüntüsü eski görünümün ekran görüntüsü, ::view-transition-new(video)
ise yeni görünümün canlı bir resmidir.
Bu sorunu düzeltebilirsiniz ancak önce düzeltmeye değip değmeyeceğini kendinize sorun. Geçiş normal hızda oynatıldığında "sorunu" görmediyseniz değiştirmenizi önermem.
Gerçekten düzeltmek istiyorsanız ::view-transition-old(video)
simgesini göstermeyin, doğrudan ::view-transition-new(video)
simgesine geçin. Bunu varsayılan stilleri ve animasyonları geçersiz kılarak yapabilirsiniz:
::view-transition-old(video) {
/* Don't show the frozen old view */
display: none;
}
::view-transition-new(video) {
/* Don't fade the new view in */
animation: none;
}
Hepsi bu kadar!
Artık video, geçiş boyunca oynatılır.
JavaScript ile animasyon
Şimdiye kadar tüm geçişler CSS kullanılarak tanımlandı ancak bazen CSS yeterli değildir:
Bu geçişin birkaç kısmı yalnızca CSS ile yapılamaz:
- Animasyon, tıklama konumundan başlar.
- Animasyon, dairenin en uzak köşeye kadar yarıçapında olmasıyla sona erer. Yine de bunun ileride CSS ile mümkün olacağını umuyoruz.
Neyse ki Web Animation API'yi kullanarak geçişler oluşturabilirsiniz.
let lastClick;
addEventListener('click', event => (lastClick = event));
function spaNavigate(data) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
// Get the click position, or fallback to the middle of the screen
const x = lastClick?.clientX ?? innerWidth / 2;
const y = lastClick?.clientY ?? innerHeight / 2;
// Get the distance to the furthest corner
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
);
// With a transition:
const transition = document.startViewTransition(() => {
updateTheDOMSomehow(data);
});
// Wait for the pseudo-elements to be created:
transition.ready.then(() => {
// Animate the root's new view
document.documentElement.animate(
{
clipPath: [
`circle(0 at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
],
},
{
duration: 500,
easing: 'ease-in',
// Specify which pseudo-element to animate
pseudoElement: '::view-transition-new(root)',
}
);
});
}
Bu örnekte, geçiş sözde öğeleri başarıyla oluşturulduktan sonra çözülen bir söz transition.ready
kullanılmaktadır. Bu nesnenin diğer özellikleri API referansında ele alınmıştır.
Geçişler bir iyileştirme olarak
View Transition API, bir DOM değişikliğini "sarmalamak" ve bunun için bir geçiş oluşturmak üzere tasarlanmıştır. Ancak bu geçiş, uygulamanız "hata" almamalıdır. Bunun gibi, bir iyileştirme olarak değerlendirilmelidir. durumu gösterir. İdeal olarak geçiş başarısız olmamalı, ancak başarısız olursa kullanıcı deneyiminin geri kalanını bozmamalıdır.
Geçişleri bir geliştirme olarak değerlendirmek için, geçiş vaatlerini, geçişin başarısız olması durumunda uygulamanızın bozulmasına neden olacak şekilde kullanmamaya özen gösterin.
async function switchView(data) { // Fallback for browsers that don't support this API: if (!document.startViewTransition) { await updateTheDOM(data); return; } const transition = document.startViewTransition(async () => { await updateTheDOM(data); }); await transition.ready; document.documentElement.animate( { clipPath: [`inset(50%)`, `inset(0)`], }, { duration: 500, easing: 'ease-in', pseudoElement: '::view-transition-new(root)', } ); }
Bu örnekteki sorun, geçişin ready
durumuna ulaşamaması durumunda switchView()
öğesinin reddetmesidir. Ancak bu, görünümün değiştirilemediği anlamına gelmez. DOM başarıyla güncellenmiş olabilir ancak yinelenen view-transition-name
'ler olduğu için geçiş atlandı.
Bunun yerine:
async function switchView(data) { // Fallback for browsers that don't support this API: if (!document.startViewTransition) { await updateTheDOM(data); return; } const transition = document.startViewTransition(async () => { await updateTheDOM(data); }); animateFromMiddle(transition); await transition.updateCallbackDone; } async function animateFromMiddle(transition) { try { await transition.ready; document.documentElement.animate( { clipPath: [`inset(50%)`, `inset(0)`], }, { duration: 500, easing: 'ease-in', pseudoElement: '::view-transition-new(root)', } ); } catch (err) { // You might want to log this error, but it shouldn't break the app } }
Bu örnekte, DOM güncellemesini beklemek ve başarısız olursa reddetmek için transition.updateCallbackDone
kullanılmaktadır. switchView
artık geçiş başarısız olursa reddetmez. DOM güncellemesi tamamlandığında çözümlenir ve başarısız olursa reddeder.
Yeni görünüm 'yerleştirildiğinde' (örneğin, animasyonlu geçiş tamamlandığında veya sonuna kadar atlandığında) switchView
ürününün çözmesini istiyorsanız transition.updateCallbackDone
öğesini transition.finished
ile değiştirin.
Bir polyfill değil ancak…
Bu, çoklu doldurma için kolay bir özellik değildir. Ancak bu yardımcı işlev, görünüm geçişlerini desteklemeyen tarayıcılarda işleri çok daha kolay hale getirir:
function transitionHelper({
skipTransition = false,
types = [],
update,
}) {
const unsupported = (error) => {
const updateCallbackDone = Promise.resolve(update()).then(() => {});
return {
ready: Promise.reject(Error(error)),
updateCallbackDone,
finished: updateCallbackDone,
skipTransition: () => {},
types,
};
}
if (skipTransition || !document.startViewTransition) {
return unsupported('View Transitions are not supported in this browser');
}
try {
const transition = document.startViewTransition({
update,
types,
});
return transition;
} catch (e) {
return unsupported('View Transitions with types are not supported in this browser');
}
}
Bu işlev şu şekilde kullanılabilir:
function spaNavigate(data) {
const types = isBackNavigation ? ['back-transition'] : [];
const transition = transitionHelper({
update() {
updateTheDOMSomehow(data);
},
types,
});
// …
}
Görüntüleme geçişlerini desteklemeyen tarayıcılarda updateDOM
çağrılır, ancak animasyonlu bir geçiş olmaz.
Ayrıca, geçiş sırasında <html>
öğesine eklenecek bazı classNames
öğeleri de sağlayabilirsiniz. Bu sayede geçişi gezinme türüne göre değiştirmek daha kolay olur.
Görüntüleme geçişlerini destekleyen tarayıcılarda bile animasyon istemiyorsanız true
öğesini skipTransition
öğesine de iletebilirsiniz. Bu, sitenizde geçişleri devre dışı bırakmak için kullanıcı tercihi varsa kullanışlıdır.
Çerçevelerle çalışma
DOM değişikliklerini soyutlayan bir kitaplık veya çerçeveyle çalışıyorsanız işin zor kısmı, DOM değişikliğinin ne zaman tamamlandığını bilmektir. Çeşitli çerçevelerde yukarıdaki yardımcıyı kullanan bir dizi örnek aşağıda verilmiştir.
- Tepki: Buradaki anahtar, bir dizi durum değişikliğini eşzamanlı olarak uygulayan
flushSync
'tır. Evet, bu API'nin kullanımıyla ilgili büyük bir uyarı var, ancak Dan Abramov, bu durumda uygun olduğunu söylüyor. React ve eşzamansız kodda her zamanki gibi,startViewTransition
tarafından döndürülen çeşitli özellikleri kullanırken kodunuzun doğru durumda çalıştığından emin olun. - Vue.js: Buradaki anahtar
nextTick
'tır. Bu anahtar, DOM güncellendikten sonra doldurulur. - Svelte: Vue'a çok benzer ancak sonraki değişikliği beklemek için kullanılan yöntem
tick
'dır. - Açık: Buradaki temel nokta, bileşenlerde yer alan ve DOM güncellendikten sonra gerçekleşen
this.updateComplete
'tır. - Angular: Buradaki anahtar, beklemedeki DOM değişikliklerini temizleyen
applicationRef.tick
'tır. Angular sürüm 17'den itibaren@angular/router
ile birlikte gelenwithViewTransitions
özelliğini kullanabilirsiniz.
API referansı
const viewTransition = document.startViewTransition(update)
Yeni bir
ViewTransition
başlatın.update
, dokümanın mevcut durumu yakalandığında çağrılan bir işlevdir.Ardından,
updateCallback
tarafından döndürülen söz yerine getirildiğinde geçiş bir sonraki karede başlar.updateCallback
tarafından döndürülen söz reddedilirse geçiş iptal edilir.const viewTransition = document.startViewTransition({ update, types })
Belirtilen türlerle yeni bir
ViewTransition
başlatmaDokümanın geçerli durumu yakalandığında
update
çağrılır.types
, geçişi kaydederken veya gerçekleştirirken geçiş için etkin türleri ayarlar. Başlangıçta boştur. Daha fazla bilgi için aşağıdakiviewTransition.types
bölümüne bakın.
ViewTransition
örneğinin üyeleri:
viewTransition.updateCallbackDone
updateCallback
tarafından döndürülen söz yerine getirildiğinde yerine getiren veya reddedildiğinde reddeden bir söz.Görüntüleme Geçişi API'si, bir DOM değişikliğini sarmalayarak geçiş oluşturur. Ancak bazen geçiş animasyonunun başarılı olup olmadığını değil, DOM değişikliğinin olup olmadığını ve ne zaman gerçekleştiğini bilmek istersiniz.
updateCallbackDone
, bu kullanım alanı içindir.viewTransition.ready
Geçiş için sözde öğeler oluşturulduktan ve animasyon başlamak üzereyken yerine getirilen bir söz.
Geçiş başlatılamazsa reddeder. Bu durum, yinelenen
view-transition-name
öğeleri gibi yanlış yapılandırmadan veyaupdateCallback
uygulamasının reddedilen bir vaadi döndürmesinden kaynaklanabilir.Bu, geçiş sözde öğelerini JavaScript ile canlandırmak için kullanışlıdır.
viewTransition.finished
Son durum kullanıcı için tamamen görünür ve etkileşimli hale geldiğinde yerine getirilecek bir vaat.
Yalnızca
updateCallback
, son durumun oluşturulmadığını belirttiği için reddedilmiş bir taahhüt döndürürse reddedilir.Aksi takdirde, bir geçiş başlatılamazsa veya geçiş sırasında atlanırsa son duruma yine ulaşılır. Bu nedenle
finished
koşulu yerine getirilir.viewTransition.types
Aktif görüntüleme geçişinin türlerini barındıran
Set
benzeri bir nesne. Girişlerde değişiklik yapmak içinclear()
,add()
vedelete()
örnek yöntemlerini kullanın.CSS'deki belirli bir türe yanıt vermek için geçiş kökünde
:active-view-transition-type(type)
sözde sınıf seçiciyi kullanın.Görünüm geçişi tamamlandığında türler otomatik olarak temizlenir.
viewTransition.skipTransition()
Geçişin animasyon bölümünü atlama.
DOM değişikliği geçişten ayrı olduğu için
updateCallback
çağrısı atlanmaz.
Varsayılan stil ve geçiş referansı
::view-transition
- Görüntü alanını dolduran ve her bir
::view-transition-group
öğesini içeren kök sözde öğe. ::view-transition-group
Kesinlikle konumlandırılmış.
"Öncesi" ile
width
veheight
geçişleri ve "sonrası" eyaletler."Öncesi" arasındaki
transform
geçişler ve "sonrası" görüntü alanı-alan dörtlüsünü.::view-transition-image-pair
Mutlaka grubu dolduracak konumda.
mix-blend-mode
'un eski ve yeni görüntülemeler üzerindeki etkisini sınırlamak içinisolation: isolate
'e sahiptir.::view-transition-new
ve::view-transition-old
Mutlaka sarmalayıcının sol üst tarafına yerleştirilmiş.
Grup genişliğinin% 100'ünü doldurur ancak otomatik yüksekliğe sahiptir, bu nedenle grubu doldurmak yerine en boy oranını korur.
Gerçek bir çapraz geçişe izin vermek için
mix-blend-mode: plus-lighter
içeriyor.Eski görünüm
opacity: 1
'tenopacity: 0
'e geçer. Yeni görünümopacity: 0
yerineopacity: 1
.
Geri bildirim
Geliştirici geri bildirimleri bizim için çok değerli. Bunu yapmak için öneriler ve sorularla GitHub'da CSS Çalışma Grubu'na sorun bildirin. Sorununuza [css-view-transitions]
öneki ekleyin.
Bir hatayla karşılaşırsanız bunun yerine Chromium hatası bildirin.