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

Ilmari Heikkinen

requestAnimationFrame kullanıyorsanız boyalarınızın ekranın yenileme hızıyla senkronize edildiğini ve mümkün olan en yüksek kaliteli animasyonların elde edildiğini görmekten keyif aldınız. Ayrıca, kullanıcılarınızın başka bir sekmeye geçiş yaptıklarında CPU fan gürültüsünden ve pil gücünden de tasarruf etmiş olursunuz.

Bununla birlikte, API'nin belli bir bölümünde bir değişiklik olacak. Geri çağırma işlevinize iletilen Zaman Damgası, sayfa açıldıktan sonra Date.now() benzeri tipik bir zaman damgasından, kayan nokta milisaniyelik yüksek çözünürlüklü ölçüme dönüşüyor. Bu değeri kullanırsanız kodunuzu aşağıdaki açıklamaya göre güncellemeniz gerekir.

Açıkça belirtmek gerekirse, şunun hakkında konuşmak istiyorum:

// 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ğeri kullanmıyorsunuz demektir. İlginizi çekmiyor. :)

Neden

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

16 ms ile 16 tamsayı ms'lik grafik karşılaştırması.

Yukarıda görebileceğiniz gibi, mavi çubuk yeni bir kare boyamadan önce (60 fps'de) tüm işlerinizi yapmanız için gereken maksimum süreyi temsil eder. Muhtemelen 16'dan fazla şey yapıyorsunuz, ancak milisaniyelerle tam sayılarla yalnızca bu çok karmaşık artışlarda planlama ve ölçüm yapabilirsiniz. Yeterince iyi değil.

Yüksek Çözünürlüklü Zamanlayıcı çok daha kesin bir değer 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ılabilmektedir ve bu değer genellikle rAF geri çağırmasına iletilen yeni bağımsız değişken değerine eşittir. Spesifikasyon, standartlarda daha ileri aşamalarda ilerlediğinde, yöntem ön eki kaldırır ve performance.now() üzerinden kullanılabilir.

Ayrıca, yukarıdaki iki değerin büyük ölçüde farklı olduğunu da fark edeceksiniz. performance.now(), söz konusu sayfanın yüklenmeye başlamasından bu yana geçen milisaniye cinsinden kayan nokta ölçümüdür (performance.navigationStart daha spesifik olarak).

Kullanılıyor

Kırpma işlemleriyle ilgili temel sorun, bu tasarım modelini kullanan animasyon kitaplıklarıdır:

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));
}

Bu sorunu düzeltmek için düzenleme yapmak oldukça kolaydır... window.performance.now() kullanmak için startTime ve now boyutlarını artırın.

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

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

Özellik algılama

Yukarıdaki kalıbı kullanmıyor ve yalnızca ne tür bir geri çağırma değeri elde ettiğinizi belirlemek istiyorsanız bu tekniği kullanabilirsiniz:

requestAnimationFrame(function(timestamp){

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

    // ...

if (timestamp < 1e12) kontrol etmek, ne kadar büyük bir sorunla uğraştığımızı görmemizi sağlayan basit bir testtir. Teknik olarak bu, yalnızca bir web sayfası 30 yıl boyunca sürekli açık olduğunda yanlış pozitif olabilir. Ancak bunun bir kayan nokta sayısı olup olmadığını (bir tam sayı olacak şekilde yuvarlanmak yerine) test edemeyiz. Yeterince yüksek çözünürlüklü zamanlayıcılar talep ederseniz bir noktada tam sayı değerleri alırsınız.

Bu değişikliği Chrome 21'de uygulamayı planlıyoruz. Bu geri çağırma parametresinden zaten yararlanıyorsanız kodunuzu güncellediğinizden emin olun.