Efek blur adalah cara yang bagus untuk mengalihkan fokus pengguna. Membuat beberapa elemen visual tampak buram sekaligus menjaga fokus elemen lain secara alami akan mengarahkan fokus pengguna. Pengguna mengabaikan konten yang diburamkan dan berfokus pada konten yang dapat mereka baca. Salah satu contohnya adalah daftar ikon yang menampilkan detail tentang setiap item saat diarahkan kursor. Selama waktu tersebut, pilihan yang tersisa dapat diburamkan untuk mengalihkan pengguna ke informasi yang baru ditampilkan.
TL;DR
Menganimasikan pemburaman bukanlah opsi yang tepat karena prosesnya sangat lambat. Sebagai gantinya, hitungkan terlebih dahulu serangkaian versi yang semakin buram dan lakukan transisi silang di antara versi tersebut. Rekan saya, Yi Gu, menulis library untuk menangani semuanya untuk Anda. Lihat demo kami.
Namun, teknik ini dapat cukup mengganggu jika diterapkan tanpa periode transisi. Menganimasikan pemburaman — bertransisi dari tidak diburamkan menjadi diburamkan — tampaknya merupakan pilihan yang wajar, tetapi jika pernah mencoba melakukannya di web, Anda mungkin mendapati bahwa animasinya tidak lancar, seperti yang ditunjukkan demo ini jika Anda tidak memiliki mesin yang canggih. Bisakah kita melakukan yang lebih baik?
Permasalahan
Saat ini, kita tidak dapat membuat animasi pemburaman berfungsi secara efisien. Namun, kita dapat
menemukan solusi yang terlihat cukup baik, tetapi secara teknis, bukan
buram animasi. Untuk memulai, mari kita pahami terlebih dahulu alasan pemburaman animasi
lambat. Untuk memburamkan elemen di web, ada dua teknik: Properti filter
CSS
dan filter SVG. Berkat peningkatan dukungan dan kemudahan penggunaan, filter CSS
biasanya digunakan. Sayangnya, jika Anda diwajibkan untuk mendukung Internet Explorer, Anda tidak punya pilihan selain menggunakan filter SVG karena IE 10 dan 11 mendukungnya, tetapi tidak mendukung filter CSS. Kabar baiknya adalah solusi kami untuk menganimasikan
buram berfungsi dengan kedua teknik tersebut. Jadi, mari kita coba temukan bottleneck dengan melihat
DevTools.
Jika mengaktifkan "Paint Flashing" di DevTools, Anda tidak akan melihat flash sama sekali. Sepertinya tidak ada proses pengecatan ulang yang terjadi. Dan secara teknis hal ini benar karena "repaint" mengacu pada CPU yang harus mewarnai ulang tekstur elemen yang dipromosikan. Setiap kali elemen dipromosikan dan diburamkan, pemburaman diterapkan oleh GPU menggunakan shader.
Filter SVG dan filter CSS menggunakan filter konvolusi untuk menerapkan buram. Filter konvolusi cukup mahal karena untuk setiap piksel output, sejumlah piksel input harus dipertimbangkan. Makin besar gambar atau makin besar radius blur, makin mahal efeknya.
Dan di sinilah letak masalahnya, kita menjalankan operasi GPU yang agak mahal setiap frame, sehingga menghabiskan anggaran frame sebesar 16 md dan akhirnya jauh di bawah 60 fps.
Rabbit Hole
Jadi, apa yang dapat kita lakukan agar proses ini berjalan lancar? Kita dapat menggunakan trik sulap. Alih-alih
mengoanimasi nilai blur sebenarnya (radius blur), kita melakukan pra-komputasi
beberapa salinan yang diburamkan dengan nilai blur yang meningkat secara eksponensial, lalu
melakukan transisi silang di antara keduanya menggunakan opacity
.
Cross-fade adalah serangkaian transisi fade-in dan fade-out opasitas yang tumpang-tindih. Misalnya, jika memiliki empat tahap pemburaman, kita akan memudarkan tahap pertama sekaligus memudarkan tahap kedua secara bersamaan. Setelah tahap kedua mencapai opasitas 100% dan tahap pertama mencapai 0%, kita akan memudarkan tahap kedua sekaligus memudarkan tahap ketiga. Setelah selesai, kita akhirnya memudarkan tahap ketiga dan memudar versi keempat dan terakhir. Dalam skenario ini, setiap tahap akan memerlukan ¼ dari total durasi yang diinginkan. Secara visual, ini terlihat sangat mirip dengan blur animasi yang sebenarnya.
Dalam eksperimen kami, meningkatkan radius pemburaman secara eksponensial per tahap menghasilkan
hasil visual terbaik. Contoh: Jika memiliki empat tahap pemburaman, kita akan menerapkan
filter: blur(2^n)
ke setiap tahap, yaitu tahap 0: 1 piksel, tahap 1: 2 piksel, tahap 2: 4 piksel
dan tahap 3: 8 piksel. Jika kita memaksa setiap salinan yang diburamkan ini ke lapisannya sendiri
(disebut "mempromosikan") menggunakan will-change: transform
, mengubah opasitas pada elemen
ini akan sangat cepat. Secara teori, hal ini akan memungkinkan kita
melakukan pemuatan awal pada pekerjaan pemburaman yang mahal. Ternyata, logikanya salah. Jika
menjalankan demo ini,
Anda akan melihat bahwa kecepatan frame masih di bawah 60 fps, dan pemburaman sebenarnya
lebih buruk dari sebelumnya.
Pemeriksaan singkat pada DevTools mengungkapkan bahwa GPU masih sangat sibuk dan memperluas setiap frame hingga ~90 md. Namun, mengapa? Kita tidak lagi mengubah nilai blur, hanya opasitas, jadi apa yang terjadi? Sekali lagi, masalahnya terletak pada sifat efek blur: Seperti yang dijelaskan sebelumnya, jika elemen dipromosikan dan diburamkan, efeknya akan diterapkan oleh GPU. Jadi, meskipun kita tidak lagi menganimasikan nilai blur, tekstur itu sendiri masih tidak diburamkan dan perlu diburamkan ulang setiap frame oleh GPU. Alasan kecepatan frame menjadi lebih buruk daripada sebelumnya berasal dari fakta bahwa dibandingkan dengan penerapan sederhana, GPU sebenarnya memiliki lebih banyak pekerjaan daripada sebelumnya, karena sebagian besar waktu dua tekstur terlihat yang perlu diburamkan secara independen.
Hasil yang kita dapatkan tidak bagus, tetapi membuat animasi menjadi sangat cepat. Kita kembali ke tidak mempromosikan elemen yang akan diburamkan, tetapi mempromosikan wrapper induk. Jika elemen diburamkan dan dipromosikan, efeknya akan diterapkan oleh GPU. Inilah yang membuat demo kita lambat. Jika elemen diburamkan tetapi tidak dipromosikan, pemburaman akan dirasterisasi ke tekstur induk terdekat. Dalam kasus ini, elemen wrapper induk yang dipromosikan. Gambar yang diburamkan kini menjadi tekstur elemen induk dan dapat digunakan kembali untuk semua frame mendatang. Hal ini hanya berfungsi karena kita tahu bahwa elemen yang diburamkan tidak dianimasikan dan menyimpan cache-nya sebenarnya bermanfaat. Berikut adalah demo yang menerapkan teknik ini. Saya ingin tahu pendapat Moto G4 tentang pendekatan ini? Peringatan spoiler: ia menganggapnya hebat:
Sekarang kita memiliki banyak headroom di GPU dan 60 fps yang lancar. Kita berhasil!
Produksi
Dalam demo, kami menduplikasi struktur DOM beberapa kali agar salinan konten dapat diburamkan dengan kekuatan yang berbeda. Anda mungkin bertanya-tanya bagaimana cara kerja ini di lingkungan produksi karena mungkin memiliki beberapa efek samping yang tidak diinginkan dengan gaya CSS penulis atau bahkan JavaScript-nya. Anda benar. Masuk ke DOM Bayangan.
Meskipun sebagian besar orang menganggap DOM Bayangan sebagai cara untuk melampirkan elemen "internal"
ke Elemen Khusus mereka, DOM Bayangan juga merupakan
isolasi dan primitif performa. JavaScript dan CSS tidak dapat menembus batas Shadow DOM
yang memungkinkan kita menduplikasi konten tanpa mengganggu
gaya atau logika aplikasi developer. Kita sudah memiliki elemen <div>
untuk
setiap salinan yang akan dirasterisasi dan sekarang menggunakan <div>
ini sebagai host bayangan. Kita
membuat ShadowRoot
menggunakan attachShadow({mode: 'closed'})
dan melampirkan salinan
konten ke ShadowRoot
, bukan <div>
itu sendiri. Kita harus memastikan
untuk juga menyalin semua stylesheet ke ShadowRoot
untuk menjamin bahwa
salinan kita diberi gaya dengan cara yang sama seperti aslinya.
Beberapa browser tidak mendukung Shadow DOM v1, dan untuk browser tersebut, kami kembali hanya menduplikasi konten dan berharap tidak ada yang rusak. Kita dapat menggunakan polyfill Shadow DOM dengan ShadyCSS, tetapi kita tidak menerapkannya di library.
Selesai. Setelah mempelajari pipeline rendering Chrome, kami menemukan cara menganimasikan pemburaman secara efisien di seluruh browser.
Kesimpulan
Efek semacam ini tidak boleh digunakan dengan sembarangan. Karena kita menyalin elemen DOM dan memaksanya ke lapisannya sendiri, kita dapat mendorong batas perangkat tingkat rendah. Menyalin semua stylesheet ke setiap ShadowRoot
juga merupakan potensi
risiko performa, jadi Anda harus memutuskan apakah lebih suka menyesuaikan
logika dan gaya agar tidak terpengaruh oleh salinan di LightDOM
atau menggunakan teknik
ShadowDOM
kami. Namun, terkadang teknik kita mungkin merupakan investasi
yang berharga. Lihat kode di repositori GitHub kami
serta
demo dan
hubungi saya di Twitter jika ada pertanyaan.