Skip to content

Скролл-движок

createScrollEngine(target?, options?) — framework-agnostic скролл-движок для окна или прокручиваемого элемента. Батчит DOM-события скролла в один общий requestAnimationFrame-цикл и уведомляет подписчиков только тогда, когда вычисленное состояние реально изменилось.

ts
function createScrollEngine(
  target?: Window | HTMLElement,
  options?: ScrollEngineOptions,
): ScrollEngine

target по умолчанию — window на клиенте. Если window нет (серверный рендеринг) или target не передан вовсе, возвращается no-op движок со статичным нулевым состоянием вместо ошибки.

Опции

idleTimeout

number · по умолчанию: 150

Сколько миллисекунд бездействия скролла должно пройти, прежде чем isScrolling вернётся в falsevelocity сбросится в 0).

Возвращаемое значение

getState()

() => ScrollState

Синхронно читает текущее состояние, без подписки.

subscribe(callback)

(callback: (state: ScrollState) => void) => () => void

Сразу вызывает callback с текущим состоянием, затем — при каждом изменении. Возвращает функцию отписки.

scrollTo(position, options?)

(position: number | { x?: number; y?: number }, options?: ScrollToOptions) => void

Скроллит к позиции. Простое number задаёт вертикальный отступ (top). options.behavior по умолчанию 'smooth', если только prefersReducedMotion() не вернул true — тогда используется 'auto'.

destroy()

() => void

Убирает слушатель скролла, очищает таймер простоя, отписывается от общего rAF-цикла и очищает всех подписчиков.

ScrollState

Объект состояния, передаваемый в subscribe() и возвращаемый getState():

ПолеТипПримечание
x, ynumberТекущий отступ скролла.
direction'up' | 'down' | 'left' | 'right' | nullВычисляется из последней дельты скролла — меняется только при ненулевой дельте.
progressnumber0..1, по вертикальному диапазону скролла, если цель прокручивается по вертикали, иначе по горизонтальному, иначе 0.
velocitynumberПикселей за кадр, вычисляется по пройденному расстоянию между двумя тиками rAF.
isScrollingbooleantrue пока идёт скролл, снова false через idleTimeout мс без скролла.

Пример:

ts
import { createScrollEngine } from '@macrulez/inview-core'

const scroll = createScrollEngine(window, { idleTimeout: 200 })

const unsubscribe = scroll.subscribe((state) => {
  document.title = `${Math.round(state.progress * 100)}% прокручено`
})

// позже
scroll.scrollTo({ y: 0 })
unsubscribe()
scroll.destroy()

Нужна реактивная версия? Смотрите Реактивное состояние скролла для Vue или React-хуки для React — оба оборачивают этот же движок.