Skip to content

Контроллер reveal-эффектов

createRevealController(options?) — сканирует DOM на элементы, подходящие под селектор, и переключает класс/атрибут на каждом по мере появления во viewport, без вызова useElementVisibility()/ observe() на каждый элемент. Построен целиком поверх пулящегося обсервера createVisibilityEngineMutationObserver (по умолчанию следит за document.body) подхватывает элементы, подходящие под selector, появившиеся позже (например, блок v-for, отрендеренный после асинхронного запроса), и снимает наблюдение с элементов, удалённых из DOM.

ts
function createRevealController(options?: RevealControllerOptions): RevealController
ts
import { createRevealController } from '@macrulez/inview-core'

const controller = createRevealController({
  selector: '[data-reveal], .reveal', // значение по умолчанию
  stagger: { step: 70, max: 4 },
})

// позже, например при демонтаже приложения
controller.destroy()
css
.reveal {
  opacity: 0;
  transition: opacity 0.4s ease-out;
  transition-delay: var(--reveal-delay, 0ms);
}
.reveal.in {
  opacity: 1;
}

Опции

RevealControllerOptions

ПолеТипПо умолчанию
selectorstring'[data-reveal], .reveal'
activeClassstring | null'in'null отключает переключение класса
activeAttributestring | nullnull — булев data-атрибут, выставляется вместе с activeClass
oncebooleantrue — в отличие от false у useElementVisibility; reveal-эффектам это почти всегда нужно
thresholdnumber | number[]0
rootMarginstring'0px'
rootElement | nullnull
staggerRevealStaggerOptions | false{}false отключает
watchMutationsbooleantrue
watchRootElementdocument.body
onEnter / onLeave(el: Element, info: IntersectionInfo) => void— та же форма info, что в Движке видимости, первым аргументом — сам элемент, потому что один контроллер следит за многими
poolObserverPoolобщий пул по умолчанию

RevealStaggerOptions

Расширяет StaggerDelayOptions (step, max, unit, mode) двумя дополнительными полями:

ПолеТипПо умолчанию
cssVarstring | null'--reveal-delay'null полностью отключает запись CSS-переменной
applyInlineDelaybooleantrue

По умолчанию вычисленная задержка записывается сразу двумя способами: как CSS-переменная --reveal-delay через bindCSSVar, и как инлайн-стиль transition-delay прямо на элементе. Именно инлайн-стиль реально побеждает в каскаде — правило вроде .card { transition: ... }, объявленное позже (например, transition при hover), иначе молча перебило бы transition-delay: var(--reveal-delay) без !important. CSS-переменная при этом продолжает записываться — она, в отличие от инлайн-стиля, наследуется, что нужно для селектора потомка вроде .reveal > .icon { transition-delay: var(--reveal-delay) }. Передайте applyInlineDelay: false, чтобы записывать только CSS-переменную, или cssVar: null, чтобы выставлять только инлайн-стиль.

Переопределения на уровне элемента

Любую опцию контроллера можно переопределить на конкретном элементе через соответствующий data-reveal-* атрибут — значение контроллера применяется только там, где элемент не сказал иначе.

АтрибутПереопределяет
data-reveal-once="false"once
data-reveal-threshold="0,0.5"threshold — через запятую для массива
data-reveal-root-margin="-10%"rootMargin
data-reveal-class="visible"activeClass, только для этого элемента
data-reveal-delay="140"явная задержка стаггера в мс, полностью пропуская вычисленную
data-reveal-group="grid-a"индекс стаггера считается внутри названной группы, а не глобально
html
<div class="reveal" data-reveal-once="false" data-reveal-threshold="0.4">
  Переключается туда-обратно, вместо того чтобы остановиться после первого показа.
</div>

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

refresh()

() => void

Повторно сканирует selector на ещё не отслеживаемые элементы. Редко нужен — watchMutations (включён по умолчанию) уже делает это автоматически — полезен сразу после синхронного изменения DOM, если watchMutations выключен.

destroy()

() => void

Отключает MutationObserver и снимает наблюдение со всех отслеживаемых на данный момент элементов.

Пример: reveal-проход по всей странице

Тот самый сценарий, для которого всё это существует — около 100 элементов с классом на нескольких динамически рендерящихся списках, часть из которых появляется после асинхронного запроса:

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

const controller = createRevealController({
  stagger: { step: 60, max: 4 },
})
vue
<template>
  <!-- отрендерено до завершения запроса — MutationObserver контроллера
       сам подхватит эти элементы, как только они появятся -->
  <div v-for="item in items" :key="item.id" class="reveal">
    {{ item.title }}
  </div>
</template>

Никакого компонента-обёртки на каждый элемент списка, никакого вызова useElementVisibility() на каждый элемент — контроллер сам находит .reveal-элементы, сейчас и позже.

Когда лучше v-reveal/<InView>, а не это

v-reveal и <InView> (Vue) дают контроль на уровне отдельного элемента — свой onEnter для каждого, или реактивный isVisible прямо в шаблоне. createRevealController — для разметки, которую не хочется размечать поэлементно вообще: один вызов покрывает всю страницу (или контейнер), включая элементы, которых ещё нет в DOM. Это также единственный из трёх вариантов, который работает одинаково вообще без фреймворка или из React (отдельного хука для этого нет — см. React-хуки).