Функция inView в Motion One предназначена для
отслеживания момента, когда DOM-элемент входит или выходит из области
видимости viewport. Механизм основан на
IntersectionObserver, что обеспечивает высокую
производительность без необходимости ручного отслеживания скролла.
Основная ценность inView заключается в декларативном
подходе к запуску анимаций, ленивой инициализации эффектов и управлении
состоянием элементов в зависимости от их видимости.
inView принимает элемент и callback-функцию, которая
вызывается при изменении его пересечения с viewport. Дополнительно
предоставляется объект options, аналогичный конфигурации
IntersectionObserver.
Поведение можно представить как реакцию на два ключевых события:
inView(element, (info) => {
// логика при изменении видимости
}, options)
Параметр info содержит метаданные наблюдения:
entry — объект
IntersectionObserverEntrytarget — DOM-элементisVisible — логическое значение текущей видимостиimport { inView } from "motion";
inView(".box", ({ target }) => {
target.style.opacity = "1";
target.style.transform = "translateY(0px)";
});
В этом сценарии элемент получает стили при попадании в viewport. Такой подход часто используется для появления блоков при скролле.
inView часто комбинируется с функцией
animate, позволяя запускать анимации строго в момент
появления элемента.
import { inView, animate } from "motion";
inView(".card", ({ target }) => {
animate(
target,
{ opacity: [0, 1], transform: ["translateY(20px)", "translateY(0px)"] },
{ duration: 0.6 }
);
});
Здесь анимация запускается однократно при первом попадании элемента в область видимости.
inView поддерживает настройки, влияющие на момент
срабатывания:
inView(element, callback, {
root: null,
margin: "0px",
amount: 0.5
});
Определяет контейнер пересечения. По умолчанию используется viewport.
null — браузерное окноПозволяет расширять или сужать область срабатывания за счёт отступов.
margin: "-100px 0px -100px 0px"
Отрицательные значения ускоряют триггер, положительные — задерживают.
Определяет долю видимого элемента, необходимую для активации:
0 — достаточно появления 1 пикселя1 — элемент должен быть полностью видимЧасто требуется запуск анимации только один раз. Это достигается через возврат функции очистки или внутреннюю логику:
inView(".item", ({ target, stop }) => {
animate(target, { opacity: [0, 1] });
stop();
});
После вызова stop() наблюдение прекращается,
предотвращая повторные срабатывания.
Callback может реагировать на оба состояния элемента.
inView(".panel", ({ isVisible, target }) => {
if (isVisible) {
target.classList.add("active");
} else {
target.classList.remove("active");
}
});
Такой подход используется для синхронизации UI с прокруткой: закрепление элементов, изменение навигации, динамическая подгрузка данных.
inView поддерживает селекторы, автоматически применяя
наблюдение ко всем совпадениям.
inView(".feature", ({ target }) => {
animate(target, { opacity: [0, 1], y: [30, 0] });
});
Каждый элемент обрабатывается независимо, с собственным observer-ом.
Основой работы является IntersectionObserver,
который:
При этом библиотека оптимизирует подписки, объединяя наблюдение там, где это возможно, снижая нагрузку при большом количестве узлов.
Элементы интерфейса появляются по мере прокрутки:
inView(".section", ({ target }) => {
animate(target, { opacity: [0, 1], scale: [0.95, 1] });
});
inView(".counter", ({ target }) => {
animate(0, 100, {
duration: 2,
onUpdate: (latest) => {
target.textContent = Math.round(latest);
}
});
});
inView(".load-trigger", async ({ target }) => {
const data = await fetch("/api/data");
target.innerHTML = await data.text();
});
Часто inView используется без прямых анимаций, только
для переключения классов:
inView(".nav", ({ isVisible, target }) => {
target.classList.toggle("sticky", !isVisible);
});
Такой подход позволяет делегировать визуальную часть CSS, оставляя JavaScript только для логики наблюдения.
При изменении DOM структура наблюдение можно пересоздавать или применять к новым элементам. Это важно в SPA и при виртуальном рендеринге списков.
function observeItems() {
inView(".item", ({ target }) => {
animate(target, { opacity: [0, 1] });
});
}
observeItems();
При использовании кастомного скролла важно явно указывать
root:
inView(".card", ({ target }) => {
animate(target, { x: [50, 0] });
}, {
root: document.querySelector(".scroll-container")
});
Это позволяет корректно отслеживать элементы внутри вложенных прокручиваемых областей.
Комбинация margin и amount используется для
создания сложных сценариев активации:
inView(".image", ({ target }) => {
animate(target, { filter: ["blur(10px)", "blur(0px)"] });
}, {
margin: "-20% 0px",
amount: 0.3
});
Каждый вызов inView создаёт наблюдатель, который
существует до:
Это важно учитывать в долгоживущих интерфейсах, чтобы избегать накопления наблюдателей при динамических рендерах.