Анімації

Анімації при прокручуванні з CSS та Intersection Observer

Дмитро Гулак
Дмитро Гулак
13 хв читання0 переглядів

Анімації при прокручуванні з 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 обережно - Тільки на активно анімованих елементах
  • Віддавайте перевагу трансформаціям над позиціонуванням - GPU-прискорені
  • Використовуйте 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, потім експериментуйте з більш складними ефектами. Завжди надавайте пріоритет продуктивності та доступності над яскравими анімаціями.

    Схожі статті

    Продовжуйте читати за близькими темами.

    Мікровзаємодії, що відчуваються миттєвимиЗапроваджуйте делікатні CSS мікровзаємодії для кнопок, карток і форм, які покращують зворотний зв’язок без втрати продуктивності.Стратегія motion budget для інтерфейсів на 60fpsПлануйте вартість анімацій як бюджет: залишайте важливі переходи, прибирайте зайве і тримайте UI чутливим на реальних пристроях.Патерни skeleton loader, які виглядають швидко і якісноПроєктуйте стани skeleton loading, які збігаються з фінальним макетом і зменшують відчутну затримку без шкоди доступності.View Transitions API в реальних продуктах: плавна навігація без SPA-ривківДізнайтеся, коли використовувати View Transitions API, як зберегти швидкість анімацій і уникнути UX та SEO-регресій у сучасних frontend-додатках.

    Коментарі

    0

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

    Поки що тут немає коментарів. Станьте першим.