Контроллер reveal-эффектов
createRevealController(options?) — сканирует DOM на элементы, подходящие под селектор, и переключает класс/атрибут на каждом по мере появления во viewport, без вызова useElementVisibility()/ observe() на каждый элемент. Построен целиком поверх пулящегося обсервера createVisibilityEngine — MutationObserver (по умолчанию следит за document.body) подхватывает элементы, подходящие под selector, появившиеся позже (например, блок v-for, отрендеренный после асинхронного запроса), и снимает наблюдение с элементов, удалённых из DOM.
function createRevealController(options?: RevealControllerOptions): RevealControllerimport { createRevealController } from '@macrulez/inview-core'
const controller = createRevealController({
selector: '[data-reveal], .reveal', // значение по умолчанию
stagger: { step: 70, max: 4 },
})
// позже, например при демонтаже приложения
controller.destroy().reveal {
opacity: 0;
transition: opacity 0.4s ease-out;
transition-delay: var(--reveal-delay, 0ms);
}
.reveal.in {
opacity: 1;
}Опции
RevealControllerOptions
| Поле | Тип | По умолчанию |
|---|---|---|
selector | string | '[data-reveal], .reveal' |
activeClass | string | null | 'in' — null отключает переключение класса |
activeAttribute | string | null | null — булев data-атрибут, выставляется вместе с activeClass |
once | boolean | true — в отличие от false у useElementVisibility; reveal-эффектам это почти всегда нужно |
threshold | number | number[] | 0 |
rootMargin | string | '0px' |
root | Element | null | null |
stagger | RevealStaggerOptions | false | {} — false отключает |
watchMutations | boolean | true |
watchRoot | Element | document.body |
onEnter / onLeave | (el: Element, info: IntersectionInfo) => void | — та же форма info, что в Движке видимости, первым аргументом — сам элемент, потому что один контроллер следит за многими |
pool | ObserverPool | общий пул по умолчанию |
RevealStaggerOptions
Расширяет StaggerDelayOptions (step, max, unit, mode) двумя дополнительными полями:
| Поле | Тип | По умолчанию |
|---|---|---|
cssVar | string | null | '--reveal-delay' — null полностью отключает запись CSS-переменной |
applyInlineDelay | boolean | true |
По умолчанию вычисленная задержка записывается сразу двумя способами: как 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" | индекс стаггера считается внутри названной группы, а не глобально |
<div class="reveal" data-reveal-once="false" data-reveal-threshold="0.4">
Переключается туда-обратно, вместо того чтобы остановиться после первого показа.
</div>Возвращаемое значение
refresh()
() => void
Повторно сканирует selector на ещё не отслеживаемые элементы. Редко нужен — watchMutations (включён по умолчанию) уже делает это автоматически — полезен сразу после синхронного изменения DOM, если watchMutations выключен.
destroy()
() => void
Отключает MutationObserver и снимает наблюдение со всех отслеживаемых на данный момент элементов.
Пример: reveal-проход по всей странице
Тот самый сценарий, для которого всё это существует — около 100 элементов с классом на нескольких динамически рендерящихся списках, часть из которых появляется после асинхронного запроса:
import { createRevealController } from '@macrulez/inview-core'
const controller = createRevealController({
stagger: { step: 60, max: 4 },
})<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-хуки).