Skip to content

Движок видимости ​

createVisibilityEngine(pool?) — оборачивает пулящийся IntersectionObserver определением стороны появления/исчезновения и поддержкой однократного срабатывания, без какой-либо реактивности, завязанной на фреймворк.

ts
function createVisibilityEngine(pool?: ObserverPool): VisibilityEngine

pool по умолчанию — общий синглтон observerPool (см. ниже) — передайте свой new ObserverPool(), чтобы изолировать обсерверы от остального приложения, что в основном полезно в тестах.

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

observe(element, options, callback) ​

(element: Element, options: VisibilityObserveOptions, callback: (info: IntersectionInfo) => void) => () => void

Начинает наблюдение за element. Возвращает функцию отписки.

destroy() ​

() => void

Выполняет все зарегистрированные функции отписки и очищает их.

VisibilityObserveOptions ​

ПолеТипПо умолчанию
rootElement | Document | nullnull
rootMarginstring'0px'
thresholdnumber | number[]0
oncebooleanundefined — при true observe() сам снимает наблюдение после первого isIntersecting: true.
onEnter(info: IntersectionInfo) => void— вызывается, когда info.edge равен 'enter-top' или 'enter-bottom'.
onLeave(info: IntersectionInfo) => void— вызывается, когда info.edge равен 'leave-top' или 'leave-bottom'.

IntersectionInfo ​

Передаётся в callback (и в onEnter/onLeave) при каждом изменении:

ПолеТипПримечание
isIntersectingboolean
intersectionRationumber
boundingClientRectDOMRectLike
edgeIntersectionEdge | undefined'enter-top' | 'enter-bottom' | 'leave-top' | 'leave-bottom' — устанавливается только когда isIntersecting отличается от предыдущего вызова.

Пример:

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

const visibility = createVisibilityEngine()

const unobserve = visibility.observe(
  document.querySelector('#hero')!,
  { threshold: 0.5, once: true },
  (info) => {
    if (info.isIntersecting) console.log('наполовину видим')
  },
)

Пул обсерверов ​

ObserverPool / observerPool — пулит нативные инстансы IntersectionObserver по (root, rootMargin, threshold) — много элементов, наблюдаемых с одинаковыми опциями, используют один обсервер вместо того, чтобы каждый порождал свой. createVisibilityEngine() по умолчанию использует общий синглтон observerPool.

ts
class ObserverPool {
  observe(
    element: Element,
    options: ObserverPoolOptions,
    callback: (entry: IntersectionObserverEntry) => void,
  ): () => void
}

const observerPool: ObserverPool

ObserverPoolOptions — это { root?, rootMargin?, threshold? }, те же три поля, что выше, с теми же значениями по умолчанию (null/'0px'/0), применяемыми в момент, когда для данного ключа реально создаётся новый нативный IntersectionObserver. Пул отключает и убирает нативный обсервер, как только последний наблюдаемый им элемент снимает наблюдение. В окружении без IntersectionObserver вообще observe() возвращает no-op функцию отписки вместо ошибки.

Ключ пула сравнивает массив threshold по значению ([0, 0.25, 0.5].join(',')), а не по ссылке — новый литерал { threshold: [0, 0.5] } при каждом вызове всё равно переиспользует тот же нативный обсервер для элементов с таким же значением, выносить массив в общую константу самим не нужно.

ObserverPool.stats() ​

() => ObserverPoolStat[]

Отладочная интроспекция пула — сколько нативных инстансов IntersectionObserver реально живы прямо сейчас, и сколько элементов сидит на каждом. Полезно, чтобы убедиться, что пулинг действительно схлопывает обсерверы как ожидается в конкретном приложении, а не незаметно фрагментируется из-за того, что свежий инлайн-массив threshold на самом деле каждый раз нёс разные значения.

ts
interface ObserverPoolStat {
  key: string // ключ пулинга (root, rootMargin, threshold)
  elementCount: number
}
ts
import { observerPool } from '@macrulez/inview-core'

observerPool.stats()
// [{ key: 'window|0px|0,0.5', elementCount: 12 }, { key: 'r|0px|0', elementCount: 3 }]

Нужна реактивная версия? Смотрите Видимость элемента для Vue или React-хуки для React — оба оборачивают этот же движок и используют тот же пул. Нужно обработать много элементов — включая те, которых ещё нет в DOM — без вызова composable/hook на каждый? Смотрите Контроллер reveal-эффектов.