requestAnimationFrame API - kini dengan presisi sub-milidetik

Ilmari Heikkinen

Jika telah menggunakan requestAnimationFrame, Anda akan senang melihat cat disinkronkan dengan kecepatan refresh layar, sehingga menghasilkan animasi dengan fidelitas setinggi mungkin. Selain itu, Anda menghemat kebisingan dan daya baterai kipas CPU pengguna saat mereka beralih ke tab lain.

Namun, akan ada perubahan pada bagian API. Stempel waktu yang diteruskan ke fungsi callback Anda berubah dari stempel waktu seperti Date.now() standar menjadi pengukuran milidetik floating point beresolusi tinggi sejak halaman dibuka. Jika menggunakan nilai ini, Anda harus mengupdate kode, berdasarkan penjelasan di bawah.

Untuk lebih jelasnya, ini yang saya bicarakan:

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

Jika menggunakan shim requestAnimFrame umum yang disediakan di sini, Anda tidak menggunakan nilai stempel waktu. Anda siap. :)

Mengapa

Mengapa? Nah rAF membantu Anda mendapatkan 60 fps terbaik yang ideal, dan 60 fps diterjemahkan menjadi 16,7 md per frame. Namun, pengukuran dengan milidetik bilangan bulat berarti kita memiliki presisi 1/16 untuk semua hal yang ingin diamati dan ditargetkan.

Perbandingan grafik 16 md vs 16 integer ms.

Seperti yang dapat Anda lihat di atas, bilah biru mewakili jumlah waktu maksimum yang Anda miliki untuk melakukan semua pekerjaan sebelum menggambar bingkai baru (pada 60 fps). Anda mungkin melakukan lebih dari 16 hal, tetapi dengan milidetik bilangan bulat, Anda hanya dapat menjadwalkan dan mengukur dalam penambahan yang sangat besar. Itu tidak cukup baik.

Timer Resolusi Tinggi mengatasi hal ini dengan memberikan angka yang jauh lebih akurat:

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

Timer resolusi tinggi saat ini tersedia di Chrome sebagai window.performance.webkitNow(), dan nilai ini umumnya sama dengan nilai argumen baru yang diteruskan ke callback rAF. Setelah spesifikasi berkembang lebih lanjut melalui standar, metode akan menghapus awalan dan tersedia melalui performance.now().

Anda juga akan melihat bahwa kedua nilai di atas memiliki perbedaan yang sangat besar. performance.now() adalah pengukuran milidetik floating point sejak halaman tertentu mulai dimuat (performance.navigationStart secara spesifik).

Sedang digunakan

Masalah utama yang muncul adalah library animasi yang menggunakan pola desain ini:

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

Pengeditan untuk memperbaikinya cukup mudah... tambahkan startTime dan now untuk menggunakan window.performance.now().

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

Ini adalah implementasi yang cukup naif, tidak menggunakan metode now() dengan awalan dan juga mengasumsikan dukungan Date.now(), yang tidak ada di IE8.

Deteksi fitur

Jika Anda tidak menggunakan pola di atas dan hanya ingin mengidentifikasi jenis nilai callback yang didapatkan, Anda dapat menggunakan teknik ini:

requestAnimationFrame(function(timestamp){

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

    // ...

Memeriksa if (timestamp < 1e12) adalah pengujian cepat untuk melihat seberapa besar angka yang kita hadapi. Secara teknis, hal ini dapat menjadi positif palsu, tetapi hanya jika halaman web dibuka terus-menerus selama 30 tahun. Namun, kita tidak dapat menguji apakah nilai tersebut merupakan bilangan floating point (bukan bilangan bulat). Minta timer resolusi tinggi yang cukup dan Anda pasti akan mendapatkan nilai bilangan bulat pada suatu saat.

Kami berencana menerapkan perubahan ini di Chrome 21, jadi jika Anda sudah memanfaatkan parameter callback ini, pastikan untuk memperbarui kode Anda.