Як 5-рядковий CSS фрагмент замінив 50+ рядків неакуратного JavaScript і змінив анімацію на веб-сайтах назавжди
Фото Панканджа Пателя на Unsplash
Кожного разу, коли я виходив з зустрічі з моїм UI/UX дизайнером, де він просив мене реалізувати анімації при прокручуванні, я таємно хотів закричати на весь голос. Чому? Тому що я ненавидів реалізовувати анімації при прокручуванні. Це виглядає чудово і "відносно просто" реалізувати, але є свої особливості, з якими потрібно мати справу, особливо якщо є багато рухомих елементів.
Але коли клієнти просять ці елегантні ефекти "поступового з'явлення при прокручуванні", що можна зробити? Загортаєш рукави і пишеш трохи JavaScript, навіть якщо це змушує тебе вмирати трохи всередині.
Дні JavaScript (або Часи Темряви)
Ось що я раніше писав (і якщо ви все ще це робите, я відчуваю вашу біль):
window.addEventListener('scroll', () => {
const elements = document.querySelectorAll('.fade-in');
elements.forEach(element => {
const elementTop = element.getBoundingClientRect().top;
const windowHeight = window.innerHeight;
if (elementTop < windowHeight * 0.8) {
element.style.opacity = '1';
element.style.transform = 'translateY(0)';
}
});
});
Я або змінював властивості CSS, або додавав класи до елементів, що мали властивості анімації, роблячи це якимось чином магічним. Ось найцікавіша частина: я також мав реалізувати зворотний процес. Це було потворно, і я це ненавидів. Бідний браузер обчислював все з кожною подією прокручування.
Іноді я міг чути її крик. На мобільних пристроях індикатор заряду батареї буквально падав.
Набувши певного досвіду з JavaScript, я використовував дебаунсинг, щоб зменшити кількість обчислень, які треба було виконати. Звісно, це було трохи краще, але це не могло бути найкращим варіантом, правда?
Знайдення Intersection Observer (Промінь Надії)
Тоді я відкрив API Intersection Observer.
Нарешті, щось краще! Замість постійної перевірки позицій прокручування, я міг сказати браузеру: "Ей, дай мені знати, коли цей елемент стане видимим":
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target); // Оскільки спрацьовує і забувається!
}
});
}, { threshold: 0.2 });
document.querySelectorAll('.animate-on-scroll')
.forEach(el => observer.observe(el));
У поєднанні з деяким CSS:
.animate-on-scroll {
opacity: 0;
transform: translateY(20px);
transition: all 0.6s ease-out;
}
.animate-on-scroll.visible {
opacity: 1;
transform: translateY(0);
}
Це було набагато краще! Але все ж… щось було не так. Анімації або включались, або вимикались — не було плавного контролю залежно від позиції прокручування.
Коли клієнти просили ці супер-плавні ефекти паралаксу або прогресивне розкриття, я знову повертався до написання складного JavaScript або використовував CSS хаки, які знаходив в інтернеті.
Потім все змінилося: функція view()
І ось, як подарунок від богів CSS, я відкрив view(). Святий. Моли. Подивіться на це:
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn linear;
animation-timeline: view(); /* Дзвони */
animation-range: entry 10% cover 30%; /* Свистки */
}
І ось це все! Ніякого JavaScript. Ніяких прослуховувачів подій (Event Listener). Ніяких кошмарів з продуктивністю.
Просто неймовірно плавні анімації, що реагують на прокручування.
Хочете зробити ще цікавіше? Потрібно, щоб елемент з'являвся та зникав, коли користувачі прокручують мимо? Без проблем:
@keyframes fadeInOut {
0% {
opacity: 0;
scale: 0.5;
transform: translateX(100%);
}
50% {
opacity: 1;
scale: 1;
transform: translateX(0);
}
100% {
opacity: 0;
scale: 0.5;
transform: translateX(100%);
}
}
.fade-in-out {
animation: fadeInOut linear;
animation-timeline: view();
animation-range: entry exit;
}
Я розсміявся вголос, коли вперше спробував це. Усі ті години, витрачені на оптимізацію обробників прокручування та обчислення відсотків… а тепер це всього кілька рядків CSS? Ось робочий приклад -
Найкраща частина?
Знаєте, що вражає? Продуктивність. Ті старі обробники прокручування на JavaScript змушували вентилятори мого MacBook працювати, наче він намагався досягти зльоту. А з view()? Плавно, як масло, навіть на мобільних пристроях.
Браузер виконує всю важку роботу, працюючи на потоці композитора (це модне слово означає "дуже швидко").
До того ж, це просто… працює. Більше не потрібно дебагити, чому анімація спрацьовує не в той час, чи чому обчислення позиції прокручування відрізняються на кілька пікселів. Більше ніякого "scroll-jank", коли є багато анімованих елементів. Просто працює.
Розуміння функції CSS view(): Глибоке занурення
Впровадження функції CSS view()
знаменує собою значну зміну в тому, як ми обробляємо анімації, що залежать від прокручування. Давайте розберемо цю потужну функцію і зрозуміємо, чому вона революціонує веб-анімації.
animation-timeline: view()
В своїй основі, view()
створює лінію часу прогресії, засновану на видимості елемента у вікні перегляду.
Уявіть це як віртуальну лінію часу, яка рухається вперед, коли елемент потрапляє у вікно перегляду, і назад, коли він виходить.
.element {
animation-timeline: view();
}
Ви можете налаштувати, як вимірюється вікно перегляду:
/* Додати відступи для виявлення вікна перегляду */
animation-timeline: view(block 10px); /* Вертикальний відступ */
animation-timeline: view(inline 50%); /* Горизонтальний відступ */
animation-timeline: view(10px 20px 30px 40px); /* Всі сторони */
Розуміння animation-range
Властивість animation-range
визначає, коли ваша анімація починається і закінчується відносно позиції елемента у вікні перегляду.
Це як встановлення контрольних точок для вашої анімації.
Доступні значення діапазону:
entry
: Коли елемент починає входити у вікно переглядуexit
: Коли елемент починає виходити з вікна переглядуcontain
: Коли елемент повністю знаходиться у вікні переглядуcover
: Коли елемент повністю покриває вікно перегляду
Ось як їх використовувати:
/* Базовий діапазон */
.fade-in {
animation-range: entry 0% cover 100%;
}
/* Кілька діапазонів для складних анімацій */
.fade-in-out {
animation-range: entry 0% entry 50%,
exit 50% exit 100%;
}
Ці контрольні точки можна використовувати з правилом keyframes для виконання складних анімацій, які раніше було важко реалізувати.
Додаткове читання
CSS покращив мій досвід розробника і зробив анімації при прокручуванні легкими — це чудовий старт.
Однак, я закликаю вас продовжити читання —
Щасливого кодування! 🚀
Знайти всі мої посилання можна тут. Давайте поспілкуємося і створимо щось разом 🙂
Перекладено з: CSS view(): The Death of JavaScript Scroll Animations