Suka atau tidak, paralaks akan tetap ada. Jika digunakan dengan cermat, parallax dapat menambahkan kedalaman dan kehalusan ke aplikasi web. Namun, masalahnya adalah menerapkan parallax dengan cara yang berperforma tinggi bisa menjadi tantangan. Dalam artikel ini, kita akan membahas solusi yang berperforma tinggi dan, yang sama pentingnya, berfungsi lintas browser.
TL;DR
- Jangan gunakan peristiwa scroll atau
background-position
untuk membuat animasi paralaks. - Gunakan transformasi 3D CSS untuk membuat efek paralaks yang lebih akurat.
- Untuk Mobile Safari, gunakan
position: sticky
untuk memastikan efek paralaks disebarkan.
Jika Anda menginginkan solusi siap pakai, buka repo GitHub Contoh Elemen UI dan dapatkan JS helper Parallax. Anda dapat melihat demo langsung scroll parallax di repo GitHub.
Paralaks masalah
Untuk memulai, mari kita lihat dua cara umum untuk mendapatkan efek paralaks, dan khususnya, alasan keduanya tidak sesuai untuk tujuan kita.
Buruk: menggunakan peristiwa scroll
Persyaratan utama paralaks adalah harus dikaitkan dengan scroll; untuk setiap perubahan pada posisi scroll halaman, posisi elemen paralaks harus diperbarui. Meskipun terdengar sederhana, mekanisme penting browser modern adalah kemampuannya untuk bekerja secara asinkron. Dalam kasus tertentu, hal ini berlaku untuk peristiwa scroll. Di sebagian besar browser, peristiwa scroll dikirim sebagai "upaya terbaik" dan tidak dijamin akan dikirim di setiap frame animasi scroll.
Informasi penting ini memberi tahu kita mengapa kita perlu menghindari solusi berbasis JavaScript yang memindahkan elemen berdasarkan peristiwa scroll: JavaScript tidak menjamin bahwa paralaks akan tetap selaras dengan posisi scroll halaman. Di Mobile Safari versi lama, peristiwa scroll sebenarnya dikirimkan di akhir scroll, sehingga tidak memungkinkan untuk membuat efek scroll berbasis JavaScript. Versi yang lebih baru memang mengirimkan peristiwa scroll selama animasi, tetapi, seperti halnya Chrome, berdasarkan "upaya terbaik". Jika thread utama sibuk dengan tugas lain, peristiwa scroll tidak akan segera dikirim, yang berarti efek paralaks akan hilang.
Buruk: mengupdate background-position
Situasi lain yang ingin kita hindari adalah menggambar pada setiap frame. Banyak solusi
mencoba mengubah background-position
untuk memberikan tampilan paralaks, yang
menyebabkan browser mengecat ulang bagian halaman yang terpengaruh saat men-scroll, dan hal itu
dapat cukup mahal untuk menyebabkan jank animasi secara signifikan.
Jika ingin memenuhi janji gerakan paralaks, kita menginginkan sesuatu yang dapat diterapkan sebagai properti yang dipercepat (yang saat ini berarti tetap menggunakan transformasi dan opasitas), dan yang tidak bergantung pada peristiwa scroll.
CSS dalam 3D
Scott Kellum dan Keith Clark telah melakukan pekerjaan yang signifikan di bidang penggunaan CSS 3D untuk mencapai gerakan paralaks, dan teknik yang mereka gunakan secara efektif adalah ini:
- Siapkan elemen penampung untuk di-scroll dengan
overflow-y: scroll
(dan mungkinoverflow-x: hidden
). - Untuk elemen yang sama, terapkan nilai
perspective
, danperspective-origin
ditetapkan ketop left
, atau0 0
. - Untuk turunan elemen tersebut, terapkan terjemahan dalam Z, dan skalakan kembali untuk memberikan gerakan paralaks tanpa memengaruhi ukurannya di layar.
CSS untuk pendekatan ini terlihat seperti ini:
.container {
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: scroll;
perspective: 1px;
perspective-origin: 0 0;
}
.parallax-child {
transform-origin: 0 0;
transform: translateZ(-2px) scale(3);
}
Yang mengasumsikan cuplikan HTML seperti ini:
<div class="container">
<div class="parallax-child"></div>
</div>
Menyesuaikan skala untuk perspektif
Mendorong elemen turunan kembali akan menyebabkannya menjadi lebih kecil secara proporsional dengan nilai perspektif. Anda dapat menghitung seberapa besar perlunya penskalaan dengan persamaan ini: (perspektif - jarak) / perspektif. Karena kita kemungkinan besar ingin elemen paralaks menjadi paralaks, tetapi muncul pada ukuran yang kita buat, elemen tersebut harus diskalakan dengan cara ini, bukan dibiarkan apa adanya.
Untuk kode di atas, perspektifnya adalah 1 piksel, dan jarak Z parallax-child
adalah -2 piksel. Artinya, elemen harus
disetel skalanya sebesar 3x, yang dapat Anda lihat adalah nilai yang dimasukkan ke dalam kode:
scale(3)
.
Untuk konten apa pun yang tidak menerapkan nilai translateZ
, Anda dapat
mengganti nilai nol. Artinya, skalanya adalah (perspektif - 0) / perspektif, yang menghasilkan nilai 1, yang berarti skalanya tidak ditingkatkan atau diturunkan. Cukup praktis.
Cara kerja pendekatan ini
Penting untuk mengetahui alasan hal ini berhasil, karena kita akan segera menggunakan pengetahuan
tersebut. Scrolling secara efektif merupakan transformasi, itulah sebabnya scrolling dapat
dipercepat; sebagian besar melibatkan pergeseran lapisan dengan GPU. Dalam
scroll biasa, yang tidak memiliki konsep perspektif, scroll
terjadi dengan cara 1:1 saat membandingkan elemen scroll dan turunannya.
Jika Anda men-scroll elemen ke bawah sebesar 300px
, turunannya akan ditransformasikan ke atas
dengan jumlah yang sama: 300px
.
Namun, menerapkan nilai perspektif ke elemen scroll akan mengacaukan
proses ini; hal ini akan mengubah matriks yang mendukung transformasi scroll.
Sekarang, scroll 300 piksel hanya dapat memindahkan turunan sebesar 150 piksel, bergantung pada
nilai perspective
dan translateZ
yang Anda pilih. Jika elemen memiliki
nilai translateZ
0, elemen tersebut akan di-scroll dengan kecepatan 1:1 (seperti biasa), tetapi turunan
yang didorong dalam Z dari asal perspektif akan di-scroll dengan kecepatan
yang berbeda. Hasil bersih: gerakan paralaks. Dan, yang sangat penting, hal ini ditangani sebagai
bagian dari mesin scroll internal browser secara otomatis, yang berarti tidak
perlu memproses peristiwa scroll
atau mengubah background-position
.
Masalah kecil: Safari Seluler
Ada batasan untuk setiap efek, dan salah satu yang penting untuk transformasi adalah pelestarian efek 3D ke elemen turunan. Jika ada elemen dalam hierarki antara elemen dengan perspektif dan turunan paralaksnya, perspektif 3D akan "diratakan", yang berarti efeknya hilang.
<div class="container">
<div class="parallax-container">
<div class="parallax-child"></div>
</div>
</div>
Dalam HTML di atas, .parallax-container
adalah baru, dan akan secara efektif
meratakan nilai perspective
dan kita kehilangan efek paralaks. Solusi,
dalam sebagian besar kasus, cukup mudah: Anda menambahkan transform-style: preserve-3d
ke elemen, sehingga elemen tersebut menyebarkan efek 3D apa pun (seperti nilai perspektif
kami) yang telah diterapkan lebih jauh ke atas hierarki.
.parallax-container {
transform-style: preserve-3d;
}
Namun, dalam kasus Mobile Safari, semuanya sedikit lebih rumit.
Menerapkan overflow-y: scroll
ke elemen penampung secara teknis berfungsi, tetapi dengan
biaya yang dapat digunakan untuk melemparkan elemen scroll. Solusinya adalah menambahkan
-webkit-overflow-scrolling: touch
, tetapi hal ini juga akan meratakan perspective
dan kita tidak akan mendapatkan paralaks.
Dari sudut pandang progressive enhancement, hal ini mungkin tidak terlalu menjadi masalah. Jika kita tidak dapat menerapkan paralaks dalam setiap situasi, aplikasi kita akan tetap berfungsi, tetapi akan lebih baik jika kita menemukan solusinya.
position: sticky
datang untuk menyelamatkan.
Sebenarnya, ada beberapa bantuan dalam bentuk position: sticky
, yang ada untuk
memungkinkan elemen "melekat" ke bagian atas area pandang atau elemen induk tertentu
selama scroll. Spesifikasinya, seperti sebagian besar spesifikasi, cukup berat, tetapi berisi
permata kecil yang berguna:
Ini mungkin tidak tampak berarti pada pandangan pertama, tetapi poin utama dalam kalimat tersebut adalah saat merujuk ke cara persis penghitungan daya lekat elemen: "offset dihitung dengan referensi ke ancestor terdekat dengan kotak scroll". Dengan kata lain, jarak untuk memindahkan elemen melekat (agar muncul terpasang ke elemen lain atau area pandang) dihitung sebelum transformasi lain diterapkan, bukan setelah. Artinya, seperti contoh scroll sebelumnya, jika offset dihitung pada 300 piksel, ada peluang baru untuk menggunakan perspektif (atau transformasi lainnya) untuk memanipulasi nilai offset 300 piksel tersebut sebelum diterapkan ke elemen melekat.
Dengan menerapkan position: -webkit-sticky
ke elemen paralaks, kita dapat "membalikan" efek perataan -webkit-overflow-scrolling:
touch
secara efektif. Hal ini memastikan bahwa elemen paralaks mereferensikan ancestor terdekat
dengan kotak scroll, yang dalam hal ini adalah .container
. Kemudian,
seperti sebelumnya, .parallax-container
menerapkan nilai perspective
,
yang mengubah offset scroll yang dihitung dan membuat efek paralaks.
<div class="container">
<div class="parallax-container">
<div class="parallax-child"></div>
</div>
</div>
.container {
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
}
.parallax-container {
perspective: 1px;
}
.parallax-child {
position: -webkit-sticky;
top: 0px;
transform: translate(-2px) scale(3);
}
Tindakan ini akan memulihkan efek paralaks untuk Safari Seluler, yang merupakan berita baik sekaligus.
Catatan pemosisian melekat
Namun, ada perbedaan di sini: position: sticky
memang mengubah
mekanika paralaks. Pemosisi melekat mencoba, ya, melekat elemen ke
penampung scroll, sedangkan versi non-melekat tidak. Ini berarti bahwa
parallax dengan sticky akan menjadi kebalikan dari parallax tanpa:
- Dengan
position: sticky
, semakin dekat elemen ke z=0, elemen tersebut akan semakin sedikit bergerak. - Tanpa
position: sticky
, semakin dekat elemen ke z=0, semakin elemen tersebut bergerak.
Jika semua hal tersebut tampak sedikit abstrak, lihat demo ini oleh Robert Flack, yang menunjukkan bagaimana elemen berperilaku secara berbeda dengan dan tanpa pemosisi melekat. Untuk melihat perbedaannya, Anda memerlukan Chrome Canary (yang merupakan versi 56 saat artikel ini ditulis) atau Safari.
Demo oleh Robert Flack yang menunjukkan pengaruh
position: sticky
terhadap scroll paralaks.
Berbagai bug dan solusi
Namun, seperti halnya hal lainnya, masih ada beberapa masalah yang perlu diperbaiki:
- Dukungan sticky tidak konsisten. Dukungan masih diimplementasikan di
Chrome, Edge tidak memiliki dukungan sama sekali, dan Firefox memiliki bug gambar saat sticky digabungkan dengan transformasi perspektif. Dalam kasus
tersebut, sebaiknya tambahkan sedikit kode untuk hanya menambahkan
position: sticky
(versi dengan awalan-webkit-
) jika diperlukan, yang hanya untuk Safari Seluler. - Efek ini tidak "langsung berfungsi" di Edge. Edge mencoba menangani scroll di tingkat OS, yang umumnya merupakan hal yang baik, tetapi dalam hal ini mencegahnya mendeteksi perubahan perspektif selama scroll. Untuk memperbaikinya, Anda dapat menambahkan elemen posisi tetap, karena tampaknya mengalihkan Edge ke metode scroll non-OS, dan memastikan bahwa elemen tersebut memperhitungkan perubahan perspektif.
- "Konten halaman menjadi sangat besar!" Banyak browser yang memperhitungkan skala
saat menentukan ukuran konten halaman, tetapi sayangnya Chrome dan Safari
tidak memperhitungkan perspektif. Jadi,
jika ada - misalnya - skala 3x yang diterapkan ke elemen, Anda mungkin
melihat scrollbar dan sejenisnya, meskipun elemen berada pada 1x setelah
perspective
diterapkan. Anda dapat mengatasi masalah ini dengan menskalakan elemen dari sudut kanan bawah (dengantransform-origin: bottom right
), yang berfungsi karena akan menyebabkan elemen yang terlalu besar tumbuh ke dalam "wilayah negatif" (biasanya kiri atas) area yang dapat di-scroll; wilayah yang dapat di-scroll tidak akan pernah memungkinkan Anda melihat atau men-scroll ke konten di wilayah negatif.
Kesimpulan
Parallax adalah efek yang menyenangkan jika digunakan dengan cermat. Seperti yang dapat Anda lihat, Anda dapat menerapkan dengan cara yang berperforma tinggi, dikaitkan dengan scroll, dan lintas browser. Karena memerlukan sedikit manipulasi matematika, dan sedikit boilerplate untuk mendapatkan efek yang diinginkan, kami telah menggabungkan library dan contoh helper kecil, yang dapat Anda temukan di repositori GitHub Contoh Elemen UI kami.
Coba gunakan, dan beri tahu kami hasilnya.