Menuju metrik kelancaran animasi

Pelajari tentang mengukur animasi, cara memperhitungkan bingkai animasi, dan kelancaran halaman secara keseluruhan.

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

Anda mungkin pernah mengalami laman yang "gagap" atau "bekukan" saat menggulir atau animasi. Kami ingin menyatakan bahwa pengalaman ini tidak mulus. Ke alamat jenis masalah ini, tim Chrome telah berupaya untuk menambahkan lebih banyak ke alat lab kami untuk deteksi animasi, serta melakukan peningkatan yang stabil ke diagnostik pipeline rendering dalam Chromium.

Kami ingin menyampaikan kemajuan terbaru, menawarkan panduan alat yang konkret, dan mendiskusikan ide untuk metrik kelancaran animasi di masa mendatang. Seperti biasa, kami akan senang untuk mendengar masukan Anda.

Postingan ini akan membahas tiga topik utama:

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

Apa itu animasi?

Animasi membuat konten menjadi lebih hidup. Dengan membuat konten bergerak, terutama untuk merespons pada interaksi pengguna, animasi dapat membuat pengalaman terasa lebih alami, dimengerti, dan menyenangkan.

Tetapi animasi yang diterapkan dengan buruk, atau hanya menambahkan terlalu banyak animasi, dapat menurunkan pengalaman dan membuatnya jelas sama sekali tidak menyenangkan. Kita mungkin semua berinteraksi dengan antarmuka yang menambahkan terlalu banyak "berguna" transisi yang efeknya negatif, yang sebenarnya tidak baik untuk dialami ketika mereka berkinerja buruk. Beberapa pengguna mungkin sebenarnya lebih memilih gerakan yang lebih rendah, preferensi pengguna yang harus Anda hormati.

Bagaimana cara kerja animasi?

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

  1. Gaya: Hitung gaya yang berlaku untuk elemen.
  2. Tata letak: Membuat geometri, dan posisi untuk setiap elemen.
  3. Menggambar: Isi {i>pixel<i} untuk setiap elemen ke dalam {i>layer<i}.
  4. Composite: Gambar {i>layer<i} ke layar.

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

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

Karena tahapan ini berurutan, penting untuk menentukan animasi ketentuan properti yang berada di jalur yang lebih rumit. Semakin awal pembaruan terjadi dalam proses, semakin besar biayanya dan semakin kecil kemungkinan lancar. (Lihat Rendering performa untuk detail selengkapnya.)

Meskipun mudah untuk menganimasikan properti tata letak, ada biaya untuk melakukannya, bahkan jika biaya tersebut tidak langsung terlihat. Animasi harus yang ditentukan dalam hal perubahan properti gabungan jika memungkinkan.

Menentukan animasi CSS deklaratif atau menggunakan Web Animasi, dan memastikan Anda menganimasikan properti, adalah langkah pertama yang bagus untuk memastikan animasi yang mulus dan efisien. Tapi tetap saja, hal ini saja tidak menjamin kelancaran karena bahkan animasi web yang efisien memiliki batasan performa. Itulah mengapa selalu penting untuk mengukur!

Apa itu {i>frame<i} animasi?

Pembaruan pada representasi visual halaman memerlukan beberapa waktu untuk muncul. Visualisasi akan menghasilkan bingkai animasi baru, yang pada akhirnya akan dirender pada tampilan pengguna.

Menampilkan update pada beberapa interval, sehingga update visual dikelompokkan. Banyak layar update pada interval waktu yang 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). Layar ini sering kali dapat beradaptasi secara aktif di 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 pembaruan visual batch dan menghasilkan {i>frame<i} animasi yang lengkap secara visual dalam tenggat waktu, setiap saat. Perhatikan bahwa tujuan ini sepenuhnya berbeda dari tugas browser yang penting seperti memuat konten dari jaringan dengan cepat atau menjalankan tugas JavaScript secara efisien.

Pada titik tertentu, menyelesaikan semua pembaruan visual dalam satu sistem bisa menjadi sangat sulit batas waktu yang ditentukan oleh layar. Jika hal ini terjadi, browser melepaskan frame. Layar Anda tidak menjadi hitam, melainkan berulang dengan sendirinya. Anda lihat pembaruan visual yang sama untuk sedikit lebih lama—bingkai animasi yang sama dengan dipresentasikan pada peluang frame sebelumnya.

Sebenarnya hal ini sering terjadi. Hal itu bahkan tidak dapat disimak, terutama untuk konten statis atau seperti dokumen, yang umum ditemukan di platform web secara khusus. Frame yang dilepas hanya terlihat jika ada elemen visual penting pembaruan, seperti animasi, yang membutuhkan aliran animasi yang stabil untuk menampilkan gerakan yang mulus.

Apa yang memengaruhi frame animasi?

Pengembang web dapat memberi pengaruh besar pada kemampuan {i>browser<i} untuk cepat dan membuat dan menyajikan pembaruan visual secara efisien!

Beberapa contohnya:

  • Menggunakan konten yang terlalu besar atau membutuhkan banyak sumber daya untuk didekode dengan cepat di perangkat target.
  • Menggunakan terlalu banyak lapisan yang memerlukan terlalu banyak memori GPU.
  • Mendefinisikan gaya CSS atau animasi web yang terlalu rumit.
  • Menggunakan anti-pola desain yang menonaktifkan pengoptimalan rendering cepat.
  • Terlalu banyak pekerjaan JS di thread utama, sehingga menyebabkan tugas panjang yang memblokir pembaruan.

Tetapi bagaimana Anda bisa tahu ketika {i>frame<i} animasi telah melewati tenggat waktu dan menyebabkan frame turun?

Salah satu metode yang mungkin adalah menggunakan requestAnimationFrame() polling, namun ia memiliki beberapa kekurangan. requestAnimationFrame(), atau "rAF", memberi tahu browser bahwa Anda ingin menjalankan animasi dan meminta sebuah kesempatan untuk melakukannya sebelum tahap cat berikutnya dari pipeline rendering. Jika fungsi callback Anda tidak dipanggil pada saat yang Anda harapkan, itu berarti cat tidak dieksekusi, dan satu atau lebih {i>frame<i} dilewati. Melalui polling dan dengan menghitung seberapa sering rAF dipanggil, Anda bisa menghitung semacam "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 pollingnya sendiri.
  • Dapat memblokir jalur kritis.
  • Bahkan jika polling rAF cepat, hal itu dapat mencegah requestIdleCallback() tidak dapat menjadwalkan blok diam yang panjang ketika digunakan secara terus-menerus (blok yang melebihi satu {i>frame<i}).
  • Demikian pula, kurangnya blok menganggur yang panjang mencegah browser menjadwalkan tugas yang berjalan lama (seperti pembersihan sampah memori yang lebih lama dan latar belakang lain atau karya spekulatif).
  • Jika polling diaktifkan dan dinonaktifkan, Anda akan melewatkan kasus ketika anggaran frame terlampaui.
  • Polling akan melaporkan positif palsu jika browser menggunakan frekuensi update variabel (misalnya, karena daya atau status visibilitas).
  • Dan yang paling penting, ia tidak benar-benar menangkap semua jenis animasi pembaruan!

Terlalu banyak pekerjaan di thread utama dapat memengaruhi kemampuan untuk melihat frame animasi. Lihat Jank Contoh untuk melihat bagaimana Animasi berbasis rAF, saat terlalu banyak pekerjaan di thread utama (misalnya tata letak), akan menyebabkan penurunan frame dan lebih sedikit callback rAF, dan FPS lebih rendah.

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

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

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

Namun, meskipun memiliki tugas yang panjang, halamannya terus bergulir dengan lancar. Ini adalah karena di browser modern, menggulir sering berangkai, sepenuhnya didorong oleh compositor.

Ini adalah contoh yang secara bersamaan berisi banyak frame yang dilepas pada utas, namun masih memiliki banyak bingkai gulir yang berhasil ditayangkan pada thread compositor. Setelah tugas yang berjalan lama selesai, update thread paint utama tidak memiliki perubahan visual untuk ditawarkan. Polling rAF menyarankan penurunan frame ke 0, namun 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 daripada hanya requestAnimationFrame().

Jadi kapan update animasi dan {i>frame<i} animasi perlu digunakan? Berikut beberapa kriteria yang sedang kami pikirkan dan kami ingin mendapatkan masukan tentang:

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

Update thread utama dan compositor

Pembaruan bingkai animasi bukan boolean. Bukan karena {i>frame<i} hanya akan sepenuhnya dihentikan atau disajikan secara lengkap. Ada banyak alasan mengapa animasi frame mungkin ditampilkan sebagian. Dengan kata lain, secara bersamaan bisa memiliki beberapa konten usang sekaligus juga memiliki beberapa pembaruan visual baru yang disajikan.

Contoh yang paling umum adalah saat {i>browser<i} tidak dapat menghasilkan pembaruan thread utama dalam batas waktu frame tetapi memiliki thread compositor baru update (seperti contoh scroll berangkai dari materi sebelumnya).

Salah satu alasan penting mengapa penggunaan animasi deklaratif adalah untuk menganimasikan properti direkomendasikan adalah tindakan ini akan mengaktifkan animasi sepenuhnya oleh thread compositor bahkan ketika thread utama sibuk. Jenis-jenis ini animasi dapat terus menghasilkan pembaruan visual secara efisien dan dalam paralel.

Di sisi lain, mungkin ada kasus di mana pembaruan thread utama akhirnya menjadi tersedia untuk presentasi, tetapi hanya setelah melewatkan beberapa tenggat waktu frame. Di sini, browser akan mendapatkan beberapa update baru, tetapi mungkin bukan yang terbaru.

Secara umum, kami mempertimbangkan {i>frame<i} yang berisi beberapa pembaruan visual, tanpa semua pembaruan visual baru, sebagai frame parsial. Frame sebagian cukup umum. Idealnya, pembaruan sebagian setidaknya akan menyertakan bagian yang paling penting pembaruan visual, seperti animasi, tetapi itu hanya bisa terjadi jika animasi didorong oleh thread compositor.

Pembaruan cat tidak ada

Jenis pembaruan parsial lainnya adalah ketika media seperti gambar belum selesai decoding dan rasterisasi tepat waktu untuk presentasi {i>frame<i}.

Atau, meskipun halaman sangat statis, browser mungkin masih tertinggal rendering pembaruan visual selama scrolling cepat. Hal itu karena rendisi piksel dari konten di luar area pandang yang terlihat dapat dihapus untuk menghemat memori GPU. Ini membutuhkan waktu untuk merender piksel, dan mungkin perlu waktu lebih dari satu frame merender semuanya setelah menggulir besar, seperti lemparan jari. Ini umumnya yang dikenal sebagai papan catur.

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

Jika GPU benar-benar macet, browser (atau platform) bahkan dapat mulai men-throttle kecepatan saat percobaan pembaruan visual dan dengan demikian mengurangi kecepatan frame yang efektif. Walaupun secara teknis dapat mengurangi jumlah penurunan pembaruan {i>frame<i}, secara visual itu akan tetap muncul sebagai throughput {i>frame<i} yang lebih rendah.

Namun, tidak semua jenis throughput frame yang rendah itu buruk. Jika sebagian besar halaman tidak ada aktivitas dan tidak ada animasi aktif, kecepatan frame rendah sama baiknya menarik karena kecepatan frame yang tinggi (dan, bisa menghemat baterai!).

Jadi, kapan throughput frame penting?

Mendeteksi animasi

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

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

Bahkan dengan requestAnimationFrame() Anda tidak selalu berasumsi bahwa setiap panggilan rAF tentu menghasilkan pembaruan atau animasi. Misalnya, menggunakan polling rAF hanya untuk melacak kecepatan frame (seperti yang ditunjukkan di atas) seharusnya tidak mempengaruhi pengukuran kelancaran karena ada tidak ada pembaruan visual.

Kualitas versus kuantitas

Terakhir, mendeteksi animasi dan pembaruan {i>frame<i} animasi masih hanya bagian dari cerita karena hanya menangkap kuantitas pembaruan animasi, bukan {i>sandwich<i} itu.

Misalnya, Anda mungkin melihat kecepatan frame 60 fps yang stabil saat menonton video video. Secara teknis, ini sangat mulus, tetapi videonya sendiri mungkin memiliki kecepatan bit rendah, atau masalah dengan {i>buffering<i} jaringan. Data ini tidak diambil oleh metrik kelancaran animasi secara langsung, tetapi mungkin masih mengagetkan .

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

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

GIF sekolah tua sedang dibangun

Maksudku, mereka lumayan keren pada waktunya!

Status satu frame animasi

Karena frame mungkin ditampilkan sebagian, atau frame yang dilepas dapat terjadi dengan cara tertentu yang tidak mempengaruhi kelancaran, kita mulai menganggap setiap {i>frame<i} memiliki kelengkapan atau kelancaran tugas.

Berikut ini spektrum cara di mana kita menafsirkan status satu {i>frame<i} animasi, diurutkan dari kasus terbaik ke terburuk:

Tidak Ada Update yang Diinginkan Waktu tidak ada aktivitas, ulangi frame sebelumnya.
Dipresentasikan secara lengkap Update thread utama di-commit dalam batas waktu, atau tidak yang diinginkan untuk pembaruan thread utama.
Dipresentasikan sebagian Khusus Compositor; pembaruan thread utama yang tertunda tidak memiliki berubah.
Dipresentasikan sebagian Khusus Compositor; thread utama memiliki pembaruan visual, tapi pembaruan tidak menyertakan animasi yang memengaruhi kelancaran.
Dipresentasikan sebagian Khusus Compositor; thread utama memiliki pembaruan visual yang memengaruhi kelancaran, tetapi {i>frame<i} yang sebelumnya sudah tidak berlaku tiba dan digunakan sebagai gantinya.
Dipresentasikan sebagian Khusus Compositor; tanpa update utama yang diinginkan, dan pembaruan compositor memiliki animasi yang memengaruhi kelancaran.
Dipresentasikan sebagian Compositor saja, tetapi pembaruan compositor tidak memiliki animasi yang memengaruhi kelancaran.
Bingkai yang dilepas Tidak ada pembaruan. Tidak ada pembaruan compositor yang diinginkan, dan utama tertunda.
Bingkai yang dilepas Pembaruan compositor diinginkan, tetapi tertunda.
Frame sudah tidak berlaku Pembaruan diinginkan, pembaruan itu dihasilkan oleh perender, tetapi GPU masih belum menyajikannya sebelum batas waktu vsync.

Anda dapat mengubah status ini menjadi semacam skor. Dan mungkin salah satu cara menafsirkan skor ini adalah menganggapnya sebagai probabilitas yang dapat diamati oleh pengguna. Satu {i>frame<i} yang diturunkan mungkin tidak bisa diamati, tetapi serangkaian banyak {i>frame<i} yang dilepas yang memengaruhi kelancaran berturut-turut!

Menyatukan semuanya: Metrik Persen Frame yang Dilepas

Meskipun terkadang diperlukan untuk mendalami status masing-masing {i>frame <i}animasi, ada baiknya juga untuk memberikan gambaran singkat "secara sekilas" skor untuk sebuah pengalaman.

Karena frame mungkin dipresentasikan sebagian, dan bahkan karena dilewati sepenuhnya update frame mungkin sebenarnya tidak memengaruhi kelancaran, kita perlu lebih berfokus pada hanya menghitung frame, dan lebih banyak lagi pada jarak yang tidak dapat dilakukan browser memberikan pembaruan lengkap secara visual jika diperlukan.

Model mental harus bergerak dari:

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

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

  • Persentase Rata-Rata Penurunan: untuk semua frame animasi non-diam di seluruh frame seluruh linimasa
  • Kasus Terburuk dari Persentase Frame yang Dihapus: seperti yang diukur selama geser 1 detik periode waktu.
  • Persentil ke-95 dari Persen Frame yang Dilepas: seperti yang diukur selama 1 detik jendela waktu yang bergeser.

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

Coba sendiri di alat developer.

HUD Performa

Chromium memiliki HUD Performa rapi yang tersembunyi di balik bendera (chrome://flags/#show-performance-metrics-hud). Di dalamnya, Anda dapat menemukan skor 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 "Rendering Frame Statistik" di DevTools melalui setelan Rendering untuk melihat tayangan live frame animasi baru, yang diberi kode warna untuk membedakan update parsial dari frame yang berhenti sepenuhnya pembaruan. FPS yang dilaporkan hanya untuk frame yang ditampilkan secara lengkap.

Statistik rendering frame

Frame Viewer di rekaman profil performa DevTools

Performa DevTools panel memiliki lama memiliki Frame . Namun, cara ini menjadi sedikit tidak sinkron dengan cara pipeline rendering modern berjalan. Baru-baru ini telah ada banyak peningkatan, bahkan dalam Chrome Canary, yang menurut kami akan sangat memudahkan proses debug masalah animasi.

Sekarang, Anda akan menemukan bahwa bingkai di penampil {i>frame<i} lebih selaras dengan batas vsync, dan kode warna berdasarkan status. Masih ada yang belum penuh visualisasi untuk semua nuansa yang diuraikan di atas, tetapi kami berencana menambahkan dalam waktu dekat.

Bingkai penampil di Chrome DevTools

Pelacakan Chrome

Terakhir, dengan Pelacakan Chrome, alat pilihan untuk mendalami detail, Anda dapat merekam "Web content rendering" rekaman aktivitas melalui Perfetto baru UI (atau about:tracing), dan mempelajari pipeline grafis. Ini bisa menjadi tugas yang sulit, tetapi ada beberapa hal yang baru ditambahkan ke Chromium untuk memudahkan Anda. Anda bisa mendapatkan gambaran umum tentang yang tersedia dalam artikel Life of a Bingkai dokumen.

Melalui peristiwa pelacakan, Anda secara pasti dapat menentukan:

  • Animasi mana yang berjalan (menggunakan peristiwa bernama TrackerValidation).
  • Mendapatkan linimasa dari frame animasi yang tepat (menggunakan peristiwa bernama PipelineReporter).
  • Untuk update animasi yang mengalami jank, cari tahu dengan tepat apa yang memblokir animasi agar tidak berjalan lebih cepat (menggunakan pengelompokan peristiwa dalam PipelineReporter peristiwa).
  • Untuk animasi berbasis 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 bagi membangun pengalaman pengguna yang hebat di web. Metrik Berbasis lab seperti Total Waktu Pemblokiran (TBT) sangat penting untuk menangkap dan mendiagnosis potensi masalah interaktivitas. Kami berencana untuk mendesain metrik berbasis lab serupa untuk kelancaran animasi.

Kami akan terus mengabari Anda selagi kami terus bekerja mencari ide-ide untuk merancang metrik komprehensif berdasarkan data frame animasi individual.

Di masa mendatang, kami juga ingin mendesain API yang memungkinkan pengukuran kelancaran animasi yang berperforma lebih baik bagi pengguna aktual di field serta di lab. Ikuti juga info terbarunya di sana.

Masukan

Kami senang dengan semua peningkatan dan alat pengembang terbaru yang telah diluncurkan di Chrome untuk mengukur kelancaran animasi. Silakan coba alat-alat ini, menetapkan tolok ukur animasi Anda, dan memberi tahu kami ke mana arahnya!

Anda dapat mengirim komentar ke web-vitals-feedback Google grup dengan "[Metrik Kehalusan]" di baris subjek. Kami benar-benar mencari kami menantikan pendapat Anda.