4 trik yang membantu kami mengoptimalkan frontend

Animasi yang indah telah lama menjadi tren tren desain. Desainer UI membuat komidi putar yang rumit, unduhan, animasi menu, dan dekorasi lainnya, dan pengembang antarmuka menerjemahkannya menjadi kode. Tapi situs itu seharusnya tidak hanya terlihat bagus, tetapi juga berfungsi dengan cepat.


Frontend modern harus mengoptimalkan kodenya. Ini terutama berlaku untuk produk-produk di mana sebagian besar audiens pergi ke situs dari perangkat seluler. Beberapa metode animasi tertinggal bahkan di Chrome pada komputer papan atas, tetapi harus berfungsi dengan lancar pada smartphone biasa.


Pengembang kami menggunakan sejumlah besar teknik yang membantu mengoptimalkan situs dan mempercepat pekerjaannya. Saya mengumpulkan 4 yang paling menarik dari mereka. Kami berbagi pengetahuan yang akan berguna bagi pemula dan profesional, serta memberikan tautan ke tutorial yang bermanfaat.



1. Animasi di SCSS


Situs ini memiliki banyak animasi. Kami membutuhkan browser untuk memainkannya dengan frame rate stabil 60 fps. Dalam CSS murni, ini sulit dilakukan, jadi kami menggunakan SCSS.


Untuk membuat bilah geser, kami menggunakan pustaka Swiper . Untuk penggeser horizontal, penggunaan pustaka ini dibenarkan, karena kami perlu memberikan dukungan untuk svayp dari pengguna. Tetapi untuk korsel tak berujung vertikal, Swiper tidak diperlukan, tidak ada interaksi pengguna. Karena itu, kami ingin mengulangi fungsionalitas yang sama hanya menggunakan fitur CSS.


Kondisi pertama dan paling penting ketika bekerja dengan animasi CSS adalah hanya menggunakan properti transform dan opacity. Browser dapat secara mandiri mengoptimalkan animasi dari properti ini dan menghasilkan 60 fps yang stabil. Namun, menggunakan @keyframe saja tidak mungkin untuk menulis animasi yang berbeda untuk elemen yang berbeda, dan menjiwai setiap elemen secara individual dalam CSS murni terlalu memakan waktu. Bagaimana cara cepat menulis animasi yang kita butuhkan? Kami memilih SCSS, dialek SASS - ekstensi CSS yang lebih fungsional.


Mari kita menganalisis penggunaan SCSS menggunakan contoh slider vertikal kami.



Yang kami miliki ada wadah yang tingginya sama dengan tinggi tiga elemen korsel. Di dalamnya ada wadah lain yang berisi semua elemen korsel.


<div class="b-vertical-carousel-slider"> <div class="vertical-carousel-slider-wrapper slider-items"> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> </div> </div> 

Kami menghapus visibilitas elemen kontainer yang akan melampaui itu dan mengatur ketinggian blok.


 .b-vertical-carousel-slider { position: relative; overflow: hidden; height: $itemHeight * 3; .vertical-carousel-slider-item { height: $itemHeight; } } 

Perhitungan animasi berubah hanya jika jumlah elemen dalam korsel berubah. Selanjutnya, kami menulis mixin yang mengambil satu parameter input - $itemCount


 @mixin verticalSlideAnimation($itemCount) { } 

Di mixin, buat keyframe untuk setiap elemen, atur status awal untuk itu, dan gunakan :nth-child menentukan animasi untuk elemen.


 for $i from * 1 through $itemCount { $animationName: carousel-item-#{$itemCount}-#{$i}; @keyframes #{$animationName} { 0% { transform: translate3d(0, 0, 0) scale(.95); } } .vertical-carousel-slider-item:nchild(#{$i}) { animation: $stepDuration * $itemCount $animation ease infinite; } } 

Selanjutnya, selama animasi, kita hanya akan memindahkan elemen di sepanjang sumbu y dan mengubah skala untuk elemen di tengah.


Status elemen korsel:


  1. Damai
  2. Y offset
  3. Y offset
  4. Y diimbangi dengan penurunan

Setiap item akan naik $itemCount kali, naik satu kali dan turun satu kali selama pergerakan. Oleh karena itu, kami akan membuat dan menghitung animasi untuk setiap gerakan ke atas.


 @keyframes #{$animationName} { 0% { transform: translate3d(0, 0, 0) scale(.95); } @for $j from 0 through $itemCount { $isFocusedStep: $i == $j + 2; $isNotPrevStep: $i != $j + 1; $offset: 100% / $itemCount * ($animationTime / $stepDuration); @if ($isFocusedStep) { #{getPercentForStep($j, $itemCount, $offset)} { transform: getTranslate($j - 1) scale(.95); } #{getPercentForStep($j, $itemCount)} { transform: getTranslate($j) scale(1); } #{getPercentForStep($j + 1, $itemCount, $offset)} { transform: getTranslate($j) scale(1); } #{getPercentForStep($j + 1, $itemCount)} { transform: getTranslate($j + 1) scale(.95); } } @else if ($isNotPrevStep) { #{getPercentForStep($j, $itemCount, $offset)} { transform: getTranslate($j - 1) scale(.95); } #{getPercentForStep($j, $itemCount)} { transform: getTranslate($j) scale(.95); } } } } 

Tetap mendefinisikan beberapa variabel dan fungsi:


  • $animationTime - gerakan tween time
  • $stepDuration - total waktu eksekusi dari satu langkah animasi ( $animationTime + waktu istirahat korsel)
  • getPercentForStep($step, $itemCount, $offset) - fungsi yang mengembalikan dalam persen titik ekstrim dari salah satu negara.
  • getTranslate($step) - mengembalikan terjemahan tergantung pada langkah animasi

Contoh implementasi fungsi:


 @function getPercentForStep($step, $count, $offset: 0) { @return 100% * $step / $count - $offset; } @function getTranslate($step) { @return translate3d(0, -100% * $step, 0); } 

Kami memiliki korsel yang berfungsi dengan elemen yang meningkat di tengah. Tetap membuat bayangan di bawah elemen yang meningkat. Awalnya, setiap elemen carousel memiliki elemen pseudo: after, yang pada gilirannya memiliki bayangan. Agar tidak menganimasikan properti bayangan, kami menggunakan properti opacity untuk itu, mis. hanya menunjukkan dan menyembunyikan bayangannya.


Tetapi dalam implementasi baru untuk solusi seperti itu, Anda perlu menghasilkan banyak kerangka kunci tambahan untuk setiap elemen pseudo. Kami memutuskan untuk melakukannya dengan lebih mudah: akan ada satu blok dengan bayangan dan itu akan menempati ruang tepat di bawah elemen tengah korsel.


Tambahkan div yang bertanggung jawab atas bayangan.


 <div class="b-vertical-carousel-slider"> <div class="vertical-carousel-slider-wrapper"> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> <div class="vertical-carousel-slider-item"></div> </div> <div class="vertical-carousel-slider-shadow"></div> </div> 

Kami menyesuaikan dgn mode dan menambahkan animasi.


 @keyframes shadowAnimation { 0% { opacity: 1; } 80% { opacity: 1; } 90% { opacity: 0; } 100% { opacity: 1; } } .vertical-carousel-slider-shadow { top: $itemHeight; left: 0; right: 0; height: $itemHeight; animation: $stepDuration shadowAnimation ease infinite; } 

Dalam hal ini, Anda tidak perlu membuat dan memikirkan cara untuk menghidupkan bayangan di bawah setiap elemen, kami hanya menganimasikan satu blok, ia memiliki dua status - terlihat dan disembunyikan


Hasilnya, kami memiliki korsel CSS murni yang menjiwai hanya dengan properti yang dioptimalkan dengan baik. Ini memungkinkan browser untuk menggunakan akselerasi perangkat keras untuk rendering. Karenanya, laba nyata dibandingkan dengan animasi JS:


  1. Saat menggulir halaman dengan animasi pada perangkat yang lemah, frame dengan jelas dilompati dalam animasi JS, menjatuhkan FPS ke 15-20. Animasi CSS jelas meningkatkan hal-hal. Pada perangkat ini, angka ini setidaknya 50-55 FPS.
  2. Kami menyingkirkan pekerjaan modul pihak ketiga di mana tidak diperlukan.
  3. Animasi akan diputar bahkan ketika JS dinonaktifkan

Animasi JS harus digunakan jika kontrol ketat atas setiap frame diperlukan: jeda, putar balik, mundur, reaksi terhadap tindakan pengguna. Dalam kasus lain, kami sarankan menggunakan CSS murni.


Tautan yang bermanfaat



2. Menggunakan API Pengamat titik-temu


Animasi yang tidak dilihat pengunjung situs diputar di suatu tempat yang tidak terlihat dan memuat CPU. Menggunakan Pengamat Persimpangan, kami menentukan jenis animasi apa yang terlihat di layar sekarang, dan hanya memutarnya.


Semua trik dari paragraf terakhir dapat dikombinasikan dengan Pengamat titik-temu. Alat ini membantu untuk tidak memuat peramban dengan animasi yang tidak dilihat pengunjung situs. Sebelumnya, “pendengar” intensif sumber daya digunakan untuk memahami apakah pengunjung melihat elemen animasi dan ini tidak menghasilkan “knalpot” yang kuat. Perbedaan antara menggunakan animasi di luar viewport dan menggunakan pendengar sangat minim. Intersection Observer API membutuhkan lebih sedikit sumber daya dan membantu memainkan hanya animasi yang terlihat oleh pengunjung.


Di situs kami, animasi hanya diaktifkan ketika elemen muncul di viewport. Jika kami tidak melakukan ini, maka halaman akan kelebihan beban dengan eksekusi terus menerus dari animasi pengulangan yang tetap tidak terlihat. Intersection Observer API memungkinkan Anda untuk memantau persimpangan elemen dengan induk atau ruang lingkup dokumen.


Contoh implementasi


Sebagai contoh, kami menunjukkan cara mengoptimalkan animasi di JS. Idenya sederhana - animasi dimainkan saat elemen animasi ada di viewport. Untuk implementasi, kami menggunakan API Pengamat titik-temu.


Tambahkan penanganan kelas yang is-paused pada gaya


 .b-vertical-carousel-slider.is-paused { .vertical-carousel-slider-wrapper { .vertical-carousel-slider-item { animation-play-state: paused; } } .vertical-carousel-slider-shadow { animation-play-state: paused; } } 

Yaitu ketika kelas ini muncul, animasi akan dijeda.


Sekarang kami menjelaskan logika menambah dan menghapus kelas ini


 if (window.IntersectionObserver) { const el = document.querySelector('.b-vertical-carousel-slider'); const observer = new IntersectionObserver(intersectionObserverCallback); observer.observe(el); } 

Di sini kami membuat instance IntersectionObserver, menentukan fungsi intersectionObserverCallback , yang akan berfungsi ketika visibilitas berubah.


Sekarang tentukan intersectionObserverCallback


 function intersectionObserverCallback(entries){ if (entries[0].intersectionRatio === undefined) { return; } helperDOM.toggleClass(el, 'is-paused', entries[0].intersectionRatio <= 0); }; 

Sekarang animasi dimainkan hanya pada elemen-elemen yang terlihat. Segera setelah elemen menghilang dari bidang tampilan, animasi dijeda. Ketika pengunjung kembali kepadanya, pemutaran akan dilanjutkan.


Tautan yang bermanfaat



3. rendering SVG


Sejumlah besar gambar atau penggunaan sprite menyebabkan celah dan keterlambatan pada detik pertama setelah pemuatan. Memasukkan SVG dalam kode halaman membantu secara visual membuat pemuatan menjadi lebih lancar.


Ketika kami memilih metode untuk bekerja dengan gambar, kami memiliki 2 opsi optimisasi: menanamkan SVG dalam HTML atau menggunakan sprite. Kami memutuskan untuk menanamkan. Kami menyematkan kode XML dari setiap gambar langsung ke kode HTML halaman. Ini sedikit meningkatkan ukurannya, tetapi SVG segera disajikan sesuai dengan dokumen.


Banyak pengembang terus menggunakan sprite SVG. Apa esensi dari metode ini: array gambar (misalnya, ikon) dikumpulkan dalam kanvas gambar besar, yang disebut sprite. Ketika Anda perlu menunjukkan ikon tertentu, sprite dipanggil, setelah itu koordinat dari bagian tertentu di mana ia berada diberikan. Ini telah dilakukan sejak lama, bahkan pada versi HTTP pertama. Sprite membantu secara agresif men-cache file dan mengurangi jumlah permintaan server. Ini penting karena banyak permintaan secara bersamaan memperlambat browser. Menggunakan sprite SVG adalah penopang yang biasa digunakan untuk mengabaikan logika bekerja demi menghemat sumber daya. Sekarang jumlah permintaan tidak begitu penting, jadi kami sarankan untuk menyematkan.


Pertama-tama, ini memengaruhi kinerja secara positif dari sudut pandang pengunjung. Dia melihat bagaimana ikon dimuat secara instan dan tidak menderita detik pertama setelah memuat halaman. Saat menggunakan gambar sprite atau PNG, halaman yang memuat sedikit melambat. Ini terutama dirasakan jika pengunjung segera menggulir halaman yang dimuat - FPS akan turun menjadi 5-15 pada perangkat non-top. Memasukkan SVG dalam HTML membantu mengurangi latensi halaman (subyektif, dari sudut pandang klien) dan menyingkirkan jalur dan lompatan frame saat memuat.


4. Caching menggunakan Pekerja Layanan dan HTTP Cache


Masuk akal untuk memuat ulang halaman yang tidak berubah dan menggunakan lalu lintas pengunjung. Ada banyak strategi caching, kami memutuskan pada kombinasi yang paling efisien.


Perlu mengoptimalkan penggunaan tidak hanya CPU / GPU, tetapi juga jaringan. Perangkat seluler merupakan batasan tidak hanya dalam hal sumber daya, tetapi juga dalam hal kecepatan dan lalu lintas Internet. Caching membantu kami di sini. Ini memungkinkan Anda untuk menyimpan respons terhadap permintaan HTTP dan menggunakannya tanpa menerima respons dari server lagi.


Ketika kami mempertimbangkan strategi caching, kami memilih untuk menggunakan Pekerja Layanan dan HTTP Cache pada saat yang sama. Mari kita mulai dengan yang pertama dan lebih maju. Service Worker adalah file js yang dapat mengontrol halaman atau file, memotong dan memodifikasi permintaan, serta permintaan cache secara terprogram. Ini bertindak sebagai proksi antara situs dan server dan menentukan perilaku offline mereka. Semua ini dilakukan di "depan", tanpa menghubungkan "backend".


Pekerja Layanan memiliki variabilitas yang luar biasa. Kita dapat memprogram perilaku itu sesuka kita. Misalnya, kita tahu bahwa pengunjung yang membuka halaman nomor 1, dengan probabilitas 90% akan masuk ke halaman nomor 2. Kami meminta SW untuk memuat halaman kedua dengan latar belakang saat pengunjung masih di halaman pertama. Ketika dia pergi ke sana, halaman akan dimuat secara instan. Ini dapat digunakan untuk berbagai tugas:


  • Sinkronisasi data latar belakang
  • Kalkulator offline
  • Templating khusus
  • Reaksi terhadap waktu dan tanggal tertentu.

File Pekerja Layanan dapat dibuat di berbagai layanan. Kami merekomendasikan Workbox . Ini cukup sederhana dan memungkinkan Anda untuk membuat seperangkat aturan dengan mana caching dilakukan, misalnya, precache.


Pekerja layanan tidak mendukung semua browser, seperti IE, Safari, atau Chrome sebelum versi 40.0. Jika perangkat tidak dapat bekerja dengannya, ini mengikuti aturan cache HTTP Cache. Kami menambahkan header HTTP berikut ke halaman:


 cache-control: no-cache last-modified: Mon, 06 May 2019 04:26:29 GMT 

Dalam hal ini, browser menambahkan respons ke permintaan ke repositori, tetapi setiap permintaan berikutnya mengirimkan header untuk memeriksa perubahan.


 if-modified-since: Mon, 06 May 2019 04:26:29 GMT 

Jika tidak terjadi perubahan, browser menerima respons dengan kode 304 Tidak dimodifikasi dan menggunakan konten yang disimpan dalam cache. Jika ada perubahan pada dokumen, respons dikembalikan dengan kode 200 dan respons baru ditulis ke penyimpanan browser.


Beberapa saat kemudian, kami mengubah metode caching dan memeriksa jumlah hash dalam nama file. Ini memastikan bahwa sumber daya akan tetap unik. Karenanya, kami dapat men-cache konten secara agresif. Header berikut ditambahkan sebagai tanggapan:


 Cache-control:max-age=31536000, immutable 

Max-age menunjukkan waktu caching maksimum, dalam kasus kami adalah 1 tahun. Nilai abadi berarti bahwa jawaban seperti itu tidak perlu diperiksa untuk perubahan.


Tautan yang bermanfaat



Ini bukan cara untuk mengoptimalkan situs. Ini dapat menambahkan penolakan bootstrap, mengurangi jumlah elemen dan acara DOM, dan banyak lagi. Tetapi ini adalah tips yang membantu kami membuat situs menjadi cepat dan responsif, meskipun animasi dalam jumlah besar.


Kami mengundang Anda ke tim kami


Kami selalu mencari spesialis keren di kantor kami di St. Petersburg untuk tugas ambisius kami: pengembang, penguji, perancang. Di bawah ini ada lowongan - bergabunglah dengan kami.


Source: https://habr.com/ru/post/id455700/


All Articles