Отслеживание видимости
useVisibilityTracker() — отслеживание «вошёл во вьюпорт» / «покинул вьюпорт» по ключу на основе настоящего IntersectionObserver, а не сравнения visibleRange — поэтому точность сохраняется даже при большом буфере overscan или порогах частичной видимости. Не зависит от структуры данных: вы сами решаете, какие элементы observe(), под любым удобным ключом (id строки, индекс, что угодно).
Типичное применение: подсветить пункт навигации/миникарты, элемент оглавления или кнопку «перейти к» на панели управления, пока соответствующая строка реально видна на экране в виртуализированном списке или таблице — мгновенно выключая подсветку, как только строка уходит за пределы экрана.
function useVisibilityTracker(options: UseVisibilityTrackerOptions): UseVisibilityTrackerReturnОпции
root
() => HTMLElement | null
Возвращает контейнер прокрутки для пересечения. Опустите для использования вьюпорта браузера.
rootMargin
string · по умолчанию: '0px'
rootMargin IntersectionObserver — расширяет/сжимает эффективные границы root (например, срабатывать чуть раньше).
threshold
number | number[] · по умолчанию: 0
Доля элемента, которая должна быть видна, чтобы считаться «видимой».
onVisible
(key: string | number) => void
Вызывается, когда отслеживаемый ключ становится видимым.
onHidden
(key: string | number) => void
Вызывается, когда отслеживаемый ключ становится скрытым (в том числе через unobserve, пока был видим).
Возвращаемое значение
visibleKeys
Readonly<Ref<Set<string | number>>>
Ключи, в данный момент пересекающиеся с root.
isVisible
(key: string | number) => boolean
Виден ли key в данный момент.
observe
(el: Element | null, key: string | number) => void
Начать отслеживать элемент под key — привязывайте через callback template-ref.
unobserve
(key: string | number) => void
Прекратить отслеживать key (например, при размонтировании строки).
rootможет разрешиться уже после того, как запустится собственная настройка этого composable (например, template-ref соседнегоVirtualList) — он опрашивается несколько кадров и автоматически перестраивается, если разрешённый элемент root когда-либо меняется (например, после принудительного:key-перемонтирования).
Пример
Отслеживание конкретных строк и отражение их видимости в боковой панели — тот же паттерн, что используется в «Watchlist» демо VirtualList:
<script setup lang="ts">
import { ref } from 'vue'
import { VirtualList, useVisibilityTracker } from 'vue-virtual-scroller-kit'
import type { VirtualListExpose } from 'vue-virtual-scroller-kit'
interface Row {
id: number
title: string
}
const items = ref<Row[]>(
Array.from({ length: 100_000 }, (_, i) => ({ id: i + 1, title: `Row ${i + 1}` })),
)
const listRef = ref<VirtualListExpose | null>(null)
const watchedIds = ref<Set<number>>(new Set([1, 50_000]))
const tracker = useVisibilityTracker({
root: () => listRef.value?.getScrollElement() ?? null,
})
// Строки размонтируются при прокрутке за пределы виртуализированного диапазона, поэтому
// отслеживание/снятие происходит на монтировании/размонтировании, а не в предположении,
// что наблюдаемый элемент остаётся живым.
function onRowMount(el: Element, id: number) {
if (watchedIds.value.has(id)) tracker.observe(el, id)
}
function onRowUnmount(id: number) {
tracker.unobserve(id)
}
</script>
<template>
<VirtualList
ref="listRef"
:items="items"
key-field="id"
:estimated-item-size="48"
style="height: 500px"
>
<template #default="{ item }">
<div
:ref="(el) => el && onRowMount(el as Element, item.id)"
@vue:unmounted="onRowUnmount(item.id)"
:style="{
background:
watchedIds.has(item.id) && tracker.isVisible(item.id) ? '#fef08a' : 'transparent',
}"
>
{{ item.title }}
</div>
</template>
</VirtualList>
</template>