Suka atau tidak, paralaks tetap ada. Ketika digunakan dengan bijak, ini dapat menambahkan kedalaman dan kehalusan ke aplikasi web. Namun, masalahnya adalah menerapkan paralaks dengan cara yang baik bisa menjadi tantangan. Dalam artikel ini, kami akan mendiskusikan solusi yang berperforma baik dan, yang sama pentingnya, lintas browser.
![Ilustrasi Paralaks.](https://developer.chrome.google.cn/static/blog/performant-parallaxing/image/parallax-illustration-330a26c14e2fc.jpg?authuser=7&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
guna memastikan bahwa efek paralaks akan disebarluaskan.
Jika Anda menginginkan solusi drop-in, buka repo GitHub Contoh Elemen UI dan ambil JS helper Paralaks. Anda dapat melihat demo langsung scroller paralaks di repositori GitHub ASL.
Paralakser soal
Sebagai permulaan, mari kita lihat dua cara umum untuk mencapai paralaks mempengaruhi, dan khususnya, mengapa mereka tidak sesuai dengan tujuan kita.
Buruk: menggunakan peristiwa scroll
Persyaratan utama dari paralaks adalah harus di-scroll dan digabungkan; untuk setiap perubahan di posisi scroll halaman, elemen seharusnya diperbarui. Meskipun kedengarannya sederhana, mekanisme penting dari browser modern adalah kemampuannya untuk bekerja secara asinkron. Hal ini berlaku, dalam kasus tertentu, untuk menggulir peristiwa. Di sebagian besar browser, peristiwa scroll ditayangkan sebagai "upaya terbaik" dan tidak dijamin akan ditayangkan pada setiap frame animasi scroll!
Informasi penting ini memberitahu 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. Dalam Mobile Safari versi lama, aktivitas scroll ditempatkan di akhir scroll, sehingga mustahil untuk Efek scroll berbasis JavaScript. Versi terbaru lainnya mengirimkan peristiwa scroll selama animasi, tetapi, sama seperti Chrome, berdasarkan "upaya terbaik" layanan. Jika thread utama sibuk dengan pekerjaan lain, peristiwa scroll tidak akan dikirimkan segera, yang berarti efek paralaks akan hilang.
Buruk: mengupdate background-position
Situasi lain yang ingin kita hindari adalah melakukan pengecatan pada setiap bingkai. Banyak solusi
mencoba mengubah background-position
untuk memberikan tampilan paralaks, yang
menyebabkan browser mengecat ulang bagian laman yang terpengaruh saat menggulir, dan itu
cukup mahal sehingga bisa menyebabkan jank pada animasi secara signifikan.
Jika kita ingin memenuhi janji gerakan paralaks, kita menginginkan sesuatu yang dapat diterapkan sebagai properti yang dipercepat (yang saat ini berarti berpegang pada transform dan opasitas), dan yang tidak bergantung pada peristiwa scroll.
CSS dalam 3D
Scott Kellum dan Keith Clark memiliki melakukan pekerjaan yang signifikan di bidang penggunaan CSS 3D untuk mencapai gerakan paralaks, dan teknik yang mereka gunakan adalah ini secara efektif:
- Menyiapkan elemen pemuat untuk men-scroll dengan
overflow-y: scroll
(dan mungkinoverflow-x: hidden
). - Untuk elemen yang sama tersebut, terapkan nilai
perspective
, danperspective-origin
ditetapkan ketop left
, atau0 0
. - Untuk turunan elemen tersebut menerapkan terjemahan dalam Z, dan menurunkan skalanya hingga menyediakan gerakan paralaks tanpa mempengaruhi 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 membuatnya 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 muncul sesuai ukuran yang kita tulis, perlu ditingkatkan skalanya dengan cara seperti ini, bukan dibiarkan apa adanya.
Dalam kasus kode di atas, perspektifnya adalah 1px, dan
Jarak Z parallax-child
adalah -2px. Ini berarti bahwa elemen itu akan membutuhkan
ditingkatkan skalanya 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. Ini berarti skalanya adalah (perspektif - 0) /
perspektif, yang menghasilkan nilai 1, yang berarti telah diskalakan
tidak ke atas
atau pun ke bawah. Cukup berguna, sebenarnya.
Cara kerja pendekatan ini
Penting untuk menjelaskan mengapa cara ini berhasil, karena kita akan menggunakannya
mengetahui informasi terbaru. Scrolling secara efektif adalah transformasi, itulah sebabnya
accelerated; kemungkinan besar prosesnya melibatkan pemindahan lapisan dengan GPU. Di
pengguliran biasa, yaitu gulir tanpa
perspektif apa pun, menggulir
terjadi secara 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
.
Akan tetapi, menerapkan nilai perspektif ke elemen {i>scrolling<i} akan mengacaukan
dalam proses ini; ini mengubah matriks yang
mendukung transformasi scroll.
Sekarang gulungan 300px hanya dapat memindahkan anak-anak sebesar 150px, tergantung pada
Nilai perspective
dan translateZ
yang Anda pilih. Jika suatu elemen memiliki
Nilai translateZ
0, ini akan di-scroll pada 1:1 (seperti sebelumnya), tetapi turunan
yang didorong di Z dari titik perspektif akan di-scroll dengan
rating! Hasil akhirnya: gerakan paralaks. Dan, yang sangat penting, ini ditangani sebagai
bagian dari mesin scroll internal browser secara otomatis, yang berarti ada
tidak perlu memproses peristiwa scroll
atau mengubah background-position
.
Lalat dengan salep: Mobile Safari
Ada catatan untuk setiap efek, dan satu yang penting untuk transformasi adalah mempertahankan efek 3D pada elemen turunan. Jika ada elemen pada hierarki antara elemen dengan perspektif dan turunan paralaksnya, perspektif 3D menjadi "rata", artinya efeknya akan hilang.
<div class="container">
<div class="parallax-container">
<div class="parallax-child"></div>
</div>
</div>
Pada HTML di atas, .parallax-container
masih baru dan akan secara efektif
meratakan nilai perspective
dan kita kehilangan efek paralaks. Solusi,
Biasanya, cukup mudah: Anda menambahkan transform-style: preserve-3d
ke elemen, menyebabkannya menyebarkan efek 3D apa pun (seperti perspektif kita
nilai) 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 pada
biaya untuk dapat mengayunkan elemen {i>scrolling<i}. Solusinya adalah dengan menambahkan
-webkit-overflow-scrolling: touch
, tetapi juga akan meratakan perspective
dan kita tidak akan mendapatkan
paralaks apa pun.
Dari sudut pandang {i>progressive enhancement<i}, hal ini mungkin tidak terlalu masalah performa. Jika kami tidak dapat menggunakan paralaks dalam setiap situasi, aplikasi kami akan tetap berfungsi, tetapi ada baiknya untuk mencari solusi.
position: sticky
menolong!
Sebenarnya, ada beberapa bantuan dalam bentuk position: sticky
, yang ada untuk
izinkan elemen untuk "melekat" ke bagian atas area pandang atau elemen induk tertentu
selama scroll. Spesifikasinya, seperti kebanyakan, cukup lumayan, tetapi berisi
permata kecil yang sangat membantu di dalam:
Ini mungkin tidak terlihat berarti banyak sekali, tetapi merupakan poin penting dalam kalimat itu adalah ketika mengacu pada bagaimana, tepatnya, kelekatan suatu elemen dihitung: "offset dihitung dengan mengacu pada ancestor terdekat dengan kotak scroll". Dengan kata lain, jarak untuk memindahkan elemen melekat (agar tampak melekat pada elemen lain atau area pandang) adalah dihitung sebelum transformasi lainnya diterapkan, bukan setelah. Artinya sangat mirip dengan contoh scroll sebelumnya, jika offset dihitung dengan ukuran 300px, ada peluang baru untuk menggunakan perspektif (atau transformasi lainnya) untuk memanipulasi nilai offset 300 px tersebut sebelum diterapkan ke yang kurang penting.
Dengan menerapkan position: -webkit-sticky
ke elemen paralaks, kita dapat
yang efektif "membalikkan" efek perataan -webkit-overflow-scrolling:
touch
. Ini memastikan bahwa elemen paralaks mereferensikan nilai terdekat
ancestor dengan kotak scroll, yang dalam hal ini adalah .container
. Lalu:
sama 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);
}
Ini akan memulihkan efek paralaks untuk Mobile Safari, yang merupakan berita baik bulat!
Peringatan penempatan melekat
Namun, ada perbedaan di sini: position: sticky
memang mengubah
mekanika paralaks. Pemosisian melekat mencoba menempelkan elemen ke
container scroll, sedangkan versi yang tidak melekat tidak. Ini berarti bahwa
paralaks dengan melekat pada akhirnya menjadi kebalikan dari tanpa:
- Dengan
position: sticky
, semakin dekat elemen ke z=0 jika lebih sedikit elemen tersebut bergerak. - Tanpa
position: sticky
, semakin dekat elemen ke z=0 lebih banyak ia bergerak.
Jika semua itu tampak agak abstrak, lihat demo ini oleh Robert Flack, yang menunjukkan bagaimana elemen berperilaku berbeda dengan dan tanpa elemen melekat pemosisian. Untuk melihat perbedaannya, Anda memerlukan Chrome Canary (yaitu versi 56 pada saat penulisan ini) atau Safari.
![Screenshot perspektif Paralaks](https://developer.chrome.google.cn/static/blog/performant-parallaxing/image/parallax-perspective-scre-e5a0b46e4d2ad.jpg?authuser=7&hl=id)
Demo oleh Robert Flack yang menunjukkan bagaimana
position: sticky
memengaruhi scroll paralaks.
Berbagai macam bug dan solusi
Namun, seperti biasa, masih ada benjolan dan rintangan yang perlu dihaluskan:
- Dukungan melekat tidak konsisten. Dukungan masih diterapkan di
Chrome, Edge tidak memiliki dukungan sepenuhnya, dan Firefox memiliki bug pengecatan saat melekat digabungkan dengan transformasi perspektif. Dengan demikian
kasus tersebut, sebaiknya tambahkan sedikit kode untuk hanya menambahkan
position: sticky
( versi berawalan-webkit-
) jika diperlukan, yaitu untuk Mobile Safari saja. - Efeknya tidak "langsung berfungsi" di Edge. Edge mencoba menangani pengguliran tingkat OS, yang umumnya merupakan hal baik, tetapi dalam hal ini hal itu mencegahnya agar tidak 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 perubahan tersebut diperhitungkan oleh perubahan perspektif.
- "Konten halaman menjadi sangat besar!" Banyak {i>browser<i} yang
mempertimbangkan skala ini
saat menentukan seberapa besar ukuran konten halaman, tetapi sayangnya Chrome dan Safari
tidak memperhitungkan perspektif. Namun
jika ada - misalnya - skala 3x yang diterapkan pada suatu elemen, Anda mungkin
kita akan melihat bilah gulir dan sejenisnya, bahkan jika elemen berada pada 1x setelah
perspective
telah diterapkan. Kita 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 menjadi "area negatif" (biasanya kiri atas) area yang dapat di-scroll; dapat di-scroll wilayah tidak memungkinkan Anda melihat atau men-scroll ke konten di wilayah negatif.
Kesimpulan
Paralaks adalah efek yang menyenangkan jika digunakan dengan bijak. Seperti yang Anda lihat, mungkin saja untuk menerapkannya dengan cara yang berperforma tinggi, ditambah scroll, dan lintas browser. Karena cara ini membutuhkan sedikit perubahan matematika, dan sedikit perubahan boilerplate untuk mendapatkan efek yang diinginkan, kami telah menyelesaikan sebuah library helper kecil dan contoh, yang dapat Anda temukan di repo GitHub Contoh Elemen UI.
Silakan coba, dan beri tahu kami cara Anda melakukannya.