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 mengalami halaman yang "terhenti" atau "membeku" selama scroll atau animasi. Kami ingin mengatakan bahwa pengalaman ini tidak lancar. Untuk mengatasi masalah semacam 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 yang konkret, dan mendiskusikan ide untuk metrik kelancaran animasi di masa mendatang. Seperti biasa, kami ingin mendapatkan masukan dari Anda.

Postingan ini akan membahas tiga topik utama:

  • Tampilan singkat tentang animasi dan frame animasi.
  • Pemikiran kami saat ini tentang cara mengukur kelancaran animasi secara keseluruhan.
  • Beberapa saran praktis untuk Anda manfaatkan dalam alat lab saat ini.

Apa yang dimaksud dengan 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 sama sekali tidak menyenangkan. Kita mungkin semua telah berinteraksi dengan antarmuka yang baru saja menambahkan terlalu banyak efek transisi "berguna", yang sebenarnya menjadi tidak nyaman untuk digunakan jika performanya buruk. Oleh karena itu, beberapa pengguna mungkin lebih memilih gerakan yang dikurangi, preferensi pengguna yang harus Anda hormati.

Bagaimana cara kerja animasi?

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

  1. Gaya: Menghitung gaya yang berlaku untuk elemen.
  2. Tata letak: Buat 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 cat.
  • Menyesuaikan properti komposit.

Karena tahap ini bersifat 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 animasi properti tata letak dapat dilakukan dengan mudah, ada biaya 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 mengoanimasikan properti komposit, adalah langkah pertama yang bagus untuk memastikan animasi yang lancar dan efisien. Namun, hal ini saja tidak menjamin kelancaran karena animasi web yang efisien sekalipun memiliki batas performa. Itulah sebabnya pengukuran selalu penting.

Apa yang dimaksud dengan frame animasi?

Pembaruan pada representasi visual halaman memerlukan waktu untuk muncul. 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 antara kecepatan refresh sesuai kebutuhan, atau bahkan menawarkan kecepatan frame yang sepenuhnya variabel.

Sasaran untuk aplikasi apa pun, seperti game atau browser, adalah memproses semua update visual dalam batch ini dan menghasilkan frame animasi yang lengkap secara visual dalam batas waktu, setiap saat. Perhatikan bahwa sasaran ini sepenuhnya berbeda dengan tugas browser penting 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 akan menghapus frame. Layar tidak mati, hanya berulang. Anda akan melihat update visual yang sama sedikit lebih lama—frame animasi yang sama dengan yang ditampilkan pada peluang frame sebelumnya.

Hal ini sebenarnya sering terjadi. Hal ini bahkan tidak selalu dapat dilihat, terutama untuk konten statis atau seperti dokumen, yang umum di platform web khususnya. Frame yang terlewat hanya terlihat 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 membutuhkan 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 panjang yang memblokir update visual.

Namun, bagaimana Anda dapat mengetahui kapan frame animasi telah melampaui batas waktunya dan menyebabkan frame yang terlewat?

Salah satu metode yang mungkin adalah menggunakan polling requestAnimationFrame(), tetapi memiliki beberapa kelemahan. requestAnimationFrame(), atau "rAF", memberi tahu browser bahwa Anda ingin melakukan animasi dan meminta kesempatan untuk melakukannya sebelum tahap gambar berikutnya dari pipeline rendering. Jika fungsi callback Anda tidak dipanggil pada saat yang Anda harapkan, artinya proses menggambar 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() bukanlah ide yang baik 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() menjadwalkan blok tidak ada aktivitas yang lama saat digunakan secara terus-menerus (blok yang melebihi satu frame).
  • Demikian pula, kurangnya blok tidak ada aktivitas yang lama akan mencegah browser menjadwalkan tugas lain yang berjalan lama (seperti pengumpulan sampah yang lebih lama dan pekerjaan spekulatif atau latar belakang 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).
  • Dan yang paling penting, kode ini tidak benar-benar menangkap semua jenis update animasi.

Terlalu banyak pekerjaan di thread utama dapat memengaruhi kemampuan untuk melihat frame animasi. Lihat Contoh Jank untuk melihat bagaimana animasi yang didorong rAF, setelah 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 macet, update visual mulai tersendat. Itu adalah jank.

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

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

Namun, meskipun tugas ini memakan waktu lama, halaman akan terus di-scroll dengan lancar. Hal ini karena di browser modern, scroll sering kali ber-thread, yang sepenuhnya didorong oleh kompositor.

Ini adalah contoh yang secara bersamaan berisi banyak frame yang dihapus di thread utama, tetapi masih memiliki banyak frame scroll yang berhasil dikirim di thread komposer. Setelah tugas yang lama selesai, update gambar thread utama tidak akan menawarkan perubahan visual. Polling rAF menyarankan penurunan frame menjadi 0, tetapi secara visual, pengguna tidak akan dapat melihat perbedaannya.

Untuk frame animasi, ceritanya tidak sesederhana itu.

Frame animasi: Update yang penting

Contoh di atas menunjukkan bahwa ada lebih banyak cerita daripada requestAnimationFrame().

Jadi, kapan pembaruan animasi dan frame animasi penting? Berikut beberapa kriteria yang kami pertimbangkan dan ingin kami dapatkan masukannya:

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

Pembaruan thread utama dan kompositor

Pembaruan frame animasi bukan boolean. Frame tidak hanya dapat dihapus sepenuhnya atau ditampilkan sepenuhnya. Ada banyak alasan mengapa frame animasi mungkin ditampilkan sebagian. Dengan kata lain, situs dapat secara bersamaan memiliki beberapa konten usang 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 komponisor baru (seperti contoh scroll dengan thread dari sebelumnya).

Salah satu alasan penting mengapa penggunaan animasi deklaratif untuk menganimasikan properti komposit direkomendasikan adalah karena tindakan tersebut memungkinkan animasi didorong sepenuhnya oleh thread kompositor meskipun thread utama sedang sibuk. Jenis animasi ini dapat terus menghasilkan update visual secara efisien dan secara 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, tanpa semua pembaruan visual baru, sebagai frame parsial. Frame sebagian cukup umum. Idealnya, update sebagian setidaknya akan menyertakan update visual yang paling penting, seperti animasi, tetapi hal itu hanya dapat terjadi jika animasi didorong oleh thread kompositor.

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 sepenuhnya statis, browser mungkin masih tertinggal dalam merender pembaruan visual selama scroll cepat. Hal ini karena rendering piksel konten di luar area pandang yang terlihat dapat dihapus untuk menghemat memori GPU. Diperlukan waktu untuk merender piksel, dan mungkin perlu waktu lebih lama dari satu frame untuk merender semuanya setelah scroll besar, seperti gerakan jari. Hal ini biasanya disebut checkerboarding.

Dengan setiap peluang rendering frame, Anda dapat melacak berapa banyak update 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 macet, browser (atau platform) bahkan dapat mulai mengurangi kecepatan saat mencoba update visual sehingga mengurangi kecepatan frame yang efektif. Meskipun secara teknis hal itu dapat mengurangi jumlah update frame yang hilang, secara visual hal itu akan tetap muncul sebagai throughput frame yang lebih rendah.

Namun, tidak semua jenis throughput frame rendah itu buruk. Jika halaman sebagian besar 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 menjadi penting?

Mendeteksi animasi

Throughput frame yang tinggi sangat penting, terutama selama periode dengan animasi penting. Berbagai jenis animasi akan bergantung pada update visual dari thread tertentu (utama, kompositor, atau pekerja), sehingga update visualnya bergantung pada thread tersebut yang memberikan updatenya 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 yang didorong oleh 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 harus menghasilkan update visual atau animasi. Misalnya, menggunakan polling rAF hanya untuk melacak kecepatan frame (seperti yang ditunjukkan di atas) seharusnya tidak memengaruhi pengukuran kelancaran karena tidak ada update visual.

Kualitas versus kuantitas

Terakhir, mendeteksi animasi dan pembaruan frame animasi masih merupakan bagian dari cerita karena hanya menangkap kuantitas pembaruan animasi, bukan kualitasnya.

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

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

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

GIF lama sedang dibangun

Maksud saya, mereka cukup keren untuk zamannya.

Status satu frame animasi

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

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

Tidak Ingin Melakukan Update Waktu tidak ada aktivitas, pengulangan frame sebelumnya.
Ditampilkan sepenuhnya Update thread utama di-commit dalam batas waktu, atau tidak ada update thread utama yang diinginkan.
Dipresentasikan sebagian Khusus kompositor; pembaruan thread utama yang tertunda tidak memiliki perubahan visual.
Dipresentasikan sebagian Khusus kompositor; thread utama memiliki update visual, tetapi update tersebut tidak menyertakan animasi yang memengaruhi kelancaran.
Dipresentasikan sebagian Khusus kompositor; thread utama memiliki update visual yang memengaruhi kelancaran, tetapi frame yang sebelumnya sudah tidak berlaku tiba dan digunakan.
Dipresentasikan sebagian Khusus kompositor; tanpa update utama yang diinginkan, dan update kompositor memiliki animasi yang memengaruhi kelancaran.
Dipresentasikan sebagian Khusus kompositor, tetapi update kompositor tidak memiliki animasi yang memengaruhi kelancaran.
Frame yang dilepas Tidak ada update. Tidak ada update kompositor yang diinginkan, dan utama tertunda.
Frame yang dilepas Update kompositor diinginkan, tetapi tertunda.
Frame usang Update diinginkan, yang dihasilkan oleh perender, tetapi GPU masih belum menampilkannya sebelum batas waktu vsync.

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

Menyatukan semuanya: Metrik Persentase Frame yang Dihapus

Meskipun terkadang Anda perlu mempelajari status setiap frame animasi secara mendalam, sebaiknya tetap tetapkan skor "sekilas" yang cepat untuk pengalaman.

Karena frame mungkin ditampilkan sebagian,dan karena update frame yang dilewati sepenuhnya mungkin tidak benar-benar memengaruhi kelancaran, sebaiknya kita tidak terlalu berfokus pada hanya menghitung frame, dan lebih berfokus pada tingkat browser tidak dapat memberikan update yang lengkap secara visual saat diperlukan.

Model mental harus beralih dari:

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

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

  • Rata-rata Persentase yang Dihapus: untuk semua frame animasi non-idle di seluruh linimasa
  • Kasus Terburuk Persentase Frame yang Dihapus: seperti yang diukur selama periode waktu geser 1 detik.
  • Persentil ke-95 Persentase Frame yang Dihapus: seperti yang diukur selama periode waktu jendela geser 1 detik.

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

Cobalah sendiri di alat developer.

HUD Performa

Chromium memiliki HUD Performa yang rapi yang disembunyikan di balik tanda (chrome://flags/#show-performance-metrics-hud). Di dalamnya, Anda dapat menemukan skor live untuk hal-hal seperti Data Web Inti dan juga beberapa definisi eksperimental untuk kelancaran animasi berdasarkan Persentase Frame yang Dihapus 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 sebagian dari update frame yang dihapus sepenuhnya. 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 Pelihat 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 debug masalah animasi.

Sekarang Anda akan menemukan bahwa frame di penampil frame lebih selaras dengan batas vsync, dan diberi kode warna berdasarkan status. Visualisasi lengkap untuk semua nuansa yang diuraikan di atas masih belum tersedia, tetapi kami berencana untuk menambahkan lebih banyak dalam waktu dekat.

Penampil frame di Chrome DevTools

Pelacakan Chrome

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

Melalui peristiwa rekaman aktivitas, Anda dapat menentukan dengan pasti:

  • Animasi mana yang sedang berjalan (menggunakan peristiwa bernama TrackerValidation).
  • Mendapatkan linimasa frame animasi yang tepat (menggunakan peristiwa bernama PipelineReporter).
  • Untuk update animasi yang lambat, cari tahu dengan tepat apa yang memblokir animasi Anda agar berjalan lebih cepat (menggunakan pengelompokan peristiwa dalam peristiwa PipelineReporter).
  • Untuk animasi yang didorong input, lihat berapa lama waktu yang diperlukan untuk mendapatkan pembaruan visual (menggunakan peristiwa bernama EventLatency).

Pelapor pipeline Pelacakan Chrome

Langkah berikutnya

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

Kami akan terus mengabari Anda saat kami terus mengerjakan ide untuk mendesain metrik komprehensif berdasarkan data frame animasi individual.

Di masa mendatang, kami juga ingin mendesain API yang memungkinkan pengukuran kehalusan animasi secara berperforma tinggi untuk pengguna nyata di lapangan serta di lab. Nantikan info terbarunya di sana juga.

Masukan

Kami senang dengan semua peningkatan terbaru dan alat developer yang telah dikirimkan di Chrome untuk mengukur kelancaran animasi. Cobalah alat ini, lakukan benchmark pada 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.