Menuju metrik kelancaran animasi

Pelajari cara mengukur animasi, cara memikirkan frame animasi, dan kelancaran halaman secara keseluruhan.

Behdad Bakhshinategh
Behdad Bakhshinategh
Jonathan Ross
Jonathan Ross

Anda mungkin pernah mengalami halaman yang "tersekat" atau "membeku" saat men-scroll atau animasi. Kami ingin mengatakan bahwa pengalaman ini tidak lancar. Untuk mengatasi jenis masalah ini, tim Chrome telah berupaya menambahkan dukungan lebih lanjut ke alat lab kami untuk deteksi animasi, serta melakukan peningkatan yang stabil pada diagnostik pipeline rendering dalam Chromium.

Kami ingin membagikan beberapa progres terbaru, menawarkan panduan alat yang konkret, dan mendiskusikan ide untuk metrik kelancaran animasi di masa mendatang. Seperti biasa, kami menantikan masukan dari Anda.

Postingan ini akan membahas tiga topik utama:

  • Tampilan cepat animasi dan frame animasi.
  • Pemikiran kami saat ini tentang mengukur kehalusan animasi secara keseluruhan.
  • Beberapa saran praktis yang dapat Anda manfaatkan dalam alat lab saat ini.

Apa itu animasi?

Animasi membuat konten menjadi lebih hidup. Dengan membuat konten bergerak, terutama sebagai respons terhadap interaksi pengguna, animasi dapat membuat pengalaman terasa lebih alami, mudah dipahami, dan menyenangkan.

Namun, animasi yang diterapkan dengan buruk, atau hanya menambahkan terlalu banyak animasi, dapat menurunkan kualitas pengalaman dan membuatnya tidak menyenangkan sama sekali. Kita mungkin pernah berinteraksi dengan antarmuka yang menambahkan terlalu banyak efek transisi "berguna", yang justru merusak pengalaman saat performanya buruk. Oleh karena itu, beberapa pengguna mungkin benar-benar lebih memilih gerakan yang dikurangi, yaitu preferensi pengguna yang harus Anda penuhi.

Bagaimana cara kerja animasi?

Sebagai ringkasan singkat, pipeline rendering terdiri dari beberapa tahap berurutan:

  1. Gaya: Menghitung gaya yang diterapkan pada elemen.
  2. Tata letak: Buat geometri dan posisi untuk setiap elemen.
  3. Paint: Isi piksel untuk setiap elemen ke dalam lapisan.
  4. Komposit: Menggambar lapisan ke layar.

Meskipun ada banyak cara untuk menentukan animasi, semuanya pada dasarnya berfungsi melalui salah satu dari berikut ini:

  • Menyesuaikan properti tata letak.
  • Menyesuaikan properti cat.
  • Menyesuaikan properti komposit.

Karena tahap ini berurutan, penting untuk menentukan animasi dalam hal properti yang lebih jauh di pipeline. Makin awal update terjadi dalam proses, makin besar biayanya dan makin kecil kemungkinannya berjalan lancar. (Lihat Performa rendering untuk mengetahui detail selengkapnya.)

Meskipun properti tata letak animasi dapat memberikan kemudahan, ada biaya yang harus dikeluarkan untuk melakukannya, meskipun biaya tersebut tidak langsung terlihat. Animasi harus ditentukan dalam hal perubahan properti komposit jika memungkinkan.

Menentukan animasi CSS deklaratif atau menggunakan Web Animations, dan memastikan Anda menganimasikan properti komposit, adalah langkah pertama yang bagus untuk memastikan animasi yang lancar dan efisien. Namun, hal ini saja tidak menjamin kelancaran karena bahkan animasi web yang efisien pun memiliki batas performa. Itulah sebabnya pengukuran selalu penting.

Apa yang dimaksud dengan frame animasi?

Pembaruan pada representasi visual halaman memerlukan waktu untuk ditampilkan. Perubahan visual akan menghasilkan frame animasi baru, yang pada akhirnya dirender di layar pengguna.

Menampilkan update pada beberapa interval, sehingga update visual dikelompokkan. Banyak layar diperbarui pada interval waktu tetap, seperti 60 kali per detik (yaitu 60 Hz). Beberapa layar yang lebih modern dapat menawarkan kecepatan refresh yang lebih tinggi (90–120 Hz menjadi umum). Sering kali layar ini dapat secara aktif beradaptasi di antara kecepatan refresh sesuai kebutuhan, atau bahkan menawarkan kecepatan frame yang sepenuhnya bervariasi.

Tujuan untuk setiap aplikasi, seperti game atau browser, adalah memproses semua update visual yang dikelompokkan ini dan menghasilkan frame animasi yang lengkap secara visual dalam batas waktu, setiap saat. Perhatikan bahwa tujuan ini sepenuhnya berbeda dari tugas penting browser lainnya seperti memuat konten dari jaringan dengan cepat atau menjalankan tugas JavaScript secara efisien.

Pada titik tertentu, akan menjadi terlalu sulit untuk menyelesaikan semua pembaruan visual dalam batas waktu yang ditetapkan oleh layar. Jika hal ini terjadi, browser menjatuhkan frame. Layar Anda tidak menjadi hitam, hanya mengulang sendiri. Anda melihat update visual yang sama untuk waktu yang lebih lama—frame animasi yang sama yang ditampilkan pada peluang frame sebelumnya.

Hal ini sebenarnya sering terjadi. Bahkan, mungkin tidak terlihat, terutama untuk konten statis atau seperti dokumen, yang umum di platform web. Frame yang terputus hanya terlihat jelas saat ada update visual penting, seperti animasi, yang memerlukan aliran update animasi yang stabil untuk menampilkan gerakan yang lancar.

Apa yang memengaruhi frame animasi?

Developer web dapat sangat memengaruhi kemampuan browser untuk merender dan menampilkan update visual dengan cepat dan efisien.

Beberapa contohnya:

  • Menggunakan konten yang terlalu besar atau memerlukan banyak resource untuk didekode dengan cepat di perangkat target.
  • Menggunakan terlalu banyak lapisan yang memerlukan terlalu banyak memori GPU.
  • Menentukan gaya CSS atau animasi web yang terlalu rumit.
  • Menggunakan anti-pola desain yang menonaktifkan pengoptimalan rendering cepat.
  • Terlalu banyak pekerjaan JS di thread utama, yang menyebabkan tugas yang berjalan lama memblokir pembaruan visual.

Namun, bagaimana Anda dapat mengetahui kapan frame animasi melewati batas waktunya dan menyebabkan frame terputus?

Salah satu metode yang mungkin adalah menggunakan polling requestAnimationFrame(), tetapi memiliki beberapa kekurangan. requestAnimationFrame(), atau "rAF", memberi tahu browser bahwa Anda ingin melakukan animasi dan meminta peluang untuk melakukannya sebelum tahap paint berikutnya dari pipeline rendering. Jika fungsi callback Anda tidak dipanggil pada waktu yang Anda harapkan, berarti rendering tidak dieksekusi, dan satu atau beberapa frame dilewati. Dengan melakukan polling dan menghitung seberapa sering rAF dipanggil, Anda dapat menghitung semacam metrik "frame per detik" (FPS).

let frameTimes = [];
function pollFramesPerSecond(now) {
  frameTimes = [...frameTimes.filter(t => t > now - 1000), now];
  requestAnimationFrame(pollFramesPerSecond);
  console.log('Frames per second:', frameTimes.length);
}
requestAnimationFrame(pollFramesPerSecond);

Menggunakan polling requestAnimationFrame() tidak disarankan karena beberapa alasan:

  • Setiap skrip harus menyiapkan loop polling-nya sendiri.
  • Hal ini dapat memblokir jalur kritis.
  • Meskipun polling rAF cepat, polling ini dapat mencegah requestIdleCallback() dapat menjadwalkan blok diam yang panjang saat digunakan secara terus-menerus (blok yang melebihi satu frame).
  • Demikian pula, kurangnya pemblokiran lama saat tidak ada aktivitas mencegah browser menjadwalkan tugas lain yang berjalan lama (seperti pengumpulan sampah yang lebih lama dan tugas latar belakang atau spekulatif lainnya).
  • Jika polling diaktifkan dan dinonaktifkan, Anda akan melewatkan kasus saat anggaran frame telah terlampaui.
  • Polling akan melaporkan positif palsu jika browser menggunakan frekuensi update variabel (misalnya, karena status daya atau visibilitas).
  • Yang terpenting, sebenarnya tidak mencatat semua jenis pembaruan animasi.

Terlalu banyak pekerjaan di thread utama dapat memengaruhi kemampuan untuk melihat frame animasi. Lihat Jank Sample untuk melihat bagaimana animasi yang didorong rAF, jika ada terlalu banyak pekerjaan di thread utama (seperti tata letak), akan menyebabkan frame yang terputus dan lebih sedikit callback rAF, serta FPS yang lebih rendah.

Saat thread utama menjadi lambat, update visual mulai tersendat-sendat. Itu jank!

Banyak alat pengukuran telah berfokus secara ekstensif pada kemampuan thread utama untuk menghasilkan tepat waktu, dan agar frame animasi berjalan lancar. Namun, ini bukan keseluruhan ceritanya. Perhatikan contoh berikut:

Video di atas menunjukkan halaman yang secara berkala menyisipkan tugas panjang ke thread utama. Tugas panjang ini benar-benar merusak kemampuan halaman untuk memberikan jenis pembaruan visual tertentu, dan Anda dapat melihat di sudut kiri atas penurunan FPS yang dilaporkan yang sesuai menjadi 0.requestAnimationFrame()

Namun, meskipun tugasnya panjang, halaman tetap di-scroll dengan lancar. Hal ini karena di browser modern, scrolling sering kali di-thread, sepenuhnya didorong oleh compositor.

Berikut adalah contoh yang secara bersamaan berisi banyak frame yang terputus di thread utama, tetapi masih memiliki banyak frame yang berhasil dikirimkan saat men-scroll di thread compositor. Setelah tugas yang panjang selesai, pembaruan gambar thread utama tidak memiliki perubahan visual untuk ditawarkan. Polling rAF menunjukkan penurunan frame menjadi 0, tetapi secara visual, pengguna tidak akan dapat melihat perbedaannya.

Untuk frame animasi, ceritanya tidak sesederhana itu.

Frame animasi: Pembaruan yang penting

Contoh di atas menunjukkan bahwa ada lebih banyak hal dalam cerita ini selain requestAnimationFrame().

Jadi, kapan pembaruan animasi dan frame animasi menjadi penting? Berikut beberapa kriteria yang kami pertimbangkan dan kami ingin mendapatkan masukan terkait hal ini:

  • Pembaruan thread utama dan compositor
  • Update cat tidak ada
  • Mendeteksi animasi
  • Kualitas versus kuantitas

Pembaruan thread utama dan compositor

Pembaruan frame animasi bukan boolean. Frame tidak hanya dapat dihilangkan sepenuhnya atau ditampilkan sepenuhnya. Ada banyak alasan mengapa frame animasi mungkin ditampilkan sebagian. Dengan kata lain, secara bersamaan, halaman tersebut dapat memiliki beberapa konten yang sudah tidak relevan sekaligus memiliki beberapa pembaruan visual baru yang ditampilkan.

Contoh paling umum dari hal ini adalah saat browser tidak dapat menghasilkan update thread utama baru dalam batas waktu frame, tetapi memiliki update thread compositor baru (seperti contoh scrolling ber-thread dari sebelumnya).

Salah satu alasan penting mengapa penggunaan animasi deklaratif untuk menganimasikan properti komposit direkomendasikan adalah karena dengan melakukannya, animasi dapat sepenuhnya didorong oleh thread compositor meskipun thread utama sedang sibuk. Jenis animasi ini dapat terus menghasilkan update visual secara efisien dan paralel.

Di sisi lain, mungkin ada kasus saat update thread utama akhirnya tersedia untuk presentasi, tetapi hanya setelah melewatkan beberapa batas waktu frame. Di sini, browser akan memiliki beberapa update baru, tetapi mungkin bukan yang terbaru.

Secara umum, kami menganggap frame yang berisi beberapa pembaruan visual baru, tetapi tidak semua pembaruan visual baru, sebagai frame parsial. Frame parsial cukup umum. Idealnya, update parsial setidaknya akan mencakup update visual yang paling penting, seperti animasi, tetapi hal itu hanya dapat terjadi jika animasi didorong oleh thread pembuat komposit.

Update cat tidak ada

Jenis update parsial lainnya adalah saat media seperti gambar belum selesai mendekode dan merasterisasi tepat waktu untuk presentasi frame.

Atau, meskipun halaman benar-benar statis, browser mungkin masih tertinggal dalam merender pembaruan visual selama scrolling cepat. Hal ini karena tampilan piksel konten di luar area tampilan yang terlihat dapat dihapus untuk menghemat memori GPU. Merender piksel membutuhkan waktu, dan mungkin membutuhkan waktu lebih lama dari satu frame untuk merender semuanya setelah scroll besar, seperti gerakan menggeser jari. Hal ini biasanya dikenal sebagai pola papan catur.

Dengan setiap peluang rendering frame, Anda dapat melacak seberapa banyak pembaruan visual terbaru yang benar-benar ditampilkan di layar. Mengukur kemampuan untuk melakukannya di banyak frame (atau waktu) secara luas dikenal sebagai throughput frame.

Jika GPU benar-benar terbebani, browser (atau platform) bahkan dapat mulai membatasi kecepatan upaya update visual dan dengan demikian mengurangi kecepatan frame efektif. Meskipun secara teknis dapat mengurangi jumlah update frame yang terputus, secara visual, hal ini akan tetap muncul sebagai throughput frame yang lebih rendah.

Namun, tidak semua jenis throughput frame rendah itu buruk. Jika halaman sebagian besar tidak aktif dan tidak ada animasi yang aktif, kecepatan frame rendah sama menariknya secara visual dengan kecepatan frame tinggi (dan, dapat menghemat baterai!).

Jadi, kapan throughput frame menjadi penting?

Mendeteksi animasi

Throughput frame tinggi sangat penting terutama selama periode dengan animasi penting. Jenis animasi yang berbeda akan bergantung pada pembaruan visual dari thread tertentu (utama, compositor, atau pekerja), sehingga pembaruan visualnya bergantung pada thread tersebut yang memberikan pembaruan dalam batas waktu. Kami mengatakan bahwa rangkaian pesan tertentu memengaruhi kelancaran setiap kali ada animasi aktif yang bergantung pada pembaruan rangkaian pesan tersebut.

Beberapa jenis animasi lebih mudah ditentukan dan dideteksi daripada yang lain. Animasi deklaratif, atau animasi yang didorong input pengguna, lebih jelas untuk ditentukan daripada animasi yang didorong JavaScript yang diterapkan sebagai pembaruan berkala pada properti gaya yang dapat dianimasikan.

Meskipun dengan requestAnimationFrame(), Anda tidak selalu dapat mengasumsikan bahwa setiap panggilan rAF pasti menghasilkan pembaruan atau animasi visual. Misalnya, menggunakan polling rAF hanya untuk melacak kecepatan frame (seperti yang ditunjukkan di atas) tidak akan memengaruhi pengukuran kelancaran karena tidak ada pembaruan visual.

Kualitas versus kuantitas

Terakhir, mendeteksi animasi dan update frame animasi masih hanya bagian dari cerita karena hanya merekam kuantitas update animasi, bukan kualitasnya.

Misalnya, Anda dapat melihat framerate stabil 60 fps saat menonton video. Secara teknis, video ini sangat lancar, tetapi video itu sendiri mungkin memiliki bit rate rendah, atau masalah dengan buffering jaringan. Hal ini tidak tercakup secara langsung oleh metrik kelancaran animasi, tetapi masih dapat mengganggu pengguna.

Atau, game yang memanfaatkan <canvas> (bahkan mungkin menggunakan teknik seperti offscreen canvas untuk memastikan frame rate yang stabil) secara teknis mungkin sangat lancar dalam hal frame animasi, tetapi gagal memuat aset game berkualitas tinggi ke dalam adegan atau menampilkan artefak rendering.

Dan tentu saja, situs dapat memiliki beberapa animasi yang sangat buruk 🙂

GIF sekolah lama yang sedang dibangun

Maksudku, mungkin mereka cukup keren pada masanya.

Status satu frame animasi

Karena frame dapat ditampilkan sebagian, atau frame yang terputus dapat terjadi dengan cara yang tidak memengaruhi kelancaran, kami mulai menganggap setiap frame memiliki skor kelengkapan atau kelancaran.

Berikut adalah spektrum cara kita menafsirkan status satu frame animasi, yang diurutkan dari kasus terbaik hingga terburuk:

Tidak Ada Pembaruan yang Diinginkan Waktu tidak ada aktivitas, pengulangan frame sebelumnya.
Disajikan sepenuhnya Pembaruan thread utama dilakukan dalam batas waktu, atau tidak ada pembaruan thread utama yang diinginkan.
Ditampilkan sebagian Hanya compositor; pembaruan thread utama yang tertunda tidak memiliki perubahan visual.
Ditampilkan sebagian Hanya compositor; thread utama memiliki pembaruan visual, tetapi pembaruan tersebut tidak menyertakan animasi yang memengaruhi kelancaran.
Ditampilkan sebagian Hanya compositor; thread utama memiliki update visual yang memengaruhi kelancaran, tetapi frame yang sebelumnya tidak aktif tiba dan digunakan sebagai gantinya.
Ditampilkan sebagian Hanya compositor; tanpa update utama yang diinginkan, dan update compositor memiliki animasi yang memengaruhi kelancaran.
Ditampilkan sebagian Hanya compositor, tetapi update compositor tidak memiliki animasi yang memengaruhi kelancaran.
Frame yang dilepas Tidak ada update. Tidak ada update compositor yang diinginkan, dan main tertunda.
Frame yang dilepas Update compositor diinginkan, tetapi tertunda.
Frame usang Update diinginkan, dihasilkan oleh perender, tetapi GPU masih belum menampilkannya sebelum batas waktu sinkronisasi vertikal.

Anda dapat mengubah status ini menjadi semacam skor. Dan mungkin salah satu cara untuk menafsirkan skor ini adalah dengan menganggapnya sebagai probabilitas dapat diamati oleh pengguna. Satu frame yang dihapus mungkin tidak terlalu terlihat, tetapi urutan banyak frame yang dihapus yang memengaruhi kelancaran secara berturut-turut pasti terlihat.

Menyatukan semuanya: Metrik Persentase Frame yang Terjatuh

Meskipun terkadang perlu untuk mempelajari secara mendalam status setiap frame animasi, akan lebih baik jika Anda hanya menetapkan skor "sekilas" untuk suatu pengalaman.

Karena frame mungkin ditampilkan sebagian, dan karena bahkan update frame yang dilewati sepenuhnya mungkin tidak memengaruhi kelancaran, kami ingin lebih berfokus pada tingkat ketidakmampuan browser untuk memberikan update lengkap secara visual saat hal itu penting.

Model mental harus beralih dari:

  1. Frame Per Detik, ke
  2. Mendeteksi update animasi yang hilang dan penting, untuk
  3. Persentase Penurunan selama jangka waktu tertentu.

Yang penting adalah: proporsi waktu menunggu update penting. Kami rasa hal ini sesuai dengan cara alami pengguna merasakan kelancaran konten web dalam praktiknya. Sejauh ini, kita telah menggunakan metrik berikut sebagai kumpulan metrik awal:

  • Rata-Rata Persen Frame yang Dihilangkan: untuk semua frame animasi non-idle di seluruh linimasa
  • Kasus Terburuk Persentase Frame yang Dihilangkan: sebagaimana diukur selama 1 detik sliding windows of time.
  • Persentil ke-95 Persentase Frame yang Dihapus: sebagaimana diukur selama 1 detik periode waktu geser.

Anda dapat menemukan skor ini di beberapa alat developer Chrome saat ini. Meskipun metrik ini hanya berfokus pada throughput frame keseluruhan, kami juga mengevaluasi faktor lain, seperti latensi frame.

Cobalah sendiri di alat developer.

HUD Performa

Chromium memiliki HUD Performa yang tersembunyi di balik tanda (chrome://flags/#show-performance-metrics-hud). Di dalamnya, Anda dapat menemukan skor langsung untuk hal-hal seperti Data Web Inti dan juga beberapa definisi eksperimental untuk kelancaran animasi berdasarkan Persentase Frame yang Terjatuh dari waktu ke waktu.

HUD Performa

Statistik Rendering Frame

Aktifkan "Statistik Rendering Frame" di DevTools melalui setelan Rendering untuk melihat tampilan langsung frame animasi baru, yang diberi kode warna untuk membedakan update parsial dari update frame yang sepenuhnya dihilangkan. FPS yang dilaporkan hanya untuk frame yang ditampilkan sepenuhnya.

Statistik rendering frame

Frame Viewer dalam rekaman profil performa DevTools

Panel Performa DevTools telah lama memiliki penampil Frame. Namun, hal ini menjadi sedikit tidak sinkron dengan cara kerja pipeline rendering modern. Ada banyak peningkatan baru-baru ini, bahkan di Chrome Canary terbaru, yang menurut kami akan sangat memudahkan proses men-debug masalah animasi.

Sekarang Anda akan melihat bahwa frame di penampil frame lebih selaras dengan batas vsync, dan diberi kode warna berdasarkan status. Belum ada visualisasi lengkap untuk semua nuansa yang diuraikan di atas, tetapi kami berencana untuk menambahkannya dalam waktu dekat.

Penampil frame di Chrome DevTools

Pelacakan Chrome

Terakhir, dengan Chrome Tracing, alat pilihan untuk mempelajari detail secara mendalam, Anda dapat merekam rekaman aktivitas "Rendering konten web" melalui UI Perfetto baru (atau about:tracing), dan mempelajari secara mendalam pipeline grafis Chrome. Tugas ini bisa jadi sulit, tetapi ada beberapa hal yang baru-baru ini ditambahkan ke Chromium untuk mempermudahnya. Anda dapat melihat ringkasan tentang apa yang tersedia dalam dokumen Siklus Proses Frame.

Melalui peristiwa rekaman aktivitas, Anda dapat menentukan secara pasti:

  • Animasi mana yang sedang berjalan (menggunakan peristiwa bernama TrackerValidation).
  • Mendapatkan linimasa persis frame animasi (menggunakan peristiwa bernama PipelineReporter).
  • Untuk update animasi yang tersendat, cari tahu persis apa yang menghalangi animasi Anda berjalan lebih cepat (menggunakan perincian peristiwa dalam peristiwa PipelineReporter).
  • Untuk animasi yang didorong input, lihat berapa lama waktu yang diperlukan untuk mendapatkan update visual (menggunakan peristiwa bernama EventLatency).

Pelapor pipeline Chrome Tracing

Langkah berikutnya

Inisiatif Data Web Inti bertujuan untuk memberikan metrik dan panduan dalam membangun pengalaman pengguna yang luar biasa di web. Metrik berbasis lab seperti Total Waktu Pemblokiran (TBT) sangat penting untuk mendeteksi dan mendiagnosis potensi masalah interaktivitas. Kami berencana untuk mendesain metrik berbasis lab serupa untuk kelancaran animasi.

Kami akan terus memberi tahu Anda saat kami terus mematangkan ide untuk mendesain metrik komprehensif berdasarkan data frame animasi individual.

Pada masa mendatang, kami juga ingin mendesain API yang memungkinkan pengukuran kelancaran animasi secara berperforma untuk pengguna nyata di lapangan dan di lab. Nantikan info terbarunya di sana juga.

Masukan

Kami sangat antusias dengan semua peningkatan dan alat developer terbaru yang telah diluncurkan di Chrome untuk mengukur kelancaran animasi. Coba alat ini, lakukan tolok ukur animasi Anda, dan beri tahu kami hasilnya.

Anda dapat mengirim komentar ke grup Google web-vitals-feedback dengan "[Smoothness Metrics]" di baris subjek. Kami sangat menantikan pendapat Anda.