Плавная прокрутка на JavaScript — практический гид

Плавная прокрутка — это приём в веб-разработке, при котором переход к целевому элементу страницы выполняется с анимированным перемещением, а не с резким «перескакиванием». Такой подход улучшает восприятие структуры страницы и помогает пользователю понять, куда именно он был перемещён.
Что такое плавная прокрутка
Плавная прокрутка — это поведение, при котором браузер или скрипт анимирует изменение положения прокрутки до целевой точки. Термин в одну строку: это анимированный переход окна просмотра к элементу, вместо мгновенного прыжка.
Почему это важно
- Улучшает визуальную иерархию и делает интерфейс более «шлифованным».
- Повышает вовлечённость: пользователи реже теряются при длинных страницах и легче находят контент.
- Упрощает навигацию между секциями и улучшает восприятие взаимосвязи контента.
Важно: не всегда стоит включать анимацию — учитывайте предпочтения пользователей (prefers-reduced-motion) и потенциальное ухудшение производительности на слабых устройствах.
Реализация в JavaScript
Ниже показан пошаговый подход: HTML-разметка, базовый CSS и JavaScript. Исходная разметка и код сохранены для наглядности.
HTML: структура
Сначала создайте навигацию и секции с идентификаторами, к которым будут вести ссылки:
Smooth Scrolling Guide for Web Developers
Section 1
Section 2
Section 3
Комментарий: каждая ссылка содержит href с «#id» целевой секции. Браузер по умолчанию делает мгновенный переход к элементу. Дальше мы заменим это поведение на плавное.
CSS: базовая стилизация
Пример стилей, чтобы увидеть секции как полноэкранные блоки и фиксированную навигацию:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
nav {
background: #fff;
box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.25);
position: sticky;
top: 0;
padding: 30px;
}
nav ul {
display: flex;
gap: 10px;
justify-content: center;
}
nav ul li {
list-style: none;
}
nav ul li a {
border-radius: 5px;
border: 1.5px solid #909090;
text-decoration: none;
color: #333;
padding: 10px 20px;
}
section {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
} Примечание: CSS-свойство html { scroll-behavior: smooth; } обеспечивает нативную плавность для якорных ссылок и window.scrollTo с behavior: ‘smooth’ в современных браузерах. Однако оно не даёт контроля над кривой ускорения.
Базовая реализация на JavaScript (scrollIntoView)
scrollIntoView — встроенный метод, который отправляет элемент в видимую область контейнера. Это самый простой способ добавить плавную прокрутку для якорных ссылок.
Слушаем событие DOMContentLoaded и устанавливаем обработчики на ссылки навигации:
document.addEventListener("DOMContentLoaded", makeLinksSmooth);Функция, которая находит ссылки и навешивает обработчики:
function makeLinksSmooth() {
const navLinks = document.querySelectorAll("nav a");
navLinks.forEach((link) => {
link.addEventListener("click", smoothScroll);
});
}Функция обработчика, которая отменяет дефолт и вызывает scrollIntoView с опцией behavior: ‘smooth’:
function smoothScroll(e) {
e.preventDefault();
const targetId = this.getAttribute("href");
const targetElement = document.querySelector(targetId);
if (targetElement) {
targetElement.scrollIntoView({ behavior: "smooth", });
}
}Важно: если на странице есть фиксированная шапка (fixed header), scrollIntoView может скрыть верхнюю часть целевого элемента под шапкой. Ниже приведены способы учёта смещения.
Учет фиксированной шапки и смещений
Если у вас есть фиксированный header высотой headerHeight, используйте явную прокрутку с вычислением позиции:
function scrollToWithOffset(element, offset) {
const elementTop = element.getBoundingClientRect().top + window.pageYOffset;
const targetPosition = elementTop - offset;
window.scrollTo({ top: targetPosition, behavior: 'smooth' });
}
// Использование:
const headerHeight = document.querySelector('header')?.offsetHeight || 0;
scrollToWithOffset(targetElement, headerHeight + 16); // добавляем отступ 16pxЭтот подход даёт контроль над финальной позицией и предотвращает скрытие заголовков.
Тонкая настройка и кастомные easing-функции
Нативный scroll-behavior поддерживает только значения auto и smooth. Если нужна кастомная кривая ускорения (cubic-bezier), придётся реализовать анимацию вручную или использовать библиотеку.
Пример функции прокрутки с кастомным easing на основе requestAnimationFrame:
// Простая функция easeInOutQuad
function easeInOutQuad(t) { return t < 0.5 ? 2*t*t : -1 + (4 - 2*t)*t; }
function animatedScrollTo(targetY, duration = 600) {
const startY = window.pageYOffset;
const diff = targetY - startY;
let start;
function step(timestamp) {
if (!start) start = timestamp;
const time = Math.min(1, (timestamp - start) / duration);
const eased = easeInOutQuad(time);
window.scrollTo(0, Math.round(startY + diff * eased));
if (time < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
// Использование:
const headerHeight = document.querySelector('header')?.offsetHeight || 0;
const targetY = targetElement.getBoundingClientRect().top + window.pageYOffset - headerHeight - 12;
animatedScrollTo(targetY, 700);Этот подход даёт полный контроль над длительностью и кривой ускорения.
Доступность и prefers-reduced-motion
Не забывайте учитывать пользователей с повышенной чувствительностью к движению. Используйте CSS-медиа-запрос prefers-reduced-motion и JS-проверку:
/* CSS */
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
}// JS
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (reduceMotion) {
// Отключаем анимации прокрутки
}Важно: уважайте пользовательские настройки — если prefers-reduced-motion включён, не выполняйте анимированную прокрутку.
Совместимость браузеров и полифилы
Нативная поддержка scroll-behavior и опции behavior в window.scrollTo/scrollIntoView есть в большинстве современных браузеров, но в старых версиях и некоторых мобильных браузерах её может не быть.
Рекомендации:
- Проверяйте поддержку через сервисы типа “Can I use”.
- Для старых браузеров используйте полифилы, например “smoothscroll-polyfill” (популярный полифил) или библиотеку с проверкой поддержки.
- Тестируйте на реальных устройствах с медленными сетями/процессорами.

Альтернативные подходы
- Чистый CSS: html { scroll-behavior: smooth; } — самый простой способ, но без контроля над easing и смещением.
- JavaScript библиотеки: “smooth-scroll” и другие предлагают конфигурации, offset, callbacks и history-поддержку.
- Полная кастомная реализация через requestAnimationFrame — максимум контроля, но требует тестирования и оптимизации.
Когда нативное поведение не хватает (нужны кастомные кривые, offsets, callbacks), выбирайте JS-библиотеку или кастомную реализацию.
Практические советы и эвристики
- Если нужен простой эффект — используйте CSS scroll-behavior.
- Если есть фиксированная шапка — используйте прокрутку с offset.
- Всегда учитывайте prefers-reduced-motion.
- Не делайте длительные анимации (>900 ms) — это может восприниматься как задержка.
- Кешируйте часто используемые DOM-элементы (например, headerHeight), но обновляйте их при ресайзе.
Готовые сниппеты (cheat sheet)
- Нативный (CSS):
html { scroll-behavior: smooth; }- JS + scrollIntoView:
link.addEventListener('click', function(e) {
e.preventDefault();
const id = this.getAttribute('href');
const el = document.querySelector(id);
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
});- Учёт фиксированного header:
const headerHeight = document.querySelector('header')?.offsetHeight || 0;
const top = element.getBoundingClientRect().top + window.pageYOffset - headerHeight;
window.scrollTo({ top, behavior: 'smooth' });- Отключение анимаций для предпочитающих reduced motion:
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
// выполняем анимированную прокрутку
}Критерии приёмки (Test cases)
- Навигация по якорям прокручивает страницу плавно в поддерживаемых браузерах.
- При наличии фиксированной шапки целевой контент не скрыт (проверка с разными высотами header).
- При включённом prefers-reduced-motion анимация отключена.
- Работает на мобильных устройствах с медленным CPU (пусть QA проверит на минимально поддерживаемом устройстве).
- При клике на ссылку фокус перемещается к заголовку (accessibility): document.focus() при необходимости.
Role-based checklist
Разработчик:
- Реализовать плавную прокрутку и учесть header offset.
- Добавить проверку prefers-reduced-motion.
- Подключить полифил или graceful fallback.
QA:
- Проверить на десктопе и мобильных устройствах.
- Проверить с включённым prefers-reduced-motion.
- Проверить на разных браузерах и версиях.
Дизайнер:
- Утвердить длительность и easing анимации.
- Подтвердить визуальные отступы после скролла.
Product Manager:
- Убедиться, что анимация улучшает UX и не мешает конверсии.
Decision flowchart
flowchart TD
A[Начало: нужно ли плавное движение?] --> B{Простой эффект}
B -- Да --> C[Использовать CSS: scroll-behavior]
B -- Нет --> D{Нужны offset или кастомная кривая}
D -- Offset --> E[Использовать window.scrollTo с вычисленным top]
D -- Кастомная кривая --> F[Реализовать requestAnimationFrame или библиотеку]
C --> G[Учесть prefers-reduced-motion]
E --> G
F --> G
G --> H[Протестировать на устройствах и браузерах]Сравнение подходов (матрица)
CSS scroll-behavior
- Плюсы: простой, нативный, мало кода.
- Минусы: нет контроля over easing, не учитывает header offset.
scrollIntoView
- Плюсы: просто подключать, работает для отдельных элементов.
- Минусы: поведение по умолчанию может скрывать контент под fixed header; ограниченный контроль.
window.scrollTo с вычисленным top
- Плюсы: контролируемая финальная позиция.
- Минусы: нужно вычислять позицию и учитывать ресайз.
Кастомная анимация (requestAnimationFrame)
- Плюсы: полный контроль над easing/duration.
- Минусы: сложнее реализовать и протестировать; возможны баги на слабых устройствах.
Edge cases и распространённые проблемы
- Скрытие цели под фиксированной шапкой — решается offset.
- Несовместимость со старым браузером — решается полифилом или fallback в auto.
- Переключение фокуса и порядок чтения экранными читалками — убедитесь, что фокус устанавливается на целевой элемент.
- Одновременные вызовы анимации — добавьте дебаунс/блокировку во время прокрутки.
Краткое резюме
- Для простых случаев используйте CSS scroll-behavior.
- Для контроля позиции при фиксированных элементах используйте window.scrollTo с offset.
- Для кастомного easing — requestAnimationFrame или проверенная библиотека.
- Всегда учитывайте prefers-reduced-motion и тестируйте на мобильных устройствах.
Важно: выбирайте решение в зависимости от требуемого уровня контроля, производительности и требований к доступности.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone