Прокрутка страницы представляет собой изменение значения скролла
документа или контейнера по вертикали или горизонтали. В стандартном DOM
API поведение контролируется через window.scrollTo,
element.scrollIntoView и свойство
scroll-behavior: smooth, которое задаёт встроенную анимацию
перемещения.
html {
scroll-behavior: smooth;
}
Такой подход ограничен отсутствием контроля над кривыми анимации, временем и синхронизацией с другими эффектами интерфейса. Библиотека Motion One добавляет возможность управлять скроллом как анимацией: задавать easing, длительность, триггеры и связывать прокрутку с состоянием интерфейса.
Базовый сценарий — программная прокрутка страницы к конкретной позиции.
В нативном варианте:
window.scrollTo({
top: 800,
beh * avior: "smooth"
});
При использовании Motion One логика расширяется через анимацию
числового значения прокрутки. Концептуально прокрутка рассматривается
как изменяемая величина scrollY.
import { animate } from "motion";
animate(window.scrollY, 800, {
duration: 0.8,
ease: "easeInOut",
onUpdate: (latest) => {
window.scrollTo(0, latest);
}
});
Такой подход позволяет полностью контролировать траекторию движения, а не полагаться на браузерную реализацию smooth scroll.
Основной сценарий интерфейсов — переход к конкретному элементу по клику или событию.
const target = document.querySelector("#section-3");
window.scrollTo({
top: target.offsetTop,
beh * avior: "smooth"
});
Однако offsetTop не учитывает сложные layout-сценарии
(flex, nested scroll containers, sticky элементы).
const target = document.querySelector("#section-3");
const top =
target.getBoundingClientRect().top +
window.scrollY;
window.scrollTo({
top,
beh * avior: "smooth"
});
Motion One позволяет заменить системную анимацию на управляемую кривую:
import { animate } from "motion";
function scrollToElement(el) {
const targetY =
el.getBoundingClientRect().top +
window.scrollY;
animate(window.scrollY, targetY, {
duration: 0.9,
ease: "easeInOut",
onUpdate: (latest) => {
window.scrollTo(0, latest);
}
});
}
document.querySelector("button")
.addEventListener("click", () => {
scrollToElement(document.querySelector("#section-3"));
});
Такой подход открывает возможность синхронизации прокрутки с другими анимациями интерфейса.
Частая проблема — перекрытие целевого элемента фиксированной панелью навигации.
const headerHeight = 80;
const top =
el.getBoundingClientRect().top +
window.scrollY -
headerHeight;
window.scrollTo({
top,
beh * avior: "smooth"
});
При изменяемой высоте хедера:
function getHeaderHeight() {
const header = document.querySelector("header");
return header ? header.offsetHeight : 0;
}
const top =
el.getBoundingClientRect().top +
window.scrollY -
getHeaderHeight();
Не все интерфейсы используют window как основной scroll
context. Часто прокрутка реализуется внутри блока с
overflow: auto.
<div class="scroll-container">
<div class="item" id="target"></div>
</div>
.scroll-container {
height: 400px;
overflow-y: auto;
}
const container = document.querySelector(".scroll-container");
const target = document.querySelector("#target");
container.scrollTo({
top: target.offsetTop,
beh * avior: "smooth"
});
const containerRect = container.getBoundingClientRect();
const targetRect = target.getBoundingClientRect();
const top = targetRect.top - containerRect.top + container.scrollTop;
container.scrollTo({
top,
beh * avior: "smooth"
});
import { animate } from "motion";
function scrollContainerTo(container, target) {
const top =
target.getBoundingClientRect().top -
container.getBoundingClientRect().top +
container.scrollTop;
animate(container.scrollTop, top, {
duration: 0.8,
ease: "easeOut",
onUpdate: (v) => {
container.scrollTop = v;
}
});
}
Motion One рассматривает анимацию как интерполяцию значений. Прокрутка — это частный случай изменения числового состояния.
animate(0, 1200, {
duration: 1.2,
ease: [0.22, 1, 0.36, 1],
onUpdate: (value) => {
window.scrollTo(0, value);
}
});
Cubic bezier-кривые позволяют добиться более естественного движения, близкого к инерции интерфейсов мобильных систем.
Прокрутка может использоваться как триггер визуальных изменений: появление секций, смена фона, прогресс-индикаторы.
window.addEventListener("scroll", () => {
const scrollY = window.scrollY;
document.body.style.setProperty(
"--scroll",
scrollY
);
});
Далее значение используется в CSS-анимациях.
При прокрутке к элементу часто требуется не только переместить viewport, но и запустить анимацию появления.
import { animate, inView } from "motion";
const el = document.querySelector("#section-3");
inView(el, () => {
animate(el, { opacity: 1, y: 0 }, {
duration: 0.6,
ease: "easeOut"
});
});
Комбинация scrollTo и inView позволяет
строить сцены интерфейса, где прокрутка выступает триггером анимационных
последовательностей.
Сценарий: сначала прокрутка, затем каскадное появление контента.
function scrollThenAnimate(el) {
const top =
el.getBoundingClientRect().top +
window.scrollY;
animate(window.scrollY, top, {
duration: 0.8,
ease: "easeInOut",
onUpdate: (v) => window.scrollTo(0, v),
onComplete: () => {
animate(el.querySelectorAll(".child"), {
opacity: [0, 1],
y: [20, 0]
}, {
delay: (i) => i * 0.05,
duration: 0.5
});
}
});
}
При загрузке данных высота страницы может изменяться, что влияет на целевые координаты.
async function scrollAfterLoad(el, loadFn) {
await loadFn();
requestAnimationFrame(() => {
const top =
el.getBoundingClientRect().top +
window.scrollY;
animate(window.scrollY, top, {
duration: 0.9,
ease: "easeInOut",
onUpdate: (v) => window.scrollTo(0, v)
});
});
}
При частых вызовах прокрутки важно предотвращать конфликт анимаций.
let currentAnimation;
function smoothScrollTo(y) {
if (currentAnimation) currentAnimation.cancel();
currentAnimation = animate(window.scrollY, y, {
duration: 0.7,
ease: "easeOut",
onUpdate: (v) => window.scrollTo(0, v)
});
}
Такой подход предотвращает дергание интерфейса при множественных событиях навигации.
Иногда требуется смещение относительно текущей позиции.
function scrollByOffset(offset) {
const start = window.scrollY;
const end = start + offset;
animate(start, end, {
duration: 0.6,
ease: "easeInOut",
onUpdate: (v) => window.scrollTo(0, v)
});
}
Motion One позволяет синхронизировать движение страницы с интерфейсными эффектами.
function scrollWithHeaderEffect(el) {
const top =
el.getBoundingClientRect().top +
window.scrollY;
animate(window.scrollY, top, {
duration: 1,
ease: "easeInOut",
onUpdate: (v) => {
window.scrollTo(0, v);
document.querySelector("header")
.style.transform =
`translateY(${v * 0.02}px)`;
}
});
}
Прокрутка перестаёт быть утилитарной функцией и становится частью реактивной модели состояния. События навигации, клики и загрузка данных переводятся в числовую интерполяцию, которая может быть связана с любым визуальным параметром интерфейса: прозрачностью, масштабом, положением слоёв и скоростью появления контента.