requestAnimationFrame — это встроенный метод JavaScript,
который оптимизирован для выполнения анимаций в браузере. В отличие от
таймеров setTimeout и setInterval, он
синхронизирован с частотой обновления экрана, что обеспечивает более
плавное движение и экономию ресурсов.
При создании плавного скролла requestAnimationFrame
используется для пошагового изменения позиции прокрутки в течение
нескольких кадров, создавая эффект плавного перехода от одной позиции к
другой.
Для реализации плавного скролла с requestAnimationFrame
обычно создаётся функция анимации, которая:
window.scrollTo или
element.scrollTop.requestAnimationFrame,
пока цель не достигнута.Пример базовой функции:
function smoothScroll(targetPosition, duration) {
const startPosition = window.scrollY;
const distance = targetPosition - startPosition;
let startTime = null;
function animation(currentTime) {
if (startTime === null) startTime = currentTime;
const timeElapsed = currentTime - startTime;
const run = easeInOutQuad(timeElapsed, startPosition, distance, duration);
window.scrollTo(0, run);
if (timeElapsed < duration) {
requestAnimationFrame(animation);
}
}
function easeInOutQuad(t, b, c, d) {
t /= d / 2;
if (t < 1) return c / 2 * t * t + b;
t--;
return -c / 2 * (t * (t - 2) - 1) + b;
}
requestAnimationFrame(animation);
}
Здесь ключевые моменты:
startPosition — начальная координата скролла.distance — разница между текущей и целевой
позицией.easeInOutQuad — функция сглаживания движения, создающая
ускорение и замедление.requestAnimationFrame(animation) — рекурсивно вызывает
анимацию на каждом кадре.Функция сглаживания (easing) влияет на ощущение движения. Наиболее популярные варианты:
Linear — равномерная скорость, без ускорения или замедления.
function linear(t, b, c, d) {
return c * t / d + b;
}EaseInQuad — ускорение в начале, замедление в конце:
function easeInQuad(t, b, c, d) {
t /= d;
return c * t * t + b;
}EaseOutQuad — замедление в конце:
function easeOutQuad(t, b, c, d) {
t /= d;
return -c * t * (t - 2) + b;
}EaseInOutQuad — комбинированное ускорение и замедление:
function easeInOutQuad(t, b, c, d) {
t /= d / 2;
if (t < 1) return c / 2 * t * t + b;
t--;
return -c / 2 * (t * (t - 2) - 1) + b;
}Правильный выбор функции позволяет добиться естественного движения и избежать резких скачков.
При работе с динамическим контентом важно пересчитывать позицию цели
перед запуском анимации, так как offsetTop элементов может
изменяться при изменении DOM.
const targetElement = document.querySelector('#section3');
const targetPosition = targetElement.getBoundingClientRect().top + window.scrollY;
smoothScroll(targetPosition, 800);
Использование getBoundingClientRect().top гарантирует
корректную позицию относительно текущего скролла, независимо от
изменений в DOM.
Для управления анимацией полезно хранить идентификатор, возвращаемый
requestAnimationFrame, чтобы иметь возможность её
прервать:
let animationId;
function startScroll() {
animationId = requestAnimationFrame(animation);
}
function stopScroll() {
cancelAnimationFrame(animationId);
}
Это особенно актуально, если пользователь начинает ручную прокрутку или происходит изменение целевого элемента во время анимации.
scrollTo для мелких изменений.
Использовать сглаживание с интервалом кадров.На мобильных браузерах частота кадров может быть разной, а
производительность ниже, чем на десктопе.
requestAnimationFrame автоматически синхронизирует
обновления с частотой дисплея, обеспечивая плавность. Для адаптации
лучше учитывать:
duration могут восприниматься медленно
на слабых устройствах.Smooth Scroll использует requestAnimationFrame как ядро
для анимации. В библиотеке:
requestAnimationFrame.linear, easeInOutCubic и др.).Пример инициализации Smooth Scroll с использованием
requestAnimationFrame:
const scroll = new SmoothScroll('a[href*="#"]', {
speed: 600,
speedAsDuration: true,
easing: 'easeInOutCubic',
updateURL: false
});