requestAnimationFrame
requestAnimationFrame (rAF), tarayıcının ekran ritmine senkron çalışan animasyon
çağrısıdır. "Her 16ms çalış" demek yerine "ekran hazır olduğunda çağır" dediğin için,
tearing azalır, ritim stabil kalır ve arka planda kaynak tüketimi düşer.
Bu sayfada rAF’in neden tercih edildiğini, recursive kullanım modelini, high-res
timestamp ile delta time hesabını ve tarayıcı pipeline’ına etkisini kuracağız.
Neden rAF? Zamanlamanın Gücü
setInterval/setTimeout, monitörün o an ne yaptığını bilmez. Ekran 60Hz tazeliyorsa, zamanlayıcı 61. kez çizim denemesi yapabilir; bu da VSync uyumsuzluğu ve tearing riskini artırır.
requestAnimationFrame ise tarayıcıya "Ekran yeni bir kare çizmeye hazır olduğunda beni çağır" demektir. Böylece "kendi ritmini dayatmak" yerine ekran ritmine bağlanırsın.
rAF’in farkı: Yüksek refresh rate’e adaptasyon (60Hz/144Hz), arka planda otomatik pause/throttle ve tarayıcının çizim zamanlamasıyla daha uyumlu çalışma.
Bir başka kritik nokta: rAF, tarayıcının "paint" (ekrana basma) döngüsüne yakın çalışır. Bu sayede, DOM/CSS güncellemeleri + senin render çağrın aynı kare hedefinde toparlanır; "rastgele anlarda çizim" yerine, tek bir frame planına uyum sağlarsın.
rAF mucize değildir: callback hâlâ main thread üstünde çalışır. Eğer uzun işler (ağır loop’lar, büyük JSON parse, gereksiz layout tetikleri) yaparsan rAF de gecikir ve "stutter" üretir. Bu yüzden hedef; rAF’i doğru seçmek kadar, tick içini de hafif tutmaktır.
Sezgi: setInterval "benim ritmim" der; rAF ise "ekranın ritmi"ne kulak verir. Bu sezgi, aşağıdaki recursive modelin neden daha kontrollü hissettirdiğini de açıklar.
Recursive (Özyinelemeli) Yapı Bir Sonrakini Talep Et
rAF, setInterval gibi "bir kez kur, sonsuza kadar dön" modeli değildir. Her tick’te bir sonraki kareyi tekrar istersin.
Bu küçük fark, kontrolü büyütür: oyun duraklatıldığında çizimi durdurabilir, sahne yüklenirken ritmi yavaşlatabilir, hatta belirli koşullarda tamamen "render" etmeyip sadece mantık güncelleyebilirsin. Render loop mantığı için Render Loop Mantığı sayfası iyi bir tamamlayıcıdır.
Bu yüzden rAF kullanımında tipik desen şudur: "Çalışıyorsam bir sonraki frame’i iste, çalışmıyorsam isteme." Yani döngünün devamı senin kontrolündedir. Bu kontrol; pause/resume, sahne geçişleri, modal açıldığında render’ı durdurma gibi UX detaylarını çok daha temiz kılar.
İnce ama önemli bir nokta da şudur: rAF "id" döndürür. Bu id’yi saklayıp cancelAnimationFrame ile iptal edebilmek, yanlışlıkla iki loop’un aynı anda çalışmasını (çift render, iki kez update, gereksiz GPU/CPU) engeller. Premium hissin arkasında çoğu zaman bu disiplin vardır: tek kaynak, tek loop.
Mini sezgi: setInterval "otomatik pilot" gibidir; rAF ise "her karede yeniden izin" ister. Bu, sana sadece hız değil, akış kontrolü verir.
Zaman Damgası High Resolution Timestamp
rAF callback’i, çoğu zaman bir timestamp ile gelir. Bu değer, sayfa yüklendiğinden beri geçen süreyi daha hassas bir ölçekte temsil eder.
İki kare arasındaki farkı alarak \(\Delta t\) hesaplarsın: hareketi "frame sayısına" değil "gerçek zamana" bağlamanın anahtarı budur. Konuyu delta time açısından daha derin okumak için Delta Time: FPS Bağımsızlığı bölümüne de bakabilirsin.
Pratikte en sık yapılan hata, \(\Delta t\) birimini "milisaniye sanıp saniye gibi kullanmak"tır. timestamp çoğu ortamda milisaniye cinsindendir (ondalıklı hassasiyetle gelebilir); hız değerlerin "birim/saniye" ise, \(\Delta t\)’yi saniyeye çevirmen gerekir. Yani amaç; matematiği birimlerle tutarlı tutmaktır.
İlk frame’de "bir önceki zaman" yoktur; bu yüzden genelde ilk tick’te \(\Delta t\) ya 0 kabul edilir ya da başlangıç timestamp’i "previous" olarak set edilir. Bu küçük detay, ilk karede objenin zıplamasını (ani büyük hareket) engeller.
Gizli tuzak: Sekmeye gidip geri geldiğinde \(\Delta t\) bir anda büyüyebilir. Bu yüzden büyük \(\Delta t\) değerlerini "clamp" etmek (üst sınır koymak) veya simülasyonu sabit adımlarla yürütmek, animasyonun kontrolünü elinde tutmanı sağlar.
CPU–GPU Senkronizasyonu Frame Bundle
rAF yalnızca JS’i çağırmaz; tarayıcı, DOM/CSS güncellemeleri ile WebGL/Three.js çizim komutlarını aynı "kare paketi" içinde toparlamaya çalışır. Bu, render pipeline’ın daha temiz planlanmasına yardım eder.
Kısacası: rAF, update→render ritmini tarayıcının çizim zamanı ile hizalar. CPU tarafındaki mantık güncellemeleri "gerektiği kadar", GPU tarafındaki çizim ise "ekran hazır olduğunda" gerçekleşir.
Bunu "tek bir hat" gibi düşünme: tarayıcıda kabaca main thread (JS + DOM), render/compositor (katmanlama/kompozit) ve GPU (raster/3D) gibi farklı aşamalar vardır. rAF, bu aşamalar arasında "ne zaman" güncelleme yapılacağını daha öngörülebilir hale getirir; böylece sahne güncellemeleri ve çizim hazırlığı aynı frame hedefinde buluşur.
En büyük kazanım, gereksiz işlerin azalmasıdır: Yanlış zamanda yapılan DOM ölçümleri (layout thrash), aşırı stil değişimleri veya kontrolsüz redraw, bir kareyi şişirip 16ms bütçesini aşabilir. rAF ile "tick" anını daha doğru seçer; tick içindeki işi minimal tutarak hem DOM tarafını hem de WebGL komut kuyruğunu daha stabil beslersin.
Mini pratik: Eğer "neden akıcılık bozuluyor?" diye bakıyorsan, genelde cevap rAF’in kendisi değil; tick içinde biriken iş yüküdür. Bu yüzden rAF’i doğru kullanırken hedef, frame bütçesini yönetmektir (update hafif, render tutarlı).
🚀 HoloDepth Lab: Ritim ve Performans AnalizirAF’in ne kadar akıllı olduğunu canlı testlerle keşfetmeye hazır mısın? Laboratuvarda tab switch, setTimeout vs rAF hassasiyet kıyası ve 144Hz adaptasyonu gibi denemeleri yapabilirsin.
Tarayıcı Ritmini Canlı İzleyin
Tarayıcının ekran tazeleme hızıyla nasıl kusursuz bir senkron kurduğunu görün. Tab-Switch testleri ile rAF’ın “uyku modunu” ve Main Thread yükü altında ritmin nasıl bozulduğunu bizzat deneyimleyin.
rAF timestamp ve VSync hizalamasını izle; sekme değiştirip geri dön; yoğun iş yükünde ritmin nasıl kaydığını gözlemle.