Menuju metrik kelancaran animasi

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

Behdad Bakhshinategh
Behdad Bakhshinategh
Jonathan Ross
Jonathan Ross
Michal Mocny
Michal Mocny

Anda mungkin pernah melihat halaman yang "tersendat" atau "berhenti" saat men-scroll atau animasi. Kami ingin mengatakan bahwa pengalaman ini tidak lancar. Untuk mengatasi jenis masalah ini, tim Chrome telah berupaya menambahkan lebih banyak dukungan 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 konkret, dan mendiskusikan ide untuk metrik kelancaran animasi pada masa mendatang. Seperti biasa, kami ingin mendengar masukan Anda.

Postingan ini akan membahas tiga topik utama:

  • Sekilas tentang animasi dan frame animasi.
  • Pemikiran kita saat ini tentang mengukur kelancaran animasi secara keseluruhan.
  • Beberapa saran praktis untuk dimanfaatkan dalam alat lab saat ini.

Apa itu animasi?

Animasi membuat konten menjadi 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 terlalu banyak animasi, dapat menurunkan pengalaman pengguna dan membuatnya benar-benar tidak menyenangkan. Kita mungkin semua berinteraksi dengan antarmuka yang hanya menambahkan terlalu banyak efek transisi "bermanfaat", yang sebenarnya berbahaya jika performanya buruk. Oleh karena itu, beberapa pengguna sebenarnya mungkin lebih menyukai gerakan yang dikurangi, preferensi pengguna yang harus Anda patuhi.

Bagaimana cara kerja animasi?

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

  1. Gaya: Menghitung gaya yang berlaku untuk elemen.
  2. Tata letak: Membuat geometri dan posisi untuk setiap elemen.
  3. Paint: Mengisi piksel untuk setiap elemen ke dalam lapisan.
  4. Gabungan: Menggambar lapisan ke layar.

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

  • Menyesuaikan properti tata letak.
  • Menyesuaikan properti paint.
  • Menyesuaikan properti komposit.

Karena tahapan ini berurutan, penting untuk menentukan animasi berdasarkan properti yang berada lebih lanjut dalam pipeline. Semakin awal update terjadi dalam proses, semakin besar biayanya dan kemungkinan tidak akan lancar. (Lihat Performa rendering untuk detail selengkapnya.)

Meskipun mungkin mudah untuk menganimasikan properti tata letak, ada biaya untuk melakukannya, meskipun biaya tersebut tidak segera terlihat. Animasi harus didefinisikan dalam hal perubahan properti gabungan jika memungkinkan.

Menentukan animasi CSS deklaratif atau menggunakan Animasi Web, dan memastikan Anda menganimasikan properti komposit, adalah langkah pertama yang tepat untuk memastikan animasi yang mulus dan efisien. Namun, hal ini saja tidak menjamin kelancaran karena animasi web yang efisien pun memiliki batas performa. Itulah mengapa penting untuk selalu mengukur.

Apa itu bingkai animasi?

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

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

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

Pada titik tertentu, menyelesaikan semua pembaruan visual dalam batas waktu yang ditentukan oleh layar dapat menjadi terlalu sulit. Jika hal ini terjadi, browser akan menghapus frame. Layar Anda tidak menjadi hitam, hanya berulang dengan sendirinya. Anda akan melihat pembaruan visual yang sama untuk waktu yang lebih lama—frame animasi yang sama dengan yang ditampilkan pada peluang frame sebelumnya.

Hal ini sebenarnya sering terjadi. Ini bahkan belum tentu terlihat, terutama untuk konten statis atau seperti dokumen, yang umumnya di platform web pada khususnya. Frame yang drop hanya terlihat saat ada update visual penting, seperti animasi, yang mengharuskan kita memerlukan aliran update animasi yang stabil untuk menampilkan gerakan yang halus.

Apa yang memengaruhi bingkai animasi?

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

Beberapa contoh:

  • Menggunakan konten yang terlalu besar atau memerlukan banyak resource untuk mendekode dengan cepat di perangkat target.
  • Penggunaan terlalu banyak lapisan memerlukan terlalu banyak memori GPU.
  • Mendefinisikan gaya CSS atau animasi web yang terlalu kompleks.
  • Menggunakan anti-pola desain yang menonaktifkan pengoptimalan rendering cepat.
  • Terlalu banyak pekerjaan JS di thread utama, sehingga menyebabkan tugas panjang yang memblokir update visual.

Namun, bagaimana cara mengetahui kapan frame animasi telah melewati batas waktunya dan menyebabkan penurunan frame?

Salah satu metode yang memungkinkan adalah menggunakan polling requestAnimationFrame(), tetapi metode ini memiliki beberapa kelemahan. requestAnimationFrame(), atau "rAF", memberi tahu browser bahwa Anda ingin menjalankan animasi dan meminta kesempatan untuk melakukannya sebelum tahap penggambaran berikutnya dari pipeline rendering. Jika fungsi callback tidak dipanggil pada saat yang Anda harapkan, artinya cat 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() bukan merupakan ide bagus karena beberapa alasan:

  • Setiap skrip harus menyiapkan loop pollingnya sendiri.
  • Dapat memblokir jalur kritis.
  • Meskipun polling rAF berlangsung cepat, polling ini dapat mencegah requestIdleCallback() untuk menjadwalkan blok tidak ada aktivitas yang panjang saat digunakan secara terus-menerus (blok yang melebihi satu frame).
  • Demikian pula, kurangnya blok tidak ada aktivitas yang panjang mencegah browser menjadwalkan tugas yang berjalan lama lainnya (seperti pembersihan sampah memori yang lebih lama dan pekerjaan latar belakang atau spekulatif lainnya).
  • Jika polling diaktifkan dan dinonaktifkan, Anda akan melewatkan kasus ketika anggaran frame telah terlampaui.
  • Polling akan melaporkan positif palsu jika browser menggunakan frekuensi update variabel (misalnya, karena daya atau status visibilitas).
  • Dan yang paling penting, diagram ini tidak benar-benar menangkap semua jenis pembaruan animasi.

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

Saat thread utama terhenti, update visual akan mulai tersendat. Itu jank!

Banyak alat pengukuran yang sangat berfokus pada kemampuan thread utama untuk menghasilkan secara tepat waktu, dan agar frame animasi berjalan dengan lancar. Tapi ini bukanlah keseluruhan ceritanya! Perhatikan contoh berikut:

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

Namun, terlepas dari tugas yang panjang ini, halaman terus dapat di-scroll dengan lancar. Hal ini karena pada browser modern, scrolling sering kali di-thread, sepenuhnya digerakkan oleh compositor.

Ini adalah contoh yang secara bersamaan berisi banyak frame yang dilepas di thread utama, tetapi masih memiliki banyak frame scroll yang berhasil dikirim di thread compositor. Setelah tugas panjang selesai, update cat thread utama tidak memiliki perubahan visual yang dapat ditawarkan. Polling rAF menyarankan penurunan frame ke 0, tetapi secara visual, pengguna tidak akan dapat melihat perbedaannya.

Untuk bingkai animasi, ceritanya tidak sesederhana itu.

Frame animasi: Pembaruan yang penting

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

Jadi, kapan pembaruan animasi dan bingkai animasi itu penting? Berikut adalah beberapa kriteria yang kami pikirkan dan kami ingin mendapatkan masukan:

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

Update thread utama dan compositor

Pembaruan bingkai animasi tidak boleh berupa boolean. Frame tidak hanya dapat diturunkan sepenuhnya atau ditampilkan sepenuhnya. Ada banyak alasan mengapa frame animasi dapat sebagian ditampilkan. Dengan kata lain, aplikasi dapat memiliki beberapa konten tidak berlaku secara bersamaan sekaligus mengalami beberapa update visual baru yang ditampilkan.

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

Salah satu alasan penting penggunaan animasi deklaratif untuk menganimasikan properti komposit direkomendasikan adalah karena hal itu memungkinkan animasi didorong sepenuhnya oleh thread compositor meskipun thread utama sedang sibuk. Jenis animasi ini dapat terus menghasilkan pembaruan visual secara efisien dan paralel.

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

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

Update paint tidak ada

Jenis update parsial lainnya adalah ketika media seperti gambar belum menyelesaikan dekode dan rasterisasi pada waktu untuk presentasi frame.

Atau, meskipun halaman sangat statis, browser mungkin masih tertinggal dalam proses rendering pembaruan visual selama scroll cepat. Hal ini dikarenakan rendisi piksel konten di luar area tampilan yang terlihat dapat dihapus untuk menghemat memori GPU. Perlu waktu untuk merender piksel, dan mungkin perlu waktu lebih dari satu frame untuk merender semuanya setelah scroll besar, seperti ayunan jari. Hal ini biasa dikenal sebagai papan catur.

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

Jika GPU benar-benar macet, browser (atau platform) bahkan dapat mulai men-throttle kecepatan saat mencoba update visual sehingga menurunkan kecepatan frame yang efektif. Meskipun secara teknis hal tersebut dapat mengurangi jumlah update frame yang hilang, secara visual hal tersebut akan tetap muncul sebagai throughput frame yang lebih rendah.

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

Jadi, kapan throughput frame penting?

Mendeteksi animasi

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

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

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

Kualitas versus kuantitas

Terakhir, mendeteksi pembaruan animasi dan frame animasi masih hanyalah sebagian dari cerita karena hanya menangkap jumlah pembaruan animasi, bukan kualitasnya.

Misalnya, Anda mungkin melihat frekuensi gambar stabil sebesar 60 fps saat menonton video. Secara teknis, proses ini berjalan sangat lancar, tetapi videonya mungkin memiliki kecepatan bit yang rendah, atau mengalami masalah buffering jaringan. Hal ini tidak ditangkap oleh metrik kelancaran animasi secara langsung, tetapi mungkin masih mengganggu pengguna.

Atau, game yang memanfaatkan <canvas> (mungkin bahkan menggunakan teknik seperti kanvas di luar layar untuk memastikan kecepatan frame yang stabil) secara teknis dapat sangat lancar dalam hal frame animasi, sekaligus gagal memuat aset game berkualitas tinggi ke dalam adegan atau menampilkan artefak rendering.

Dan tentu saja, situs bisa saja memiliki animasi yang sangat buruk 🙂

GIF sekolah lama sedang dibangun

Maksudku, mereka lumayan keren pada saat itu!

Status frame animasi tunggal

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

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

Tidak Ada Pembaruan yang Diinginkan Waktu tidak ada aktivitas, pengulangan dari frame sebelumnya.
Presentasi lengkap Update thread utama di-commit dalam batas waktu, atau tidak ada update thread utama yang diinginkan.
Dipresentasikan sebagian Khusus compositor; update thread utama yang tertunda tidak memiliki perubahan visual.
Dipresentasikan sebagian Khusus kompositor; thread utama memiliki pembaruan visual, tetapi pembaruan tersebut tidak menyertakan animasi yang memengaruhi kelancaran.
Dipresentasikan sebagian Khusus kompositor; thread utama memiliki update visual yang memengaruhi kelancaran, tetapi frame yang sebelumnya tidak berlaku tiba dan digunakan sebagai gantinya.
Dipresentasikan sebagian Khusus compositor; tanpa update utama yang diinginkan, dan update compositor memiliki animasi yang memengaruhi kehalusan.
Dipresentasikan sebagian Compositor saja, tetapi update compositor tidak memiliki animasi yang memengaruhi kehalusan.
Frame drop Tidak ada pembaruan. Tidak ada update compositor yang diinginkan, dan peristiwa utama tertunda.
Frame drop Pembaruan compositor diinginkan, tetapi tertunda.
Frame usang Kami menginginkan update yang dihasilkan oleh perender, tetapi GPU tetap tidak menampilkannya sebelum batas waktu vsync.

Status ini bisa diubah menjadi skor. Dan mungkin salah satu cara untuk menafsirkan skor ini adalah dengan menganggapnya sebagai probabilitas agar dapat diamati oleh pengguna. Satu frame yang dilepaskan mungkin tidak mudah diamati, tetapi urutan dari banyak frame yang dilepaskan yang memengaruhi kelancaran berturut-turut tentu saja.

Menyatukan semuanya: Metrik Persentase yang Dihapus

Meskipun terkadang Anda perlu memahami status setiap frame animasi, menetapkan skor "sekilas" singkat juga akan berguna untuk sebuah pengalaman.

Karena frame mungkin ditampilkan sebagian, dan karena update frame yang sepenuhnya dilewati mungkin tidak benar-benar memengaruhi kelancaran, kita sebaiknya tidak berfokus hanya pada perhitungan frame, dan lebih fokus pada jangkauan yang tidak dapat diberikan oleh browser untuk secara visual lengkap jika diperlukan.

Model mental harus bergerak dari:

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

Yang penting adalah: proporsi waktu untuk menunggu update penting. Menurut kami, hal ini sesuai dengan cara alami pengguna menikmati kelancaran konten web dalam praktiknya. Sejauh ini, kami telah menggunakan hal berikut sebagai kumpulan metrik awal:

  • Rata-Rata Persentase yang Dihapus: untuk semua frame animasi non-tidak ada aktivitas di seluruh linimasa
  • Kasus Terburuk dari Percent Dropped Frames: yang diukur dalam periode geser selama 1 detik.
  • Persentil ke-95 dari Percent Dropped Frames: yang diukur dalam periode waktu geser selama 1 detik.

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

Cobalah sendiri di alat developer.

HUD Performa

Chromium memiliki HUD Performa yang rapi dan tersembunyi di balik tanda (chrome://flags/#show-performance-metrics-hud). Di dalamnya, Anda dapat menemukan skor live untuk hal-hal seperti Core Web Vitals dan juga beberapa definisi eksperimental untuk kelancaran animasi berdasarkan Percent Dropped Frames dari waktu ke waktu.

HUD Performa

Statistik Rendering Frame

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

Statistik rendering frame

Frame Viewer di rekaman profil performa DevTools

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

Hari ini Anda akan menemukan bahwa frame dalam penampil frame lebih disejajarkan dengan batas vsync, dan diberi kode warna berdasarkan status. Masih belum ada visualisasi lengkap untuk semua perbedaan yang diuraikan di atas, tetapi kami berencana untuk menambahkan lebih banyak perbedaan dalam waktu dekat.

Penampil frame di Chrome DevTools

Pelacakan Chrome

Terakhir, dengan Pelacakan Chrome, alat pilihan untuk mendalami detail, Anda dapat merekam rekaman aktivitas "Rendering konten web" melalui UI Perfetto (atau about:tracing) yang baru, dan mendalami pipeline grafis Chrome. Tugas ini mungkin cukup sulit, tetapi ada beberapa hal yang baru-baru ini ditambahkan ke Chromium untuk mempermudah Anda. Anda bisa mendapatkan ringkasan tentang apa yang tersedia dalam dokumen Life of a Frame.

Melalui peristiwa rekaman aktivitas, Anda dapat menentukan:

  • Animasi mana yang sedang berjalan (menggunakan peristiwa bernama TrackerValidation).
  • Mendapatkan linimasa frame animasi yang tepat (menggunakan peristiwa bernama PipelineReporter).
  • Untuk update animasi yang mengalami jank, cari tahu persis apa yang memblokir animasi Anda agar tidak berjalan lebih cepat (menggunakan perincian peristiwa dalam peristiwa PipelineReporter).
  • Untuk animasi berbasis input, lihat waktu yang diperlukan untuk mendapatkan pembaruan visual (menggunakan peristiwa bernama EventLatency).

Pelaporan pipeline Pelacakan Chrome

Langkah selanjutnya

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

Kami akan terus mengabari Anda karena kami terus mengembangkan ide untuk merancang metrik komprehensif berdasarkan data setiap frame animasi.

Pada masa mendatang, kami juga ingin mendesain API yang dapat digunakan untuk mengukur kelancaran animasi secara optimal bagi pengguna sungguhan di kolom dan di lab. Nantikan juga info terbarunya di sana.

Masukan

Kami senang dengan semua peningkatan dan alat developer terbaru yang telah dihadirkan di Chrome untuk mengukur kelancaran animasi. Coba alat ini, lakukan benchmark animasi Anda, dan beri tahu kami ke mana arahnya.

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