Scrollytelling adalah teknik populer yang digunakan untuk menciptakan pengalaman web yang menarik dan imersif. Teknik ini melibatkan animasi elemen di halaman saat pengguna men-scroll, sehingga secara efektif menceritakan kisah atau memandu pengguna melalui narasi. Dengan Animasi Berbasis Scroll CSS, Anda dapat membuat efek ini langsung di CSS, tanpa perlu mengandalkan JavaScript. Animasi dikontrol oleh posisi scroll, bukan jam berbasis waktu, yang memastikan animasi selalu sinkron dengan scroll pengguna.
Cara menerapkan
Untuk membuat pengalaman scrollytelling, Anda memerlukan dua set elemen: satu untuk melacak posisi scroll dan satu lagi untuk dianimasikan.
Pertama, tentukan view-timeline bernama pada elemen yang ingin Anda lacak. Ini akan berfungsi sebagai pemicu untuk animasi Anda.
#tracked {
section:nth-child(1){ view-timeline: --tl-1 block; }
section:nth-child(2){ view-timeline: --tl-2 block; }
section:nth-child(3){ view-timeline: --tl-3 block; }
section:nth-child(4){ view-timeline: --tl-4 block; }
section:nth-child(5){ view-timeline: --tl-5 block; }
}
Selanjutnya, terapkan animasi ke elemen yang ingin Anda animasikan dan tautkan ke linimasa yang baru saja Anda buat menggunakan properti animation-timeline.
#animated {
section {
animation: animate-in auto linear both, animate-out auto linear forwards;
animation-range: entry 25% cover 50%, exit 50% exit 75%;
}
section:nth-child(1){ animation-timeline: --tl-1; }
section:nth-child(2){ animation-timeline: --tl-2; }
section:nth-child(3){ animation-timeline: --tl-3; }
section:nth-child(4){ animation-timeline: --tl-4; }
section:nth-child(5){ animation-timeline: --tl-5; }
}
Agar animation-timeline dapat mereferensikan linimasa yang diberi nama, keduanya harus berada dalam cakupan yang sama. Anda dapat menggunakan properti timeline-scope pada ancestor umum untuk membuat linimasa tersedia bagi semua elemen yang memerlukannya. Elemen :root sering kali merupakan pilihan yang baik untuk hal ini.
html {
timeline-scope: --tl-1, --tl-2, --tl-3, --tl-4, --tl-5;
}
Terakhir, Anda dapat menggunakan properti animation-range untuk menentukan rentang waktu yang tepat saat animasi harus berjalan. Hal ini memberi Anda kontrol terperinci atas kapan animasi dipicu dan bagaimana progresnya.
#animated section {
animation-range: entry 25% cover 50%, exit 50% exit 75%;
}
Kode contoh
html {
timeline-scope: --tl-1, --tl-2, --tl-3, --tl-4, --tl-5;
}
#tracked {
section:nth-child(1){ view-timeline: --tl-1 block; }
section:nth-child(2){ view-timeline: --tl-2 block; }
section:nth-child(3){ view-timeline: --tl-3 block; }
section:nth-child(4){ view-timeline: --tl-4 block; }
section:nth-child(5){ view-timeline: --tl-5 block; }
}
@keyframes animate-in {
from { scale: 0.5; opacity: 0; transform: rotateY(-180deg); }
to { transform: rotateY(0deg); }
}
@keyframes animate-out {
to { translate: 100% 0; opacity: 0; }
}
#animated {
section {
animation: animate-in auto linear both, animate-out auto linear forwards;
animation-range: entry 25% cover 50%, exit 50% exit 75%;
backface-visibility: hidden;
}
section:nth-child(1){ animation-timeline: --tl-1; }
section:nth-child(2){ animation-timeline: --tl-2; }
section:nth-child(3){ animation-timeline: --tl-3; }
section:nth-child(4){ animation-timeline: --tl-4; }
section:nth-child(5){ animation-timeline: --tl-5; }
}
/* MANDATORY Copy-Paste Safety: Disable continuous storytelling motion for sensitive users */
@media (prefers-reduced-motion: reduce) {
#animated section {
animation: none !important;
opacity: 1 !important;
transform: none !important;
}
}
Praktik Terbaik
Saat menggunakan animasi berbasis scroll, penting untuk mengikuti beberapa praktik terbaik guna memastikan pengalaman yang lancar dan mudah diakses:
- LAKUKAN penyertaan deteksi fitur: Tidak semua browser mendukung animasi berbasis scroll. Gunakan
@supports ((animation-timeline: scroll()) and (animation-range: 0% 100%))untuk memeriksa dukungan dan menyediakan penggantian untuk browser yang tidak mendukungnya.- Pemeriksaan
(animation-range: 0% 100%)HARUS disertakan di sini, untuk memfilter browser yang hanya memiliki dukungan parsial. - JANGAN gunakan paket
scroll-timeline-polyfilluntuk strategi penggantian karena fiturnya tidak lengkap dan memiliki banyak masalah umum. - Jika animasi hanya dianggap sebagai dekoratif, pilih Progressive Enhancement dan JANGAN berikan penggantian.
- Pemeriksaan
- LAKUKAN penghormatan terhadap preferensi pengguna: Beberapa pengguna lebih memilih untuk memiliki lebih sedikit gerakan di web. Gunakan kueri media
prefers-reduced-motionuntuk menonaktifkan atau mengurangi animasi bagi pengguna ini. - LAKUKAN upaya untuk menganimasikan hanya properti CSS yang berperforma tinggi: Untuk animasi yang paling lancar, tetap gunakan properti animasi yang dapat ditangani oleh thread compositor browser, seperti
transformdanopacity. Menganimasikan properti lain sepertiwidthatauheightdapat menyebabkan masalah performa. - LAKUKAN penggunaan urutan deklarasi yang benar: Saat menggunakan properti singkat
animation, deklarasikananimation-timelinedananimation-rangesetelahnya untuk mencegah properti singkat mereset linimasa.
Saat menggunakan properti view-timeline untuk membuat animasi berbasis scroll:
- LAKUKAN penggunaan
<dashed-ident>CSS untuk nama. - OPSIONAL tentukan sumbu yang akan dilacak: Jika tidak menargetkan sumbu
blockdefault (seperti dalam scroller horizontal), tentukan sumbu yang akan dilacak denganview-timeline-axis. - LAKUKAN: pastikan cakupan pencarian berfungsi: Jika elemen yang mendeklarasikan
view-timelinebukan ancestor hierarki datar dari elemen animasi, naikkan visibilitas namaview-timelinemenggunakantimeline-scopepada ancestor bersama.
Strategi penggantian
Untuk browser yang tidak mendukung animasi berbasis scroll, Anda dapat menggunakan penggantian untuk membuat ulang efek visual. Penggantian biasanya dibuat dengan pemroses scroll (untuk efek ScrollTimeline) atau IntersectionObserver API (untuk efek ViewTimeline).
Di browser dengan dukungan bawaan untuk animasi berbasis scroll, SELALU gunakan implementasi CSS native karena lebih berperforma.
Perhatikan bahwa tidak semua efek dapat dibuat ulang menggunakan pendekatan penggantian.
Khusus untuk kasus penggunaan ini, skrip berikut menerapkan penggantian untuk browser yang tidak mendukung animasi berbasis scroll. Kode ini menggunakan IntersectionObserver untuk melacak visibilitas setiap elemen #tracked section dan memperbarui properti transform dari #animated section yang sesuai.
const animatedSections = document.querySelectorAll('#animated section');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
const sectionIndex = Array.from(document.querySelectorAll('#tracked section')).indexOf(entry.target);
if (sectionIndex !== -1) {
const animatedSection = animatedSections[sectionIndex];
const ratio = entry.intersectionRatio;
// Animate-in
animatedSection.style.opacity = ratio;
animatedSection.style.transform = `scale(${0.5 + ratio * 0.5}) rotateY(${-180 + ratio * 180}deg)`;
// Animate-out
if (ratio < 0.5) {
animatedSection.style.translate = `${(0.5 - ratio) * 2 * 100}% 0`;
} else {
animatedSection.style.translate = '0 0';
}
}
});
}, { threshold: Array.from({length: 101}, (_, i) => i / 100) });
document.querySelectorAll('#tracked section').forEach(section => {
observer.observe(section);
});
Dan CSS yang menyertainya:
#animated section {
opacity: 0;
transform: scale(0.5) rotateY(-180deg);
backface-visibility: hidden;
}
/* MANDATORY Copy-Paste Safety: Ensure content remains fully visible and legible for assistive technologies or users with motion sensitivities */
@media (prefers-reduced-motion: reduce) {
#animated section {
opacity: 1 !important;
transform: none !important;
translate: 0 0 !important;
}
}
Penggantian ini memberikan animasi berbasis scroll yang lebih akurat untuk browser yang tidak mendukung fitur CSS native, sehingga memastikan pengalaman yang lebih konsisten bagi semua pengguna. Dengan menggunakan serangkaian nilai minimum untuk IntersectionObserver, kita dapat melacak posisi scroll dengan lebih presisi dan membuat animasi yang lebih halus.