holodepth

Holodepth • ByteOmi Köprü • Zamanlama

requestAnimationFrame

Ekran ritmini mi takip ediyorsun, yoksa kendi ritmini mi dayatıyorsun? rAF, “ekran hazır olduğunda çağır” der; setInterval ise sabit bir zamanlayıcı ritmine bağlanır — bu da animasyonlarda drift/jitter üretir.

Bu köprü sayfası render loop’un anatomisini sıfırdan anlatmak için değil; “neden rAF kullanılır?” hissini kurmak için durur: recursive kullanım modeli, high‑res timestamp ile \(\Delta t\) ve main-thread üzerindeki iş yükünün etkisi.

Kaynak metin: byteomi.com • Önceki köprü: Render Loop • Loop’un “kalp atışı” ile birlikte düşün.

Neden setInterval animasyonları bozar? — “Browser Pulse Monitor”

Soruyu şöyle kur: ekranın ritmini mi takip ediyorsun, yoksa kendi ritmini mi dayatıyorsun? Radar çizgisi her tick’te ilerler; stamp’ler (iz noktaları) “tick düzeni”ni görünür kılar. rAF modunda düzen stabil kalır; setInterval modunda ise zamanlayıcı ile gerçek dünya zamanı uyumsuzlaşır ve jitter/drift büyür. Jammer’ı açarak main thread’i şişir ve ritmin nasıl kekeleyeceğini izle.

Hero demo

Bu demo render loop’un anatomisini değil; neden rAF kullanılır hissini verir: ekranla senkron ritim vs zamanlayıcı drift/jitter • recursive start/stop • timestamp → Δt • main-thread jammer.

FPS: • frame: ms timestamp: ms • Δt: ms
● rAF: ekranla senkron ● setInterval: drift / jitter Drift (yalnızca interval): ms
Recursive: request → tick → request Loop id: (cancel ile iptal)

İpucu: Jammer açıkken rAF de gecikir; “doğru seçim” kadar tick içini hafif tutmak da önemlidir.

Neden rAF?

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. rAF ise “ekran hazır olduğunda çağır” diyerek ritmi ekrana bağlar.

rAF mucize değildir: callback hâlâ main thread üstünde çalışır. Tick içinde ağır işler yaparsan rAF de gecikir ve stutter üretir.

Recursive (özyinelemeli) yapı

rAF kendiliğinden devam etmez: her tick’te bir sonraki kareyi tekrar istersin. Bu küçük fark kontrolü büyütür: pause/resume, modal açılınca render’ı durdurma, yanlışlıkla iki loop’u aynı anda çalıştırmama gibi.

rAF bir “id” döndürür. Bu id’yi saklayıp cancelAnimationFrame ile iptal edebilmek, çift render / çift update gibi maliyetli hataları engeller.

Zaman damgası: High‑res timestamp

rAF callback’i bir timestamp verir. İki kare arasındaki farkı alarak \(\\Delta t\\) hesaplar ve hareketi “frame sayısına” değil “gerçek zamana” bağlarsın. Yaygın hata: timestamp’i saniye sanmak — çoğu ortamda milisaniyedir; hızın birim/saniye ise \(\\Delta t\\)’yi saniyeye çevirmelisin.

Sekmeye gidip geri geldiğinde \(\\Delta t\\) büyüyebilir. Bu yüzden clamp veya fixed‑step gibi disiplinler devreye girer.

CPU–GPU senkronizasyonu: Frame bundle

rAF, DOM/CSS güncellemeleri ile WebGL çizim komutlarını aynı “kare paketi” içinde toplamaya çalışır. Bu sayede update→render ritmi tarayıcının çizim zamanı ile daha öngörülebilir şekilde hizalanır.

Akıcılık bozulduğunda çoğu zaman sorun rAF’in kendisi değil; tick içinde biriken iş yüküdür. Hedef, frame bütçesini yönetmektir.