requestAnimationFrame API - artık bir milisaniyeden kısa hassasiyetle

Ilmari Heikkinen

requestAnimationFrame kullanıyorsanız boyamalarınızın ekranın yenileme hızıyla senkronize edildiğini ve bu sayede mümkün olan en yüksek kalitede animasyonlar elde edildiğini görmüşsünüzdür. Ayrıca, kullanıcılarınız başka bir sekmeye geçtiğinde CPU fan gürültüsünden ve pil gücünden tasarruf edersiniz.

Ancak API'nin bir bölümünde değişiklik yapılacak. Geri çağırma işlevinize iletilen zaman damgası, tipik bir Date.now() benzeri zaman damgasından, sayfanın açılmasından bu yana geçen kayan nokta milisaniyelerinin yüksek çözünürlüklü bir ölçümüne dönüşüyor. Bu değeri kullanırsanız kodunuzu aşağıdaki açıklamaya göre güncellemeniz gerekir.

Daha net olmak gerekirse şundan bahsediyorum:

// assuming requestAnimationFrame method has been normalized for all vendor prefixes..
requestAnimationFrame(function(timestamp){
    // the value of timestamp is changing
});

Burada sağlanan ortak requestAnimFrame dolgusunu kullanıyorsanız zaman damgası değerini kullanmıyorsunuz demektir. Sorumluluktan kurtuldunuz. :)

Neden?

Neden? rAF, ideal olan 60 fps'yi elde etmenize yardımcı olur.60 fps, kare başına 16,7 ms anlamına gelir. Ancak milisaniye cinsinden tam sayılarla ölçüm yapmak, gözlemlemek ve hedeflemek istediğimiz her şey için 1/16 hassasiyetimiz olduğu anlamına gelir.

16 ms ile 16 tam sayı ms grafik karşılaştırması.

Yukarıda gördüğünüz gibi mavi çubuk, yeni bir kareyi boyamadan önce (60 fps'de) tüm işlerinizi yapmanız gereken maksimum süreyi temsil eder. Muhtemelen 16'dan fazla işlem yapıyorsunuzdur ancak tam sayı milisaniyelerle yalnızca bu çok büyük artışlarla planlama ve ölçüm yapabilirsiniz. Bu yeterli değil.

Yüksek Çözünürlüklü Zamanlayıcı, çok daha hassas bir rakam sağlayarak bu sorunu çözer:

Date.now()         //  1337376068250
performance.now()  //  20303.427000007

Yüksek çözünürlüklü zamanlayıcı şu anda Chrome'da window.performance.webkitNow() olarak kullanılabilir ve bu değer genellikle rAF geri çağırmasına iletilen yeni bağımsız değişken değerine eşittir. Spesifikasyon, standartlar açısından daha fazla ilerleme kaydettiğinde yöntem, ön eki bırakır ve performance.now() üzerinden kullanılabilir.

Yukarıdaki iki değerin birbirinden çok farklı olduğunu da fark edeceksiniz. performance.now(), söz konusu sayfanın yüklenmeye başlamasından (özellikle performance.navigationStart) itibaren geçen kayan nokta milisaniye cinsinden bir ölçümdür.

Kullanımda

Bu tasarım kalıbını kullanan animasyon kitaplıklarında ortaya çıkan temel sorun şudur:

function MyAnimation(duration) {
    this.startTime = Date.now();
    this.duration = duration;
    requestAnimFrame(this.tick.bind(this));
}
MyAnimation.prototype.tick = function(time) {
    var now = Date.now();
    if (time > now) {
        this.dispatchEvent("ended");
        return;
    }
    ...
    requestAnimFrame(this.tick.bind(this));
}

Bunu düzeltmek için yapılacak düzenleme oldukça kolaydır: window.performance.now() kullanmak için startTime ve now öğelerini genişletin.

this.startTime = window.performance.now ?
                    (performance.now() + performance.timing.navigationStart) :
                    Date.now();

Bu oldukça basit bir uygulamadır. Önekli bir now() yöntemi kullanmaz ve IE8'de bulunmayan Date.now() desteğini varsayar.

Özellik algılama

Yukarıdaki kalıbı kullanmıyorsanız ve yalnızca ne tür bir geri çağırma değeri aldığınızı belirlemek istiyorsanız şu tekniği kullanabilirsiniz:

requestAnimationFrame(function(timestamp){

    if (timestamp < 1e12){
        // .. high resolution timer
    } else {
        // integer milliseconds since unix epoch
    }

    // ...

if (timestamp < 1e12) değerini kontrol etmek, ne kadar büyük bir sayıyla uğraştığımızı görmek için hızlı bir ördek testidir. Teknik olarak yanlış pozitif sonuç verebilir ancak bu yalnızca bir web sayfasının 30 yıl boyunca sürekli açık kalması durumunda gerçekleşir. Ancak bunun bir kayan nokta sayısı olup olmadığını (tam sayıya yuvarlanmak yerine) test edemiyoruz. Yeterli sayıda yüksek çözünürlüklü zamanlayıcı isterseniz bir noktada tam sayı değerleri alırsınız.

Bu değişikliği Chrome 21'de kullanıma sunmayı planlıyoruz. Bu nedenle, bu geri çağırma parametresinden yararlanıyorsanız kodunuzu güncellemeyi unutmayın.