Suka atau tidak suka, paralaks akan tetap ada. Saat digunakan dengan bijak, hal ini dapat menambahkan kedalaman dan kehalusan pada aplikasi web. Namun, penerapan paralaks dengan cara yang berperforma tinggi bisa menjadi tantangan tersendiri. Dalam artikel ini, kami akan membahas solusi yang berperforma baik dan, yang sama pentingnya, berfungsi di seluruh browser.
TL;DR
- Jangan gunakan peristiwa scroll atau
background-position
untuk membuat animasi paralaks. - Menggunakan transformasi 3D CSS untuk menciptakan 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 Sampel Elemen UI dan ambil JS helper Parallax. Anda dapat melihat demo langsung scroller paralaks di repo GitHub.
Paralakser permasalahan
Untuk memulainya, mari kita lihat dua cara umum untuk mencapai efek paralaks, dan khususnya, mengapa cara tersebut tidak sesuai untuk tujuan kita.
Buruk: menggunakan peristiwa scroll
Persyaratan utama paralaks adalah parameter tersebut harus digabungkan dengan scroll; untuk setiap perubahan dalam posisi scroll halaman, posisi elemen paralaks akan diperbarui. Meskipun terdengar sederhana, mekanisme penting dari browser modern adalah kemampuannya untuk berfungsi secara asinkron. Hal ini berlaku, dalam kasus khusus kami, untuk men-scroll peristiwa. Di sebagian besar browser, peristiwa scroll ditayangkan sebagai "upaya terbaik" dan tidak dijamin akan ditayangkan pada setiap frame animasi scroll.
Informasi penting ini memberi tahu kita alasan kita perlu menghindari solusi berbasis JavaScript yang memindahkan elemen berdasarkan peristiwa scroll: JavaScript tidak menjamin bahwa paralaks akan tetap sejalan dengan posisi scroll halaman. Pada Mobile Safari versi lama, peristiwa scroll sebenarnya ditayangkan pada akhir scroll, sehingga tidak memungkinkan untuk membuat efek scroll berbasis JavaScript. Versi yang lebih baru memang menampilkan peristiwa scroll selama animasi, tetapi, seperti halnya Chrome, berdasarkan "upaya terbaik". Jika thread utama sibuk dengan pekerjaan lain, peristiwa scroll tidak akan segera dikirimkan, yang berarti efek paralaks akan hilang.
Buruk: memperbarui background-position
Situasi lain yang ingin kita hindari adalah mengecat di 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 menimbulkan biaya yang cukup mahal untuk mengacaukan animasi secara signifikan.
Jika ingin memenuhi janji gerakan paralaks, kita ingin sesuatu yang dapat diterapkan sebagai properti yang dipercepat (yang saat ini berarti tetap pada transformasi dan opasitas), dan yang tidak bergantung pada peristiwa scroll.
CSS dalam 3D
Scott Kellum dan Keith Clark telah berhasil melakukan banyak hal dalam bidang penggunaan CSS 3D untuk mencapai gerakan paralaks, dan teknik yang mereka gunakan secara efektif adalah sebagai berikut:
- Siapkan elemen penampung untuk men-scroll dengan
overflow-y: scroll
(dan mungkinoverflow-x: hidden
). - Untuk elemen yang sama, terapkan nilai
perspective
danperspective-origin
yang ditetapkan ketop left
, atau0 0
. - Terapkan terjemahan dalam Z ke turunan elemen tersebut, dan skalakan kembali untuk menyediakan gerakan paralaks tanpa memengaruhi ukurannya di layar.
CSS untuk pendekatan ini terlihat seperti berikut:
.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 sebanding 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, elemen tersebut 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, skala elemen perlu ditingkatkan sebesar 3x, yang dapat Anda lihat sebagai 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 naik atau turun. Cukup berguna.
Cara kerja pendekatan ini
Penting untuk menjelaskan alasan keberhasilan cara ini, karena kita akan segera menggunakan pengetahuan tersebut. Scrolling pada dasarnya adalah transformasi, dan karenanya dapat
dipercepat; scroll sebagian besar melibatkan pergeseran lapisan dengan GPU. Dalam
scroll standar, yaitu scroll tanpa perspektif apa pun, scroll
terjadi dalam cara 1:1 saat membandingkan elemen scroll dan turunannya.
Jika Anda men-scroll elemen ke bawah menurut 300px
, turunannya akan diubah ke atas
dengan jumlah yang sama: 300px
.
Namun, menerapkan nilai perspektif ke elemen scroll akan mengacaukan
proses ini; hal ini akan mengubah matriks yang mendasari transformasi scroll.
Sekarang, scroll 300 piksel hanya dapat memindahkan turunannya sebesar 150 piksel, bergantung pada
nilai perspective
dan translateZ
yang Anda pilih. Jika elemen memiliki
nilai translateZ
0, elemen tersebut akan di-scroll pada 1:1 (seperti biasanya), tetapi turunan
yang didorong dalam Z menjauh 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
Anda tidak perlu memproses peristiwa scroll
atau mengubah background-position
.
Lalat: Mobile Safari
Ada peringatan untuk setiap efek, dan satu hal penting untuk transformasi adalah pemeliharaan efek 3D pada 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. Solusinya,
dalam sebagian besar kasus, cukup sederhana: Anda menambahkan transform-style: preserve-3d
ke elemen, yang menyebabkannya menyebarkan efek 3D (seperti nilai perspektif kita) yang telah diterapkan ke hierarki lebih jauh.
.parallax-container {
transform-style: preserve-3d;
}
Namun, dalam kasus Mobile Safari, semuanya sedikit lebih rumit.
Menerapkan overflow-y: scroll
ke elemen container secara teknis berfungsi, tetapi
Anda harus dapat melempar elemen scroll. Solusinya adalah dengan menambahkan
-webkit-overflow-scrolling: touch
, tetapi tindakan ini 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. Jika kita tidak dapat membuat paralaks dalam setiap situasi, aplikasi kita akan tetap berfungsi, tetapi akan lebih baik jika kita mencari solusinya.
position: sticky
siap 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 spesifikasinya, cukup lumayan, tetapi berisi
permata kecil yang berguna di dalamnya:
Secara sekilas, hal ini mungkin tidak memiliki arti yang besar, tetapi poin penting dalam kalimat tersebut adalah ketika mengacu pada bagaimana tepatnya, kelekatan elemen dihitung: "offset dihitung dengan mengacu pada ancestor terdekat dengan kotak scroll". Dengan kata lain, jarak untuk memindahkan elemen melekat (agar terlihat melekat ke elemen lain atau area pandang) dihitung sebelum transformasi lain diterapkan, bukan setelah. Ini berarti bahwa, sangat mirip dengan 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 paralaksa mereferensikan ancestor
terdekat dengan kotak scroll, yang dalam hal ini adalah .container
. Kemudian,
sama seperti sebelumnya, .parallax-container
menerapkan nilai perspective
,
yang mengubah offset scroll yang dihitung dan menciptakan 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 merupakan berita bagus secara keseluruhan.
Peringatan penempatan yang melekat
Namun, ada perbedaan di sini: position: sticky
memang mengubah
mekanisme paralaks. Pemosisian melekat akan mencoba mempertahankan elemen ke
container scroll, sedangkan versi tidak melekat tidak. Ini berarti bahwa
paralaks dengan melekat pada akhirnya menjadi kebalikan dari yang tidak:
- Dengan
position: sticky
, semakin dekat elemen dengan z=0 kurang yang bergerak. - Tanpa
position: sticky
, semakin dekat elemen ke z=0, semakin banyak elemennya bergerak.
Jika semua itu tampak agak abstrak, lihat demo ini oleh Robert Flack, yang menunjukkan bagaimana elemen berperilaku berbeda dengan dan tanpa positioning yang melekat. Untuk melihat perbedaannya, Anda memerlukan Chrome Canary (yang merupakan versi 56 pada saat penulisan) atau Safari.
Demo oleh Robert Flack yang menunjukkan bagaimana
position: sticky
memengaruhi scroll paralaks.
Berbagai bug dan solusi
Namun, seperti halnya apa pun, masih ada benjolan dan benjolan yang perlu dihaluskan:
- Dukungan melekat tidak konsisten. Dukungan masih diterapkan di
Chrome, Edge tidak memiliki dukungan sepenuhnya, dan Firefox memiliki bug yang terdeteksi saat elemen melekat digabungkan dengan transformasi perspektif. Dalam
kasus tersebut, sebaiknya tambahkan sedikit kode untuk hanya menambahkan
position: sticky
(versi awalan-webkit-
) saat diperlukan, yaitu khusus untuk Mobile Safari. - Efeknya tidak "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 hal ini tampaknya mengalihkan Edge ke metode scroll non-OS, dan memastikan bahwa elemen tersebut memperhitungkan perubahan perspektif.
- "Konten halaman menjadi sangat besar!" Banyak browser memperhitungkan skala ini saat menentukan seberapa besar konten halaman, tetapi sayangnya Chrome dan Safari tidak memperhitungkan perspektif. Jadi,
misalnya, skala 3x yang diterapkan ke suatu elemen, Anda mungkin
melihat scroll bar dan semacamnya, meskipun elemen berada pada posisi 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 berukuran besar berkembang ke "wilayah negatif" (biasanya kiri atas) dari area yang dapat di-scroll; region yang dapat di-scroll tidak pernah mengizinkan Anda melihat atau men-scroll ke konten di wilayah negatif.
Kesimpulan
Paralaks adalah efek yang menyenangkan jika digunakan dengan cermat. Seperti yang Anda lihat, Anda dapat menerapkannya dengan cara yang berperforma tinggi, digabungkan dengan scroll, dan lintas browser. Karena diperlukan sedikit penulisan matematis, dan sedikit boilerplate untuk mendapatkan efek yang diinginkan, kami telah menyusun beberapa library dan contoh bantuan kecil, yang dapat Anda temukan di repo GitHub Contoh Elemen UI.
Silakan coba dan beri tahu kami pendapat Anda.