requestAnimationFrame API - kini dengan presisi sub-milidetik

Ilmari Heikkinen

Jika menggunakan requestAnimationFrame, Anda senang melihat paint disinkronkan dengan kecepatan refresh layar, yang menghasilkan animasi paling berkualitas. Selain itu, Anda menghemat penggunaan daya baterai dan derau kipas CPU pengguna saat mereka beralih ke tab lain.

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

Untuk memperjelas, berikut ini yang saya bicarakan:

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

Jika Anda menggunakan shim requestAnimFrame umum yang disediakan di sini, berarti Anda tidak menggunakan nilai stempel waktu. Anda tidak perlu melakukan apa pun. :)

Mengapa

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

Perbandingan grafik 16 md vs 16 md bilangan bulat.

Seperti yang dapat Anda lihat di atas, panel biru menunjukkan jumlah waktu maksimum yang Anda miliki untuk melakukan semua pekerjaan sebelum menggambar frame baru (pada 60 fps). Anda mungkin melakukan lebih dari 16 hal, tetapi dengan integer milidetik, Anda hanya memiliki kemampuan untuk menjadwalkan dan mengukur dalam penambahan yang sangat tebal tersebut. Hal itu tidak cukup baik.

Pengatur Waktu 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 melewati standar lebih lanjut, metode ini akan menghapus awalan dan tersedia melalui performance.now().

Anda juga akan melihat dua nilai di atas memiliki banyak urutan besaran yang berbeda. performance.now() adalah pengukuran floating point milidetik sejak halaman tersebut mulai dimuat (performance.navigationStart untuk lebih spesifik).

Sedang digunakan

Masalah utama yang dipangkas 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 penerapan yang cukup naif. Penerapan ini tidak menggunakan metode now() berawalan 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 Anda dapatkan, Anda bisa 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 tangani. Secara teknis, nilai tersebut bisa positif palsu (PP) tetapi hanya jika halaman web dibuka secara terus-menerus selama 30 tahun. Namun, kita tidak dapat menguji apakah itu merupakan bilangan floating point (bukannya diturunkan ke bilangan bulat). Minta timer beresolusi tinggi yang cukup dan Anda terikat untuk 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.