Görüntüleme geçişi tek bir dokümanda çalıştırıldığında, buna 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ı doküman görüntüleme geçişleri Chrome 111 sürümünden itibaren Chrome'da desteklenmektedir.
Aynı doküman görüntüleme geçişini tetiklemek için document.startViewTransition
numaralı telefonu arayı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());
}
Çağrılduğunda, tarayıcı, üzerinde view-transition-name
CSS özelliği tanımlanmış tüm öğelerin anlık görüntülerini otomatik olarak yakalar.
Ardından, iletilen geri çağırmayı yürüterek DOM'u günceller 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üler çiftler eski konumlarından ve boyutlarından yeni konumlarına sorunsuz bir şekilde geçiş yaparken içerikleri değişir. İsterseniz animasyonları özelleştirmek için CSS'yi kullanabilirsiniz.
Varsayılan geçiş: Çapraz geçiş
Varsayılan görünüm geçişi çapraz geçiştir; bu nedenle API'yi tanıtmak için iyi bir giriş işlevi görür:
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'u yeni duruma değiştirir. Bunu istediğiniz gibi yapabilirsiniz. Örneğin, öğe ekleyip kaldırabilir, sınıf adlarını ya da stilleri değiştirebilirsiniz.
Bu şekilde, sayfalar çapraz olarak şeffaflaştırılır:
Pekala, çapraz geçiş o kadar da etkileyici değil. Neyse ki geçişler özelleştirilebilir ama öncelikle bu temel çapraz geçişinin 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. Fotoğraf çekmek de buna dahildir.
Tamamlandığında, .startViewTransition()
öğesine iletilen geri çağırma ç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 yapay öğ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
öğesi bir yer paylaşımında, sayfadaki diğer her şeyin üzerine yerleştirilir. Bu, geçiş için bir arka plan rengi ayarlamak istediğinizde yararlı olur.
::view-transition-old(root)
eski görünümün ekran görüntüsü, ::view-transition-new(root)
ise yeni görünümün canlı temsilidir. Her ikisi de CSS'nin "değiştirilmiş içerik" olarak (<img>
gibi) oluşturulur.
Eski görünüm opacity: 1
ile opacity: 0
arasında, yeni görünüm ise opacity: 0
ile opacity: 1
arasında animasyonlarla bir çapraz 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, mevcut CSS animasyon özelliklerini kullanarak bunları değiştirebilirsiniz. Örneğin:
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 5s;
}
Bu tek değişiklikle birlikte, bu geçiş artık gerçekten yavaşlamış:
Tamam, bu hâlâ etkileyici değil. Bunun yerine, aşağıdaki kod Materyal Tasarım'ı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 çok öğeyi geçirme
Önceki demoda, paylaşılan eksen geçişinde tüm sayfa yer alır. Bu yöntem sayfanın büyük bir kısmında işe yarar, ancak başlık tekrar kayarak aşağı kaydığı için başlık için pek doğru sayılmaz.
Bunu önlemek için başlığı sayfanın geri kalanından çıkartıp ayrı olarak canlandırabilirsiniz. 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ı olmadığı anlamına gelir). Öğeyi geçiş boyunca benzersiz bir şekilde tanımlamak için kullanılır.
Bunun sonucu:
Böylece başlık yerinde kalır ve yanarak geçiş yapar.
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)
Şu anda iki geçiş grubu vardır. Biri başlık, diğeri de geri kalan kısım için. Bunlar CSS ile bağımsız olarak hedeflenebilir ve farklı geçişler verilir. Yine de bu örnekte, çapraz geçiş olan varsayılan geçişle main-header
kalmıştır.
Tamamdır, varsayılan geçiş sadece çapraz geçişi değil, ::view-transition-group
aynı zamanda geçiş yapar:
- Konumlandırma ve dönüştürme (
transform
kullanarak) - Genişlik
- Boy
Başlık aynı boyutta olduğundan ve DOM değişikliğinin her iki tarafında da konumlandığından, bu şimdiye kadar önemli değildi. Ancak, başlıktaki metni de çıkarabilirsiniz:
.main-header-text {
view-transition-name: main-header-text;
width: fit-content;
}
Öğenin kalan genişliğe uzatmak yerine, metnin boyutu olması için fit-content
kullanılır. Bu olmadan, geri ok simgesi her iki sayfada aynı boyut yerine başlık metin öğesinin boyutunu küçültür.
Şimdi üzerinde oynayacağımız üç bölüm var:
::view-transition
├─ ::view-transition-group(root)
│ └─ …
├─ ::view-transition-group(main-header)
│ └─ …
└─ ::view-transition-group(main-header-text)
└─ …
Ama yine de varsayılanlara devam edelim:
Şimdi başlık metni, geri düğmesine yer açmak için aşağı doğru küçük bir kaydırma yapıyor.
Birden fazla sözde öğeye view-transition-class
ile aynı şekilde animasyon uygulama
Tarayıcı Desteği
- 125
- 125
- x
- x
Birçok kartın yanı sıra sayfada bir başlık içeren bir görüntüleme geçişiniz olduğunu varsayalım. Başlık hariç tüm kartları canlandırmak 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ı? Yazmanız gereken 20 seçici var. Yeni bir öğe mi ekliyorsunuz? Daha sonra, animasyon stillerini uygulayan seçiciyi de büyütmeniz gerekir. Tam olarak ölçeklenebilir değildir.
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 kartlar örneğinde önceki CSS snippet'inden yararlanılmıştır. Yeni eklenenler de dahil tüm kartlara tek bir seçiciyle aynı zamanlama uygulanır: html::view-transition-group(.card)
.
Hata ayıklama geçişleri
Görünüm geçişleri CSS animasyonlarına dayalı olarak oluşturulduğundan, Chrome Geliştirici Araçları'ndaki Animasyonlar paneli, geçişlerde hata ayıklamak için idealdir.
Animasyonlar panelini kullanarak bir sonraki animasyonu duraklatabilir ve ardından animasyonda ileri geri sarabilirsiniz. Bu işlem sırasında, sözde geçiş öğeleri Öğeler panelinde bulunabilir.
Geçiş öğelerinin aynı DOM öğesi olması gerekmez
Şimdiye kadar başlık ve başlık metni için ayrı geçiş öğeleri oluşturmak üzere view-transition-name
kodunu kullandık. Bunlar, DOM değişikliğinden önceki ve sonraki kavramsal olarak aynı öğelerdir, ancak istemediğiniz durumlarda geçişler oluşturabilirsiniz.
Örneğin, ana video yerleştirmesine view-transition-name
verilebilir:
.full-embed {
view-transition-name: full-embed;
}
Daha sonra, küçük resim tıklandığında yalnızca geçiş süresince aynı view-transition-name
değeri verilebilir:
thumbnail.onclick = async () => {
thumbnail.style.viewTransitionName = 'full-embed';
document.startViewTransition(() => {
thumbnail.style.viewTransitionName = '';
updateTheDOMSomehow();
});
};
Sonuç:
Küçük resim şimdi ana resme geçiş yapar. Bunlar kavramsal olarak (ve kelime anlamıyla) farklı öğeler olsa da, aynı view-transition-name
öğesini paylaştıklarından geçiş API'si bunları aynı şey olarak ele alır.
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. Eksiksiz uygulama için kaynağa bakın.
Özel giriş ve çıkış geçişleri
Şu örneğe bakın:
Kenar çubuğu geçişin bir parçasıdır:
.sidebar {
view-transition-name: sidebar;
}
Ancak önceki örnekteki başlığın aksine, 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 burada yer almaz. 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)
bulunur.
Önceki demoda kenar çubuğunun geçişi, her iki durumda da giriş, çıkış veya her iki durumda da yapılmasına bağlı olarak değişir. Sağdan içeri kayarak girip yavaşça girer, sağa doğru kayarak çıkar ve her iki durumda da sabit kalır.
Belirli giriş ve çıkış geçişleri oluşturmak amacıyla, resim çiftindeki tek alt öğe olduğunda eski veya yeni sözde öğeleri hedeflemek için :only-child
sözde sınıfı 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 bekleniyor
.startViewTransition()
öğesine iletilen geri çağırma, eşzamansız DOM güncellemelerine olanak tanıyan ve önemli içeriğin hazır olmasını bekleyen bir söz verebilir.
document.startViewTransition(async () => {
await something;
await updateTheDOMSomehow();
await somethingElse;
});
Sözü yerine getirilene kadar geçiş başlamaz. Bu süre zarfında sayfa donduğundan, buradaki gecikmeler en az düzeyde tutulmalıdır. Özellikle ağ getirme işlemleri, .startViewTransition()
geri çağırma işleminin bir parçası olarak yapılmak yerine sayfa hâlâ tamamen etkileşimliyken .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, gecikmeyi tamamen önlemek ve halihazırda sahip olduğunuz içeriği kullanmak daha iyidir.
Mevcut içerikten en iyi şekilde yararlanın
Küçük resmin daha büyük bir resme dönüştüğü durumlarda:
Varsayılan geçiş, çapraz geçiştir. Bu, küçük resmin henüz yüklenmemiş tam bir resimle çapraz olarak şeffaflaşabileceği anlamına gelir.
Bunu halletmenin bir yolu, geçişi başlatmadan önce resmin tamamının yüklenmesini beklemektir. İdeal olarak bu, .startViewTransition()
çağrısından önce yapılır. Böylece, sayfa etkileşimli durumda kalır ve kullanıcıya öğelerin yüklenmekte olduğunu belirten bir döner simge gösterilebilir. Ancak bu durumda daha iyi bir yol var:
::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;
}
Şimdi küçük resim kaybolmuyor, yalnızca tam resmin altında duruyor. Yani, yeni görünüm yüklenmediyse küçük resim geçiş boyunca görünür. Bu, geçişin hemen başlayabileceği ve kendi kendine tam resmin yüklenebileceği anlamına gelir.
Yeni görünümde şeffaflık öne çıkarılıyorsa bu işe yaramaz. Ancak bu örnekte bunun böyle olmadığını biliyoruz ve dolayısıyla bu optimizasyonu yapabiliriz.
En boy oranındaki değişiklikleri işleme
Kolay bir şekilde, şimdiye kadarki tüm geçişler aynı en boy oranına sahip öğelerde gerçekleşmiştir, ancak bu her zaman yaşanmayabilir. Küçük resim 1:1, ana resim 16:9 ise ne olur?
Varsayılan geçişte grup, önceki boyuttan sonraki boyuta animasyonu yapar. Eski ve yeni görünümler, grubun% 100 genişliğindedir ve otomatik yüksekliktir. Yani en boy oranlarını grubun boyutundan bağımsız olarak korurlar.
Bu iyi bir varsayılan ayardır ancak bu durumda istenen şey 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, küçük resim genişlik genişledikçe öğenin ortasında kalır, ancak tam resim 1:1'den 16:9'a geçerken "kırpmayı kaldırır".
Daha ayrıntılı bilgi için (Geçişleri görüntüleme: En boy oranı değişikliklerini işleme)(https://jakearchibald.com/2024/view-transitions-handling-aspect-ratio-changes/) başlıklı makaleyi inceleyin
Farklı cihaz durumlarının geçişlerini değiştirmek için medya sorgularını kullanma
Mobilde ve masaüstünde farklı geçişler kullanmak isteyebilirsiniz. Örneğin, mobil cihazda yandan tam bir slayt, masaüstünde ise daha ince bir slayt gösterilen bu örnek:
Bu, normal medya sorguları kullanılarak elde edilebilir:
/* 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 view-transition-name
atayacağınız öğeleri değiştirmek de isteyebilirsiniz.
"Azaltılmış hareket" tercihine tepki verme
Kullanıcılar, işletim sistemlerinde daha az hareket tercih ettiklerini ve bu tercihin CSS'de gösterildiğini belirtebilir.
Şu kullanıcıların geçiş yapmasını engelleyebilirsiniz:
@media (prefers-reduced-motion) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
Bununla birlikte, "azaltılmış hareket" tercihi, kullanıcının hareketsiz olduğu anlamına gelmez. Önceki snippet yerine, öğeler ve veri akışı arasındaki ilişkiyi ifade eden, daha sade bir animasyon seçebilirsiniz.
Görünüm geçiş türleriyle birden çok görünüm geçiş stilini yönetme
Bazen bir görünümden diğerine geçişin özel olarak uyarlanmış bir geçişi olmalıdır. Örneğin, sayfalandırma sırasında sonraki veya önceki sayfaya giderken içeriği farklı bir yönde kaydırmak isteyebilirsiniz. Bu durum, sıralamadan daha üst veya daha alt bir sayfaya gitmenize bağlıdır.
Bunun için, bir etkin görünüm geçişine bir veya daha fazla tür atamanıza olanak tanıyan görünüm geçiş türlerini kullanabilirsiniz. Örneğin, sayfalandırma sırasında daha yüksek bir sayfaya geçiş yaparken forwards
türünü, daha düşük bir sayfaya giderken 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 kullanacak şekilde CSS ile özelleştirilebilir.
Aynı doküman görüntüleme geçişinde türleri kullanmak için types
yöntemini startViewTransition
yöntemine geçirirsiniz. Buna izin vermek için document.startViewTransition
, bir nesneyi de kabul eder: update
, DOM'u güncelleyen geri çağırma işlevi, 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çiciyi kullanın. Hedeflemek istediğiniz type
öğesini seçiciye iletin. Böylece, birden fazla görünüm geçişinin stillerini, bir tanesinin bildirimleri diğerinin bildirimlerine müdahale etmeden, birbirinden ayrı tutabilirsiniz.
Türler yalnızca geçiş yakalanırken veya gerçekleştirilirken geçerli olduğundan, bir öğede yalnızca bu tür görünüm geçişinde view-transition-name
ayarlamak (veya ayarı 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 sayfalara ayırma demosunda, sayfa içerikleri, gittiğiniz sayfa numarasına göre ileriye veya geriye doğru kaydırılır. Türler, tıklanarak document.startViewTransition
ürününe iletilecekleri şekilde belirlenir.
Türü ne olursa olsun herhangi bir aktif görüntüleme geçişini hedeflemek için bunun yerine :active-view-transition
sözde sınıf seçiciyi kullanabilirsiniz.
html:active-view-transition {
…
}
Görünüm geçiş kökünde bir sınıf adıyla birden çok görünüm geçiş stilini işleyin
Bazen belirli bir görünüm türünden diğerine geçişin özel olarak uyarlanmış bir geçişi olmalıdır. Ya da 'geri' gezinme, 'ileri' gezinmeden farklı olmalıdır.
Geçiş türlerinden önce, bu destek yazışmalarını ele almak için geçici olarak geçiş köküne bir sınıf adı belirlemek gerekiyordu. 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');
}
Bu örnekte, geçiş tamamlandıktan sonra sınıfların kaldırılması için transition.finished
kullanılmıştır. Bu, geçiş, son durumuna ulaştığında kesinleşen bir vaadidir. Bu nesnenin diğer özellikleri API referansında ele alınmıştır.
Artık geçişi değiştirmek için bu sınıf adını CSS'de 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ırma
Video geçiş pozisyonunu gösteren bu demoya göz atın:
Videoda yanlış bir şey gördünüz mü? Görmediyseniz de endişelenmeyin. Burada yavaşlamış:
Geçiş sırasında video donar, ardından videonun oynatılan sürümü kaybolur. Bunun nedeni, ::view-transition-old(video)
öğesinin eski görünümün ekran görüntüsü, ::view-transition-new(video)
biriminin ise yeni görünümün canlı bir resmi olmasıdır.
Bunu düzeltebilirsiniz, ancak önce kendinize bunun düzeltmeye değip değmeyeceğini sorun. Geçiş normal hızında gerçekleşirken 'sorun' sorununu görmediyseniz, onu değiştirmem gerekmez.
Sorunu gerçekten düzeltmek istiyorsanız ::view-transition-old(video)
göstermeyin; doğrudan ::view-transition-new(video)
öğesine 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!
Video şimdi geçiş boyunca oynatılır.
JavaScript ile animasyon uygulama
Şu ana kadar tüm geçişler CSS kullanılarak tanımlanmıştır, ancak bazen CSS yeterli olmayabilir:
Bu geçişin bazı kısmenleri tek başına CSS ile gerçekleştirilemez:
- Animasyon, tıklama konumundan başlar.
- Animasyon, dairenin en uzak köşeye kadar bir yarıçapına sahip olmasıyla sona erer. Yine de bunun gelecekte 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, sözde geçiş öğeleri başarıyla oluşturulduktan sonra çözümlenen transition.ready
vaadi kullanılmıştır. Bu nesnenin diğer özellikleri API referansında ele alınmıştır.
Bir geliştirme olarak geçişler
View Transition API, bir DOM değişikliğini "sarmalamak" ve bununla ilgili bir geçiş oluşturmak için tasarlanmıştır. Bununla birlikte, DOM değişikliği başarılı olsa da geçiş başarısız olursa geçiş, bir geliştirme olarak kabul edilmelidir. İdeal olarak geçiş başarısız olmamalıdır, ancak böyle bir durumda kullanıcı deneyiminin geri kalanı bozulmaz.
Geçişleri bir geliştirme olarak değerlendirmek için geçiş sözlerini, geçişin başarısız olması durumunda uygulamanızın iptal etmesine yol açacak şekilde kullanmamaya dikkat edin.
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()
ürününün reddedileceğidir, ancak bu, görünümün değiştirilemediği anlamına gelmez. DOM başarıyla güncellenmiş olabilir ancak kopya 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 güncelleme başarısız olursa reddetmek için transition.updateCallbackDone
kullanılır. Geçiş başarısız olursa switchView
artık reddetmez. DOM güncellemesi tamamlandığında çözümlenir, başarısız olursa reddedilir.
Yeni görünüm "yerleştirildiğinde" switchView
tarafından çözümlenmesini istiyorsanız (örneğin, tüm animasyonlu geçişler tamamlandığında veya sonuna atlandığında) transition.updateCallbackDone
değerini transition.finished
ile değiştirin.
Çoklu dolgu değil ama...
Bu, çoklu dolgu 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');
}
}
Ve ş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ılacaktır, ancak animasyonlu bir geçiş olmayacaktır.
Geçiş sırasında <html>
ürününe eklemek için classNames
sağlayarak gezinme türüne bağlı olarak geçişi değiştirmeyi kolaylaştırabilirsiniz.
Ayrıca, görüntüleme geçişlerini destekleyen tarayıcılarda bile animasyon istemiyorsanız true
öğesini skipTransition
öğesine de iletebilirsiniz. Sitenizde geçişlerin devre dışı bırakılması yönünde bir kullanıcı tercihi varsa bu yararlı olur.
Ç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. Yukarıdaki yardımcıyı çeşitli çerçevelerde kullanan bir dizi örneği burada bulabilirsiniz.
- Tepki: Buradaki anahtar, bir dizi durum değişikliğini eşzamanlı olarak uygulayan
flushSync
'tır. Evet, söz konusu API'nın kullanımıyla ilgili önemli bir uyarı var ama Dan Abramov bu durumda uygun olacağı konusunda bana güvence veriyor. React ve eşzamansız kodda her zaman olduğu gibi,startViewTransition
tarafından döndürülen çeşitli vaatleri kullanırken kodunuzun doğru durumda çalıştığından emin olun. - Vue.js: Burada anahtar olan
nextTick
, DOM güncellendikten sonra yerine getirilir. - Svelte: Vue'ya çok benzer, ancak bir sonraki değişikliği bekleme yöntemi
tick
. - Lit: Buradaki anahtar, bileşenlerde
this.updateComplete
sözü verilendir ve DOM güncellendikten sonra yerine getirilir. - Açısal: Buradaki anahtar, bekleyen DOM değişikliklerini temizleyen
applicationRef.tick
'tir. 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
, belgenin mevcut durumu yakalandıktan sonra çağrılan bir işlevdir.Ardından,
updateCallback
tarafından verilen söz yerine getirildiğinde geçiş işlemi bir sonraki karede başlar.updateCallback
tarafından verilen söz reddedilirse geçiş iptal edilir.const viewTransition = document.startViewTransition({ update, types })
Belirtilen türlerle yeni bir
ViewTransition
başlatınBelgenin mevcut durumu yakalandıktan sonra
update
çağrılır.types
, geçişi yakalarken veya gerçekleştirirken kullanılacak etkin geçiş türlerini ayarlar. Başlangıçta boştur. Daha fazla bilgi için daha aşağıda bulunanviewTransition.types
konusuna bakın.
ViewTransition
örneğinin üyeleri:
viewTransition.updateCallbackDone
updateCallback
tarafından verilen söz yerine getirildiğinde söz konusu vaat yerine getirilir veya reddedildiğinde söz konusu taahhüt reddedilir.View Transition API, bir DOM değişikliğini sarmalar ve bir geçiş oluşturur. Ancak bazen geçiş animasyonunun başarısı veya başarısızlığı önemsenmez. Tek yapmanız gereken DOM değişikliğinin olup olmadığını ve ne zaman olacağını bilmektir.
updateCallbackDone
bu kullanım alanı içindir.viewTransition.ready
Geçişe ilişkin sözde öğeler oluşturulduğunda ve animasyon başlamak üzereyken yerine getirilecek bir taahhüt.
Geçişin başlayamazsa bunu reddeder. Bunun nedeni, yinelenen
view-transition-name
'ler gibi yanlış yapılandırma veyaupdateCallback
ürününün reddedilen bir söz döndürmesi olabilir.Bu, JavaScript ile geçiş sözde öğelerinin animasyonunu yaparken işe yarar.
viewTransition.finished
Son durum tamamen görünür hale geldiğinde ve kullanıcı tarafından etkileşimli olarak sunulduğunda yerine getirilen bir taahhüt.
Yalnızca
updateCallback
reddedilen bir sözü döndürürse reddeder. Bu durum, bitiş durumunun oluşturulmadığını gösterir.Aksi halde, bir geçiş başlamazsa veya geçiş sırasında atlanırsa bitiş durumuna yine de ulaşılmış olur. Dolayısıyla
finished
, isteği karşılar.viewTransition.types
Aktif görüntüleme geçiş türlerini barındıran
Set
benzeri bir nesne. Girişleri değiştirmek için örnek yöntemlerini kullanın:clear()
,add()
vedelete()
.CSS'de belirli bir türe yanıt vermek için geçiş kökündeki
: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 kısmını atlayın.
DOM değişikliği geçişten ayrı olduğu için bu işlem
updateCallback
çağrısını atlamaz.
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ı.
"Önce" ve "sonra" durumları arasında
width
veheight
geçişlerini yapar.transform
adlı "önce" ve "sonra" görüntü alanı-boşluk dörtlüsü arasında geçiş yapar.::view-transition-image-pair
Mutlaka grubu dolduracak biçimde konumlandırılmış.
mix-blend-mode
öğesinin eski ve yeni görünümler üzerindeki etkisini sınırlandırmak içinisolation: isolate
özelliği vardır.::view-transition-new
ve::view-transition-old
Mutlaka, sarmalayıcının sol üst tarafına konumlandırılır.
Grup genişliğinin% 100'ünü doldurur, ancak otomatik bir yüksekliğe sahip olduğundan grubu doldurmak yerine en boy oranını korur.
Gerçek çapraz geçişe olanak tanımak için
mix-blend-mode: plus-lighter
özelliği vardır.Eski görünüm
opacity: 1
ürünündenopacity: 0
görünümüne geçiş yapar. Yeni görünümopacity: 0
ürünündenopacity: 1
görünümüne geçer.
Geri bildirim
Geliştiricilerin geri bildirimleri her zaman çok değerlidir. Bunu yapmak için öneriler ve sorularla GitHub'da CSS Çalışma Grubu'na sorun bildirin. Sorununuzun önüne [css-view-transitions]
ekleyin.
Bir hatayla karşılaşırsanız bunun yerine Chromium hatası bildiriminde bulunun.