Анімації при прокручуванні з CSS та Intersection Observer
Анімації при прокручуванні з CSS та Intersection Observer
Анімації при прокручуванні додають життя веб-сайтам, направляючи користувачів через контент з плавними, привабливими переходами. Давайте створимо продуктивні анімації прокручування, використовуючи Intersection Observer API та CSS.
Чому Intersection Observer?
Традиційні слухачі подій прокручування спрацьовують постійно, викликаючи проблеми з продуктивністю. Intersection Observer:
- Продуктивний - Працює асинхронно, не блокує основний потік
- Економний для батареї - Спрацьовує тільки при зміні видимості
- Точний - Точно визначає, коли елементи входять/виходять з області перегляду
Базовий Fade-In при прокручуванні
Найпростіша анімація прокручування - елементи з'являються, коли вони входять в область перегляду.
HTML:
<div class="fade-in">Контент з'являється</div>
<div class="fade-in">Більше контенту</div>
<div class="fade-in">Ще більше</div>
CSS:
.fade-in {
opacity: 0;
transform: translateY(30px);
transition: opacity 0.6s ease, transform 0.6s ease;
}.fade-in.visible {
opacity: 1;
transform: translateY(0);
}
JavaScript:
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -100px 0px'
};const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, observerOptions);
document.querySelectorAll('.fade-in').forEach(el => {
observer.observe(el);
});
Каскадні анімації
Анімуйте кілька елементів з каскадною затримкою:
CSS:
.stagger-item {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s ease, transform 0.5s ease;
}.stagger-item.visible {
opacity: 1;
transform: translateY(0);
}
.stagger-item:nth-child(1).visible { transition-delay: 0.1s; }
.stagger-item:nth-child(2).visible { transition-delay: 0.2s; }
.stagger-item:nth-child(3).visible { transition-delay: 0.3s; }
.stagger-item:nth-child(4).visible { transition-delay: 0.4s; }
.stagger-item:nth-child(5).visible { transition-delay: 0.5s; }
JavaScript (динамічні затримки):
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry, index) => {
if (entry.isIntersecting) {
setTimeout(() => {
entry.target.classList.add('visible');
}, index * 100);
}
});
});
Вхід з боків
Створіть спрямовані анімації входу:
CSS:
.slide-left {
opacity: 0;
transform: translateX(-100px);
transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1);
}.slide-right {
opacity: 0;
transform: translateX(100px);
transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1);
}
.slide-left.visible,
.slide-right.visible {
opacity: 1;
transform: translateX(0);
}
Масштабування та обертання
Додайте вимірність зі масштабуванням та обертанням:
.scale-rotate {
opacity: 0;
transform: scale(0.8) rotate(-5deg);
transition: all 0.6s ease;
}.scale-rotate.visible {
opacity: 1;
transform: scale(1) rotate(0deg);
}
Parallax ефект
Створіть глибину з CSS спеціальними властивостями та Intersection Observer:
HTML:
<div class="parallax-container">
<div class="parallax-bg" data-speed="0.5"></div>
<div class="parallax-content">Контент тут</div>
</div>
CSS:
.parallax-container {
position: relative;
overflow: hidden;
height: 500px;
}.parallax-bg {
position: absolute;
inset: 0;
background: url('/image.jpg') center/cover;
transform: translateY(var(--parallax-offset, 0));
will-change: transform;
}
JavaScript:
const parallaxElements = document.querySelectorAll('[data-speed]');window.addEventListener('scroll', () => {
requestAnimationFrame(() => {
parallaxElements.forEach(el => {
const speed = parseFloat(el.dataset.speed);
const rect = el.getBoundingClientRect();
const scrolled = window.pageYOffset;
const offset = (scrolled - rect.top) * speed;
el.style.setProperty('--parallax-offset', ${offset}px);
});
});
});
Прогресивний лічильник чисел
Анімуйте числа, коли вони з'являються в області перегляду:
HTML:
<div class="counter" data-target="1250">0</div>
CSS:
.counter {
font-size: 3rem;
font-weight: 700;
color: var(--primary);
}
JavaScript:
const animateCounter = (element) => {
const target = parseInt(element.dataset.target);
const duration = 2000;
const increment = target / (duration / 16);
let current = 0; const updateCounter = () => {
current += increment;
if (current < target) {
element.textContent = Math.floor(current);
requestAnimationFrame(updateCounter);
} else {
element.textContent = target;
}
};
updateCounter();
};
const counterObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
animateCounter(entry.target);
counterObserver.unobserve(entry.target);
}
});
}, { threshold: 0.5 });
document.querySelectorAll('.counter').forEach(counter => {
counterObserver.observe(counter);
});
Анімація розкриття тексту
Розкривайте текст слово за словом або літера за літерою:
HTML:
<div class="text-reveal">
<span>Дивовижні</span>
<span>анімації</span>
<span>прокручування</span>
</div>
CSS:
.text-reveal span {
display: inline-block;
opacity: 0;
transform: translateY(20px);
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}.text-reveal.visible span {
opacity: 1;
transform: translateY(0);
}
.text-reveal.visible span:nth-child(1) { transition-delay: 0.1s; }
.text-reveal.visible span:nth-child(2) { transition-delay: 0.2s; }
.text-reveal.visible span:nth-child(3) { transition-delay: 0.3s; }
Розширене: Індикатор прогресу
Показуйте прогрес прокручування з динамічною смугою:
HTML:
<div class="progress-bar"></div>
CSS:
.progress-bar {
position: fixed;
top: 0;
left: 0;
height: 4px;
background: linear-gradient(90deg, #667eea, #764ba2);
width: var(--scroll-progress, 0%);
transition: width 0.1s ease;
z-index: 9999;
}
JavaScript:
const updateProgressBar = () => {
const scrolled = window.pageYOffset;
const height = document.documentElement.scrollHeight - window.innerHeight;
const progress = (scrolled / height) * 100;
document.querySelector('.progress-bar')
.style.setProperty('--scroll-progress', ${progress}%);
};window.addEventListener('scroll', () => {
requestAnimationFrame(updateProgressBar);
});
Найкращі практики продуктивності
will-change обережно - Тільки на активно анімованих елементахrequestAnimationFrame - Синхронізація з частотою оновлення браузераПриклад оптимізації:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1 });
Поважання вподобань користувачів
Завжди поважайте prefers-reduced-motion:
@media (prefers-reduced-motion: reduce) {
.fade-in,
.slide-left,
.slide-right,
.scale-rotate {
transition: none;
opacity: 1;
transform: none;
}
}
Висновок
Анімації прокручування покращують розповідь історій та залучення користувачів при продуманому використанні. Починайте з простих fade-ins, потім експериментуйте з більш складними ефектами. Завжди надавайте пріоритет продуктивності та доступності над яскравими анімаціями.

Коментарі
0Щоб залишати коментарі, увійдіть у свій акаунт.