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 mendengar masukan Anda.

Postingan ini akan membahas tiga topik utama:

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

Apa yang dimaksud dengan animasi?

Animasi membuat konten 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 berperforma buruk. Oleh karena itu, beberapa pengguna mungkin lebih memilih gerakan yang dikurangi, preferensi pengguna yang harus Anda hormati.

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: Isi piksel untuk setiap elemen ke dalam lapisan.
  4. Composite: 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 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 mengapa selalu penting untuk mengukur!

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 pembaruan pada beberapa interval, sehingga pembaruan 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 hal yang 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, menyelesaikan semua pembaruan visual dalam batas waktu yang ditetapkan oleh layar menjadi terlalu sulit. Jika hal ini terjadi, browser akan menghapus frame. Layar tidak menjadi hitam, tetapi hanya berulang. Anda akan melihat pembaruan visual yang sama sedikit lebih lama—frame animasi yang sama dengan yang disajikan pada peluang frame sebelumnya.

Hal ini sebenarnya sering terjadi. Konten ini bahkan tidak terlihat, terutama untuk konten statis atau seperti dokumen, yang umumnya umum ditemukan di platform web. 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 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 tidak dipanggil pada saat yang Anda harapkan, berarti 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 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 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 di-thread, yang sepenuhnya didorong oleh kompositor.

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 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: Pembaruan 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, aset ini dapat secara bersamaan memiliki beberapa konten usang sekaligus juga memiliki 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 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 parsial cukup umum terjadi. 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 terhambat, browser (atau platform) dapat mulai memperlambat kecepatan percobaan update visual, sehingga menurunkan 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. Jenis animasi yang berbeda akan bergantung pada update visual dari thread tertentu (utama, compositor, atau pekerja), sehingga update visualnya bergantung pada thread tersebut yang menyediakan 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 berbasis input pengguna, lebih jelas untuk ditentukan dibandingkan animasi berbasis JavaScript yang diterapkan sebagai update berkala pada properti gaya yang dapat dianimasikan.

Meskipun dengan requestAnimationFrame(), Anda tidak selalu dapat berasumsi bahwa setiap panggilan rAF tentu menghasilkan pembaruan 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 60 fps yang stabil 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 ditangkap oleh metrik kelancaran animasi secara langsung, tetapi mungkin masih mengagetkan 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 layar 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 dilepas dapat 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 Compositor; thread utama memiliki update visual yang memengaruhi kelancaran, tetapi frame yang sebelumnya tidak berlaku tiba dan digunakan sebagai gantinya.
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.
Bingkai 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 diperlukan untuk mendalami status setiap frame animasi, ada baiknya juga untuk menetapkan skor "secara sekilas" cepat untuk sebuah pengalaman.

Karena frame mungkin dipresentasikan sebagian, dan karena update frame yang dilewati sepenuhnya mungkin tidak memengaruhi kelancaran, kita ingin mengurangi fokus hanya pada penghitungan frame, dan lebih berfokus pada jangkauan yang browser tidak dapat menyediakan update yang lengkap secara visual saat diperlukan.

Model mental harus beralih dari:

  1. Frame Per Detik, hingga
  2. Mendeteksi pembaruan 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.

Coba 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 "Statistik Rendering Frame" di DevTools melalui setelan Rendering untuk melihat tayangan live frame animasi baru, yang diberi kode warna untuk membedakan update parsial dari update frame yang dihentikan 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 terbaru, bahkan di Chrome Canary terbaru, yang menurut kami akan sangat memudahkan proses debug masalah animasi.

Saat ini, Anda akan menemukan bahwa frame di penampil frame lebih selaras dengan batas vsync, dan diberi kode warna berdasarkan status. Masih belum ada visualisasi lengkap untuk semua nuansa yang diuraikan di atas, tetapi kami berencana menambahkan lebih banyak visualisasi 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 mengalami jank, cari tahu apa yang menghalangi animasi berjalan lebih cepat (menggunakan perincian 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 dalam membangun 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 sebenarnya 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 menantikan pendapat Anda.