Suka atau tidak, paralaks tetap ada. Jika digunakan dengan bijak, hal ini dapat menambah kedalaman dan kehalusan aplikasi web. Namun, masalahnya adalah menerapkan paralaks dengan cara yang efektif dapat menjadi tantangan. Dalam artikel ini, kami akan membahas solusi yang berperforma baik dan, sama pentingnya, juga berfungsi di berbagai browser.
![Ilustrasi Paralaks.](https://developer.chrome.com/static/blog/performant-parallaxing/image/parallax-illustration-330a26c14e2fc.jpg?authuser=6&hl=id)
TL;DR
- Jangan gunakan peristiwa scroll atau
background-position
untuk membuat animasi paralaks. - Menggunakan 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 drop-in, buka repo GitHub Contoh Elemen UI dan pilih Parallax helper JS. Anda dapat melihat demo langsung scroller paralaks di repo GitHub.
Paralakser soal
Untuk memulainya, mari kita lihat dua cara umum untuk mencapai efek paralaks, dan secara khusus, mengapa mereka tidak sesuai untuk tujuan kita.
Buruk: menggunakan peristiwa scroll
Persyaratan utama paralaks adalah harus digabungkan dengan scroll; untuk setiap perubahan dalam posisi scroll halaman, posisi elemen paralaks harus diperbarui. Meskipun terdengar sederhana, mekanisme penting browser modern adalah kemampuannya untuk bekerja secara asinkron. Hal ini berlaku, dalam kasus khusus kami, untuk peristiwa scroll. Di sebagian besar browser, peristiwa scroll dijalankan sebagai "upaya terbaik" dan tidak dijamin akan ditayangkan pada setiap frame animasi scroll.
Informasi penting ini memberi tahu kita alasan mengapa kita harus menghindari solusi berbasis JavaScript yang memindahkan elemen berdasarkan peristiwa scroll: JavaScript tidak menjamin bahwa paralaks akan tetap sesuai dengan posisi scroll halaman. Dalam Mobile Safari versi lama, peristiwa scroll sebenarnya ditayangkan di akhir scroll, yang membuat efek scroll berbasis JavaScript tidak dapat diberikan. Versi yang lebih baru mengirimkan peristiwa scroll selama animasi, tetapi, sama seperti Chrome, atas dasar "upaya terbaik". Jika thread utama sibuk dengan pekerjaan lain, peristiwa scroll tidak akan segera dikirim, yang berarti efek paralaks akan hilang.
Buruk: mengupdate background-position
Situasi lain yang ingin kita hindari adalah melakukan pengecatan pada setiap bingkai. Banyak solusi
yang mencoba mengubah background-position
untuk memberikan tampilan paralaks, yang
menyebabkan browser menggambar ulang bagian halaman yang terpengaruh saat men-scroll, dan hal itu
dapat cukup mahal untuk menyebabkan jank secara signifikan pada animasi.
Jika ingin memenuhi janji gerakan paralaks, kita menginginkan sesuatu yang dapat diterapkan sebagai properti yang dipercepat (yang saat ini berarti berpegang pada 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:
- Siapkan elemen pemuat untuk men-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, menerapkan terjemahan di Z, dan menskalakannya 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 proporsional dengan nilai perspektif. Anda dapat menghitung berapa banyak yang perlu ditingkatkan skalanya dengan persamaan ini: (perspektif - jarak) / perspektif. Karena kemungkinan besar kita ingin elemen paralaks menjadi paralaks, tetapi ditampilkan sesuai ukuran yang kita tulis, elemen ini perlu ditingkatkan skalanya dengan cara ini, bukan dibiarkan apa adanya.
Dalam kasus kode di atas, perspektifnya adalah 1px, dan
jarak Z parallax-child
adalah -2px. Artinya, elemen tersebut harus
diskalakan hingga 3x, yang dapat Anda lihat adalah nilai yang dimasukkan ke dalam kode:
scale(3)
.
Untuk konten yang tidak menerapkan nilai translateZ
, Anda dapat
mengganti nilai nol. Artinya, skalanya adalah (perspektif - 0) /
perspektif, yang menghasilkan nilai 1, yang berarti skalanya telah
ditingkatkan, bukan naik atau turun. Cukup berguna, sebenarnya.
Cara kerja pendekatan ini
Penting untuk menjelaskan mengapa cara ini berhasil, karena kita akan segera menggunakan
pengetahuan tersebut. Scrolling secara efektif adalah transformasi, sehingga dapat
diakselerasi; sebagian besar melibatkan pergeseran lapisan dengan GPU. Dalam
scroll biasa, yaitu tanpa gagasan perspektif apa pun, scroll
terjadi 1:1 saat membandingkan elemen scroll dan turunannya.
Jika Anda men-scroll elemen ke bawah sebesar 300px
, turunannya akan diubah ke atas
dengan jumlah yang sama: 300px
.
Namun, menerapkan nilai perspektif ke elemen scroll akan mengacaukan
proses ini; tindakan ini akan mengubah matriks yang mendukung transformasi scroll.
Sekarang scroll 300 px hanya dapat memindahkan turunannya 150 px, bergantung pada
nilai perspective
dan translateZ
yang Anda pilih. Jika memiliki
nilai translateZ
0, elemen akan di-scroll pada rasio 1:1 (seperti sebelumnya), tetapi turunan
yang didorong dari Z jauh dari asal perspektif akan di-scroll dengan kecepatan
yang berbeda. Hasil akhirnya: 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
.
Lalat dengan salep: Mobile Safari
Ada peringatan untuk setiap efek, dan salah satu hal penting untuk transformasi adalah tentang pelestarian efek 3D pada elemen turunan. Jika ada elemen dalam hierarki antara elemen dengan perspektif dan turunannya, perspektif 3D akan "diratakan", yang berarti efeknya akan hilang.
<div class="container">
<div class="parallax-container">
<div class="parallax-child"></div>
</div>
</div>
Dalam HTML di atas, .parallax-container
bersifat baru, dan akan secara efektif
meratakan nilai perspective
dan kita akan kehilangan efek paralaks. Solusinya,
pada umumnya, cukup mudah: Anda menambahkan transform-style: preserve-3d
ke elemen, yang menyebabkannya menyebarkan efek 3D (seperti nilai
perspektif kita) yang telah diterapkan lebih jauh ke atas pohon.
.parallax-container {
transform-style: preserve-3d;
}
Namun, dalam kasus Mobile Safari, segalanya menjadi sedikit lebih rumit.
Menerapkan overflow-y: scroll
ke elemen container secara teknis berfungsi, tetapi
harus dapat mengayunkan elemen scroll. Solusinya adalah menambahkan
-webkit-overflow-scrolling: touch
, tetapi juga akan meratakan perspective
dan kita tidak akan mendapatkan paralaks apa pun.
Dari sudut pandang progressive enhancement, hal ini mungkin bukan masalah yang biasa. Jika kami tidak dapat menggunakan paralaks dalam setiap situasi, aplikasi kami akan tetap berfungsi, tetapi akan lebih baik untuk mencari solusinya.
position: sticky
menolong!
Sebenarnya, ada beberapa bantuan dalam bentuk position: sticky
, yang ada untuk memungkinkan elemen "menempel" di bagian atas area pandang atau elemen induk tertentu selama scroll. Spesifikasinya, seperti kebanyakan dari mereka, cukup lumayan, tetapi berisi
permata kecil yang berguna di dalamnya:
Sekilas mungkin tidak terlihat berarti, tetapi poin penting dalam kalimat tersebut adalah ketika mengacu pada bagaimana tepatnya, tingkat kelekatan suatu elemen dihitung: "offset dihitung dengan mengacu pada ancestor terdekat dengan kotak scroll". Dengan kata lain, jarak untuk memindahkan elemen melekat (agar elemen tersebut tampak melekat pada elemen lain atau area tampilan) dihitung sebelum transformasi lainnya 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 secara efektif "membalikkan" efek perataan -webkit-overflow-scrolling:
touch
. Hal ini memastikan bahwa elemen paralaks mereferensikan ancestor
terdekat dengan kotak scroll, yang dalam hal ini adalah .container
. Kemudian,
mirip dengan 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 Mobile Safari, yang tentu saja merupakan berita yang sangat baik!
Peringatan penempatan melekat
Namun, ada perbedaan di sini: position: sticky
memang mengubah
mekanisme paralaks. Pemosisian melekat mencoba menempelkan elemen ke
penampung scroll, sedangkan versi yang tidak melekat tidak. Ini berarti bahwa
paralaks dengan melekat pada akhirnya menjadi kebalikan dari paralaks tanpa:
- Dengan
position: sticky
, semakin dekat elemen ke z=0 less yang dipindahkannya. - Tanpa
position: sticky
, semakin dekat elemen ke z=0, semakin banyak gerakannya dilakukan.
Jika semua tampak agak abstrak, lihat demo ini oleh Robert Flack, yang menunjukkan perilaku berbeda dengan dan tanpa posisi yang melekat. Untuk melihat perbedaannya, Anda memerlukan Chrome Canary (yang merupakan versi 56 pada saat penulisan) atau Safari.
![Screenshot perspektif Paralaks](https://developer.chrome.com/static/blog/performant-parallaxing/image/parallax-perspective-scre-e5a0b46e4d2ad.jpg?authuser=6&hl=id)
Demo oleh Robert Flack yang menunjukkan pengaruh
position: sticky
terhadap scroll paralaks.
Berbagai macam bug dan solusi
Namun, seperti biasa, masih ada benjolan yang perlu dihaluskan:
- Dukungan melekat tidak konsisten. Dukungan masih diterapkan di
Chrome, Edge tidak memiliki dukungan sepenuhnya, dan Firefox memiliki cat bug saat sticky digabungkan dengan transformasi perspektif. Dalam kasus
seperti ini, ada baiknya menambahkan sedikit kode untuk hanya menambahkan
position: sticky
(versi berawalan-webkit-
) saat diperlukan, yang khusus untuk Mobile Safari. - Efeknya tidak "langsung berfungsi" di Edge. Edge mencoba menangani scroll di tingkat OS, yang umumnya merupakan hal yang baik, tetapi dalam hal ini, Edge mencegahnya mendeteksi perubahan perspektif selama scroll. Untuk memperbaikinya, Anda dapat menambahkan elemen posisi tetap, karena tindakan ini akan mengalihkan Edge ke metode scroll non-OS, dan memastikan bahwa elemen ini memperhitungkan perubahan perspektif.
- "Konten halaman menjadi sangat besar!" Banyak browser yang memperhitungkan skala saat menentukan seberapa besar konten halaman, tetapi sayangnya Chrome dan Safari tidak memperhitungkan perspektif. Jadi,
jika ada - misalnya - skala 3x yang diterapkan pada suatu elemen, Anda mungkin
akan melihat scroll bar dan sejenisnya, meskipun elemen tersebut 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 "area negatif" (biasanya kiri atas) area yang dapat di-scroll; wilayah yang dapat di-scroll tidak pernah mengizinkan Anda melihat atau men-scroll ke konten di area negatif.
Kesimpulan
Paralaks adalah efek yang menyenangkan jika digunakan dengan bijak. Seperti yang Anda lihat, Anda dapat menerapkannya dengan cara yang berperforma tinggi, terkait scroll, dan lintas browser. Karena memerlukan sedikit repo secara matematis, serta sejumlah kecil boilerplate untuk mendapatkan efek yang diinginkan, kami telah menggabungkan library helper dan contoh yang kecil, yang dapat Anda temukan di repo GitHub Contoh Elemen UI kami.
Silakan coba, dan beri tahu kami cara Anda melakukannya.