CSS scroll-driven animations (kaydırmaya bağlı animasyonlar), bir @keyframes animasyonunun zamanla değil kaydırmayla ilerlemesini sağlar. Animasyonu animation-timeline özelliğiyle bir zaman çizelgesine bağlarsınız: scroll() bir kaydırma kapsayıcısının ne kadar kaydırıldığını, view() ise bir öğenin kaydırma alanında ne kadar göründüğünü izler. Kullanıcı aşağı kaydırdıkça animasyon ileri, yukarı kaydırdıkça geri gider; kaydırma durduğunda animasyon da durur. Bunun için JavaScript gerekmez.

Bu yazıda kaydırma ilerleme ve görünüm ilerleme zaman çizelgelerini, adlı ve anonim çizelge tanımlamayı, scroll() ve view() fonksiyonlarının parametrelerini, animation-range ile cover, contain, entry, exit aralıklarını ve animation kısayolunun zaman çizelgesini nasıl sıfırladığını MDN belgelerine göre anlatıyoruz. Konu @keyframes bilgisine dayandığı için önce CSS animation ve @keyframes yazısını okumanızı öneririz.

Zaman çizelgesi kavramı

Zaman çizelgesi kavramı: auto (varsayılan), none, scroll(), view()

Normal bir CSS animasyonu belgenin varsayılan zaman çizelgesinde (DocumentTimeline) çalışır: from karesinden to karesine zaman geçtikçe ilerler ve animation-duration kadar sürer. Kaydırmaya bağlı animasyonlar ise bu çizelgeyi kullanmaz; bir kaydırma ilerleme çizelgesine ya da görünüm ilerleme çizelgesine bağlanır. MDN'ye göre kullanıcının kaydırması ile animasyonun anahtar kareler boyunca ilerlemesi arasında doğrudan bir bağ vardır; kaydırma durduğunda animasyon animation-play-state: paused verilmiş gibi durur.

animation-timeline özelliği beş tür değer alır:

DeğerAnlamı
auto (varsayılan)Belgenin zamana bağlı varsayılan çizelgesi
noneHiç çizelge yok; animasyon çalışmaz
scroll()Anonim kaydırma ilerleme çizelgesi
view()Anonim görünüm ilerleme çizelgesi
--ad (dashed-ident)scroll-timeline-name ya da view-timeline-name ile adlandırılmış çizelge

MDN, JavaScript ile kaydırmaya bağlı animasyonların ana iş parçacığında scroll olay dinleyicileri ve IntersectionObserver nesneleri gerektirdiğini, ana iş parçacığını meşgul etmenin sayfanın tepkisiz kalmasına ve takılmaya yol açabileceğini belirtir; CSS ile yazılan kaydırmaya bağlı animasyonları ise performanslı olarak tanımlar.

En önemli kural: animation kısayolundan sonra yazın

animation-timeline, animation kısayolunda yalnızca sıfırlanan (reset-only) bir bileşendir. Yani animation kısayolunu her yazdığınızda daha önce verdiğiniz animation-timeline değeri auto değerine döner ve kısayolun içinde zaman çizelgesi belirleyemezsiniz. MDN bu yüzden animation-timeline özelliğinin her zaman kısayoldan sonra yazılmasını ister. MDN'nin animation sayfası ayrıca, kısayol içine zaman çizelgesi değerini desteklemeyen bir tarayıcıda yazarsanız bildirimin tamamının geçersiz sayılacağını belirtir.

css
/* yanlış: kısayol sonradan geldiği için zaman çizelgesi auto olur */
.kutu {
  animation-timeline: scroll();
  animation: dondur 1ms linear;
}

/* doğru */
.kutu {
  animation: dondur 1ms linear;
  animation-timeline: scroll();
}

@keyframes dondur {
  from {
    rotate: 45deg;
  }
  to {
    rotate: 765deg;
  }
}

Örnekteki 1ms süresi de bilinçli bir tercihtir. MDN, kaydırmaya bağlı animasyonlarda süre değerinin animasyonun uzunluğunu belirlemediğini, ancak doğrusal olmayan görünüm çizelgelerini etkileyebildiğini ve Firefox'un animasyonu uygulamak için sıfırdan farklı bir süre istediğini belirtir. Bu yüzden süreyi 1ms yazmak yaygın bir uygulamadır. animation-delay ise kaydırmaya bağlı animasyonlarda hiçbir etki yapmaz. Zamanlama fonksiyonu ve dolgu modu zamana bağlı animasyonlarla aynı şekilde çalışır; MDN örnekleri bu tür animasyonlarda linear zamanlama kullanır; böylece ilerleme kaydırma oranıyla aynı hızda değişir.

Kaydırma ilerleme çizelgesi: scroll()

Kaydırma ilerleme çizelgesinde ilerleme, bir kaydırma kapsayıcısının (scroller) baştan sona kaydırılma oranına bağlıdır: Kaydırma aralığının başı yüzde 0, sonu yüzde 100 kabul edilir. scroll() fonksiyonu iki isteğe bağlı parametre alır ve bunlar herhangi bir sırada yazılabilir:

  • Kaydırıcı: nearest (varsayılan; iki eksenden birinde kaydırma çubuğu olan en yakın ata), root (belgenin kök öğesi) ya da self (öğenin kendisi).
  • Eksen: block (varsayılan), inline, y ya da x. block ve inline yazı yönüne göre çözülür; yatay yazılan bir sayfada block dikey eksendir.

MDN'ye göre şu beş bildirim eşdeğerdir: scroll(), scroll(block), scroll(nearest), scroll(block nearest) ve scroll(nearest block). Seçilen eksende kaydırma çubuğu yoksa zaman çizelgesi etkin olmaz ve animasyon ilerlemez. Kaydırılabilir bir alan için kapsayıcının içeriği taşmalı ve overflow kaydırmaya izin vermelidir: Kaydırma yoksa zaman çizelgesi de yoktur.

Pratik bir örnek: sayfanın en üstünde, okuma ilerlemesini gösteren bir çubuk. Çubuk sayfanın kök kaydırıcısına bağlanır ve sayfa kaydırıldıkça soldan sağa dolar:

css
@supports (animation-timeline: scroll()) {
  .ilerleme {
    position: fixed;
    inset: 0 0 auto 0;
    height: 4px;
    background-color: rebeccapurple;
    transform-origin: left;
    animation: doldur 1ms linear;
    animation-timeline: scroll(root block);
  }
}

@keyframes doldur {
  from {
    scale: 0 1;
  }
  to {
    scale: 1 1;
  }
}
/* sonuç: sayfanın başında çubuk boş, en altta tam dolu */

Burada ölçekleme yatay eksende 0'dan 1'e gider ve transform-origin: left sayesinde çubuk sol kenardan büyür. Sayfa yarıya kadar kaydırıldığında ilerleme yüzde 50 olur ve doğrusal zamanlama kullanıldığı için ölçek 0 + (1 - 0) x 0,5 = 0,5 değerini alır. Çubuğun @supports bloğu içinde olması, desteklemeyen tarayıcılarda tam dolu bir çubuğun anlamsızca görünmesini önler.

Adlı kaydırma çizelgesi: scroll-timeline

Animasyonlu öğenin en yakın kaydırıcısı istediğiniz kapsayıcı değilse kaydırıcıya bir ad verirsiniz. scroll-timeline-name (ya da scroll-timeline kısayolu) kaydırıcıya yazılır ve değeri -- ile başlayan bir dashed-ident olmalıdır; MDN bunun standart CSS anahtar sözcükleriyle çakışmayı önlemek için olduğunu belirtir. Aynı ad animasyonlu öğenin animation-timeline değerine yazılır.

css
.kaydirici {
  height: 100px;
  overflow: scroll;
  scroll-timeline: --dondurme block;
  /* ad önce, eksen sonra; ters sıra geçersizdir */
}

.kaydirici .oge {
  animation: dondur 1ms linear;
  animation-timeline: --dondurme;
}

scroll-timeline kısayolunda ad ve eksen birlikte yazılırsa sıra mutlaka önce ad sonra eksendir; eksen önce gelirse bildirim geçersiz sayılır. Kaydırıcının taşan içeriği yoksa ya da taşma hidden veya clip ile gizlenmişse zaman çizelgesi oluşmaz. Adlı çizelgeler varsayılan olarak yalnızca kaydırıcının alt öğelerinden görülebilir; kardeş bir öğeyi aynı çizelgeyle canlandırmak gerektiğinde ortak bir ataya timeline-scope: --dondurme yazarak adın kapsamını genişletebilirsiniz. MDN, timeline-scope özelliğinin yalnızca adlı çizelgelerle çalıştığını, scroll() ve view() ile kullanılamayacağını belirtir.

Görünüm ilerleme çizelgesi: view()

Görünüm ilerleme çizelgesi kaydırma miktarını değil, bir öğenin (özne, subject) kaydırma alanı içindeki görünürlüğünü izler. Varsayılan olarak ilerleme, özne kaydırma alanına bir kenardan girmeye başladığında yüzde 0, karşı kenardan tamamen çıktığında yüzde 100 olur. Özne her zaman en yakın kaydırıcı atasına göre izlenir; bu yüzden view() fonksiyonunda kaydırıcı parametresi yoktur.

view() en fazla üç isteğe bağlı değer alır: bir eksen ve auto ya da en fazla iki length-percentage değerinden oluşan iç boşluk (inset). İlk iç boşluk değeri kaydırma alanının başından, ikincisi sonundan içeri doğru bir kaydırmadır; pozitif değer alanı daraltır, negatif değer genişletir. Eksen ve iç boşluk herhangi bir sırada yazılabilir. Parametresiz view(), view(block auto) ile eşdeğerdir.

MDN önemli bir tasarım uyarısı yapar: Yüzde 100 noktasına genellikle öğe görünüm alanından çıkarken ulaşıldığı için, animasyonun "tamamlanmış" görünümünü to karesine değil daha önceki bir kareye (yüzde 20, 50 ya da 80 gibi) koymak isteyebilirsiniz. Böylece öğe hâlâ görünürken animasyon biter.

css
@supports (animation-timeline: view()) {
  .kart {
    animation: belir 1ms linear both;
    animation-timeline: view();
  }
}

@keyframes belir {
  0%,
  20% {
    opacity: 0;
    translate: 0 40px;
  }
  50%,
  100% {
    opacity: 1;
    translate: 0 0;
  }
}
/* sonuç: kart görünüme girdikçe belirir, ekranın ortasına gelmeden yerine oturur */

Aynı amaçla iç boşluk da kullanılabilir. MDN örneğinde view(block 20% 20%) yazıldığında görünür alan her iki uçtan yüzde 20 daraltılır ve animasyon bu daraltılmış alanın başı ile sonu arasında çalışır; bu sayede from ve to kareleri rahatça kullanılabilir.

Adlı görünüm çizelgesi de mümkündür: view-timeline-name (ya da view-timeline kısayolu) özneye yazılır. Adlı çizelgenin avantajı, canlandırılan öğenin özneyle aynı olmak zorunda olmamasıdır; bir öğenin kaydırma alanındaki hareketine göre başka bir öğeyi canlandırabilirsiniz. Kısayolda sıra önce ad, sonra eksen ve iç boşluktur: view-timeline: --kart block 20% 200px;

animation-range ve aralık adları

animation-range ve aralık adları: animation-range, animation-range, animation-range, animation-range

animation-range, animasyonun zaman çizelgesinin hangi bölümüne bağlanacağını belirler. animation-range-start ve animation-range-end özelliklerinin kısayoludur. Değerler normal, bir length-percentage, bir aralık adı ya da aralık adı ile yüzde çiftinden oluşur. normal başlangıç için yüzde 0, bitiş için yüzde 100 demektir.

Aralık adıGörünüm çizelgesinde kapsadığı bölüm
coverÖzne alana girmeye başladığı andan tamamen çıktığı ana kadar; görünüm çizelgesinin varsayılanı
containÖzne alanın içinde tamamen kaldığı (ya da alanı tamamen kapladığı) süre
entryÖzne girmeye başladığı andan tamamen girdiği ana kadar
exitÖzne çıkmaya başladığı andan tamamen çıktığı ana kadar
entry-crossingÖznenin alanın bitiş kenarını geçtiği süre
exit-crossingÖznenin alanın başlangıç kenarını geçtiği süre
scrollKaydırıcının tüm kaydırma aralığı; kaydırma çizelgesinin varsayılanı

Aralıklar birbiriyle bağlantılıdır: entry aralığının yüzde 0 noktası cover aralığının yüzde 0 noktasına, yüzde 100 noktası contain aralığının yüzde 0 noktasına eşittir. exit ise contain aralığının yüzde 100 noktasında başlar ve cover aralığının yüzde 100 noktasında biter.

Kısayolun tek değerli yazımında kurallar MDN'de ayrıntılı açıklanır ve sık karıştırılır:

  • animation-range: 20%; başlangıcı yüzde 20 yapar, bitiş normal kalır.
  • animation-range: contain; hem başlangıca hem bitişe aynı adı uygular: contain 0% ile contain 100%.
  • animation-range: cover 20%; başlangıcı cover 20%, bitişi cover 100% yapar.
  • animation-range: 25% contain; başlangıcı yüzde 25, bitişi contain 100% yapar.
  • animation-range: entry 10% exit 90%; iki ucu ayrı ayrı belirler.
css
@supports (animation-timeline: view()) {
  .gorsel {
    animation: buyu 1ms linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 100%;
    /* animasyon yalnızca öğe görünüme girerken çalışır */
  }
}

@keyframes buyu {
  from {
    scale: 0.8;
    opacity: 0;
  }
  to {
    scale: 1;
    opacity: 1;
  }
}

Burada both dolgu modu önemlidir. MDN, anahtar kare stillerinin varsayılan olarak yalnızca aralığın içinde uygulandığını; aralığın dışında da uygulanmaları için animation-fill-mode değerinin backwards, forwards ya da both olması gerektiğini belirtir. both olmadan öğe aralığa girmeden önce tam boyutta görünür, sonra aniden küçülüp animasyona başlardı. animation-range da animation kısayolu tarafından sıfırlanır; onu da kısayoldan sonra yazın. @keyframes içinde aralık adı ile yüzde de kullanılabilir (örneğin entry 50%); bu durumda kare, belirtilen aralığın o noktasına yerleşir.

Yön, tekrar ve diğer özelliklerin davranışı

Zamana bağlı animasyon özelliklerinin çoğu kaydırmaya bağlı animasyonlarda da çalışır ama bazı farklar vardır. MDN'ye göre animation-direction: reverse animasyonu çizelge ilerledikçe tersten oynatır; alternate bir tekrar sayısıyla birlikte kullanıldığında çizelge boyunca ileri geri oynar. animation-iteration-count verilen sayı kadar tekrarı çizelgenin tamamına yayar; verilmezse animasyon bir kez gerçekleşir. infinite sözdizimi açısından geçerlidir ama MDN bunun çalışmayan bir animasyonla sonuçlandığını belirtir. animation-duration: auto değeri kaydırmaya bağlı animasyonda çizelgenin tamamını doldurur.

Erişilebilirlik

Tüm animasyonlarda olduğu gibi burada da kullanıcının prefers-reduced-motion tercihine saygı gösterilmelidir. MDN, animation-timeline: none değerinin öğeyi tüm zaman çizelgelerinden, varsayılan zamana bağlı çizelge dahil, ayırdığını ve öğenin hiç canlanmayacağını belirtir. Bu değer hareket azaltma tercihinde isteğe bağlı animasyonları kapatmak için kullanılabilir:

css
@media (prefers-reduced-motion: reduce) {
  .istege-bagli-animasyon {
    animation-timeline: none;
  }
}

MDN'nin uyarısı burada da geçerlidir: animation kısayolu zaman çizelgesini auto yaptığı için bu kuralın, kısayolu içeren kuraldan yeterince yüksek özgüllüğe sahip olması ya da kaynakta ondan sonra gelmesi gerekir. Medya sorgularının sözdizimi @media yazısında, @supports ayrıntıları @supports yazısında anlatılıyor.

Sık yapılan hatalar

Sık yapılan hatalar: Zaman çizelgesini kısayoldan önce yazmak, Kaydırma olmayan kapsayıcı, Yanlış eksen, Ad tirelerle başlamıyor
  • Zaman çizelgesini kısayoldan önce yazmak: animation kısayolu onu auto yapar ve animasyon zamana bağlı çalışır.
  • Kaydırma olmayan kapsayıcı: İçerik taşmıyorsa ya da taşma hidden ise çizelge oluşmaz.
  • Yanlış eksen: Seçilen eksende kaydırma çubuğu yoksa çizelge etkin olmaz.
  • Ad tirelerle başlamıyor: Çizelge adları --ad biçiminde olmalıdır.
  • Sonsuz tekrar: infinite kaydırmaya bağlı animasyonda çalışmaz.
  • Gecikme beklemek: animation-delay burada etkisizdir; zamanlamayı animation-range ile ayarlayın.
  • Dolgu modunu unutmak: Aralık dışındaki durum için both gerekir.

Sık Sorulan Sorular

scroll() ile view() arasındaki fark nedir?

scroll() bir kaydırıcının ne kadar kaydırıldığını izler ve okuma ilerleme çubuğu gibi sayfa geneli efektlere uygundur. view() belirli bir öğenin görünüm alanında ne kadar göründüğünü izler ve öğeler ekrana girerken belirme efektleri için uygundur.

Kaydırma tetiklemeli animasyonla aynı şey mi?

Hayır. Kaydırmaya bağlı animasyonda ilerleme doğrudan kaydırma konumuna bağlıdır ve geri kaydırınca animasyon da geri gider. MDN ayrıca, belirli bir kaydırma noktasına gelindiğinde zamana bağlı bir animasyonu başlatan ayrı bir "animation triggers" modülünü ayrı bir rehberde anlatır.

animation-duration neden 1ms yazılıyor?

Süre kaydırma animasyonunun uzunluğunu belirlemez, ama bazı tarayıcılar animasyonu uygulamak için sıfırdan farklı bir süre ister. MDN bu nedenle 1ms değerini yaygın uygulama olarak gösterir.

Desteklemeyen tarayıcılarda ne olur?

Tarayıcı animation-timeline özelliğini tanımazsa animasyon normal zaman çizelgesinde 1ms süreyle çalışır; MDN, bu sürenin animasyonu kullanıcıya görünmez kıldığını belirtir. Dolgu modu forwards ya da both ise öğe son karenin stilinde kalır. Desteği @supports ile kontrol edip içeriğin animasyonsuz halinin de kullanılabilir olmasını sağlayın.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: CSS öğrenme rehberi · CSS animation ve @keyframes · View transitions · overflow ve kaydırma · @supports · CSS transform · @media sözdizimi