Реалізація Smooth Scroll на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація Smooth Scroll на сайті
Проста
~1 робочий день
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація плавного скролу на веб-сайті

Плавний скролл — це не scroll-behavior: smooth в CSS. Це управління швидкістю та характером прокрутки: інерція, easing, привязка до секцій, синхронізація з анімаціями. Нативний CSS-варіант дає одне фіксоване значення плавності без можливості настройки.

CSS-підхід: коли його достатньо

Для простих випадків — якорні посилання всередині сторінки, повернення вгору — нативного CSS хватає:

html {
  scroll-behavior: smooth;
}

/* Відступ при наявності фіксованого хедера */
[id] {
  scroll-margin-top: 80px;
}
// Якорна навігація
document.querySelectorAll('a[href^="#"]').forEach((link) => {
  link.addEventListener('click', (e) => {
    e.preventDefault()
    const target = document.querySelector(link.getAttribute('href')!)
    if (target) {
      target.scrollIntoView({ behavior: 'smooth', block: 'start' })
    }
  })
})

Обмеження: не можна настроїти швидкість, немає подій початку/кінця прокрутки, не однаково підтримується у всіх браузерах, не працює з кастомним scroll-контейнером.

JavaScript-реалізація без бібліотек

Коли потрібен контроль: кастомний easing, callbacks, програмна прокрутка з різною швидкістю.

type EasingFn = (t: number) => number

const easings: Record<string, EasingFn> = {
  linear: (t) => t,
  easeInOutCubic: (t) => t < 0.5 ? 4 * t ** 3 : 1 - (-2 * t + 2) ** 3 / 2,
  easeOutQuart: (t) => 1 - (1 - t) ** 4,
  easeInOutExpo: (t) => t === 0 ? 0 : t === 1 ? 1
    : t < 0.5 ? 2 ** (20 * t - 10) / 2
    : (2 - 2 ** (-20 * t + 10)) / 2,
}

function scrollTo(
  target: number | HTMLElement,
  options: {
    duration?: number
    easing?: keyof typeof easings
    offset?: number
    onComplete?: () => void
  } = {}
) {
  const {
    duration = 800,
    easing = 'easeInOutCubic',
    offset = 0,
    onComplete,
  } = options

  const startY = window.scrollY
  const endY = typeof target === 'number'
    ? target
    : target.getBoundingClientRect().top + window.scrollY + offset

  const distance = endY - startY
  const easeFn = easings[easing]
  let startTime: number | null = null

  function step(timestamp: number) {
    if (!startTime) startTime = timestamp
    const elapsed = timestamp - startTime
    const progress = Math.min(elapsed / duration, 1)
    const eased = easeFn(progress)

    window.scrollTo(0, startY + distance * eased)

    if (progress < 1) {
      requestAnimationFrame(step)
    } else {
      onComplete?.()
    }
  }

  requestAnimationFrame(step)
}

Scroll Snap

CSS Scroll Snap привязує прокрутку до секцій. Працює нативно та продуктивно, не вимагає JS.

.scroll-container {
  height: 100vh;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;  /* або proximity */
}

.scroll-section {
  height: 100vh;
  scroll-snap-align: start;
  scroll-snap-stop: always;  /* заборона "перельоту" через секцію */
}

/* Горизонтальний snap для слайдера */
.slider {
  display: flex;
  overflow-x: scroll;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
}

Відслідковування позиції скролу

Реактивне відслідковування для анімацій при скролі. Throttle через requestAnimationFrame замість setTimeout:

class ScrollTracker {
  private scrollY = 0
  private ticking = false
  private listeners: Set<(y: number, direction: 'up' | 'down') => void> = new Set()
  private lastY = 0

  constructor() {
    window.addEventListener('scroll', this.onScroll, { passive: true })
  }

  private onScroll = () => {
    this.scrollY = window.scrollY

    if (!this.ticking) {
      requestAnimationFrame(() => {
        const direction = this.scrollY > this.lastY ? 'down' : 'up'
        this.listeners.forEach(fn => fn(this.scrollY, direction))
        this.lastY = this.scrollY
        this.ticking = false
      })
      this.ticking = true
    }
  }

  subscribe(fn: (y: number, direction: 'up' | 'down') => void) {
    this.listeners.add(fn)
    return () => this.listeners.delete(fn)
  }

  destroy() {
    window.removeEventListener('scroll', this.onScroll)
  }
}

// Використання
const tracker = new ScrollTracker()

tracker.subscribe((y, dir) => {
  // Приховати хедер при скролі вниз, показати при скролі вгору
  header.classList.toggle('header--hidden', dir === 'down' && y > 100)
})

Intersection Observer для анімацій при появленні

const observer = new IntersectionObserver(
  (entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        entry.target.classList.add('in-view')
        // Відписатися якщо анімація одноразова
        observer.unobserve(entry.target)
      }
    })
  },
  {
    threshold: 0.15,       // 15% елемента має бути видно
    rootMargin: '0px 0px -50px 0px',  // відступ знизу
  }
)

document.querySelectorAll('[data-animate]').forEach((el) => {
  observer.observe(el)
})
[data-animate] {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}

[data-animate].in-view {
  opacity: 1;
  transform: translateY(0);
}

/* Staggered delay для груп */
[data-animate]:nth-child(1) { transition-delay: 0ms; }
[data-animate]:nth-child(2) { transition-delay: 100ms; }
[data-animate]:nth-child(3) { transition-delay: 200ms; }

Доступність

prefers-reduced-motion — системний флаг, який потрібно поважати. Користувачі з вестибулярними розстройствами вимикають анімацію в системних налаштуваннях.

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  [data-animate] {
    transition: none;
    opacity: 1;
    transform: none;
  }
}
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches

// Якщо анімація не потрібна — миттєва прокрутка
function safeScrollTo(target: HTMLElement) {
  if (prefersReduced) {
    target.scrollIntoView()
  } else {
    scrollTo(target, { duration: 800 })
  }
}

Терміни

Якорна навігація + Intersection Observer анімації — половина дня. Кастомний smooth scroll з easing, scroll-tracker для хедера та staggered анімаціями — 1 день.