Виртуализация
Двухфазная раскладка для больших списков: карточки внутри видимой области (± буфер overscan) измеряются и расставляются как обычно; карточки вне неё вообще пропускают измерение DOM и упаковываются по оценке размера — так setItems()/relayout() остаются дешёвыми независимо от общего размера списка.
Включение
MasonryOptions.virtualize / scrollContainer / estimateSize
virtualize
boolean | { overscan?: number } · по умолчанию: false
false (или не задано) — каждая карточка всегда измеряется по-настоящему, независимо от размера списка. true включает виртуализацию с дефолтным overscan (600). Объектная форма позволяет явно задать overscan.
scrollContainer
HTMLElement | 'self' | 'window' · по умолчанию: 'window' при direction: 'vertical', 'self' при 'horizontal'
Позицию скролла/размер вьюпорта какого элемента отслеживает движок, чтобы определить видимую область. 'self' — скроллится сам контейнер движка (типичный случай для 'horizontal', где контейнер уже является фиксированным по размеру вьюпортом скролла). 'window' — скроллится страница (типичный случай для 'vertical'). Явный элемент — произвольный скролл-предок вне контейнера (например, скроллируемая панель, внутри которой сидит сетка, но которая не является самим контейнером). Игнорируется при выключенном virtualize.
estimateSize
(item: MasonryItemDescriptor) => number · опционально
Резервная оценка размера по главной оси (px), используется только когда у конкретной карточки нет ни estimatedSize, ни aspectRatio. Игнорируется при выключенном virtualize.
Пример:
const engine = createMasonryEngine(container, {
virtualize: { overscan: 800 }, // отслеживать более широкий буфер, чем дефолтные 600px
scrollContainer: 'window', // скроллится страница, а не сам контейнер
estimateSize: (item) => item.estimatedHeight ?? 200, // резерв для карточек без estimatedSize и aspectRatio
})Определение размера карточки вне зоны видимости
Без virtualize карточка без реального измерения (и без aspectRatio) просто выпадает из раскладки, пока не появится способ её измерить. Один такой случай — карточка, чей элемент это (или содержит) непрогруженный <img>: движок вообще не доверяет его текущему getBoundingClientRect(), поскольку непрогруженный <img> без известных intrinsic-размеров и без CSS aspect-ratio даёт браузерный дефолт 150px, который выглядит как настоящее измерение, но им не является. Вместо ResizeObserver движок сам подписывается на load/error этой картинки и пересчитывает раскладку, когда она сработает. Практический эффект: галерея изображений без единого aspectRatio раскладывается верно и без искажений — просто карточки появляются по одной, по мере догрузки картинок, а не сразу все с оценкой. При включённом virtualize движок вместо этого определяет размер по главной оси через такую цепочку, по порядку:
- Реальное измерение, если у карточки сейчас есть смонтированный элемент (то есть она прямо сейчас внутри видимой области) — кроме карточки с непрогруженной
<img>внутри (см. выше): такая не считается «измеренной», даже будучи смонтированной, и цепочка продолжается со следующего шага. - Последнее известное реальное измерение, если карточка уже измерялась раньше, но с тех пор прокрутилась за пределы экрана и её элемент размонтировался — сохраняется между пересчётами, так что виртуализированная карточка остаётся самокорректирующейся, а не откатывается к сырой оценке каждый раз, когда покидает измеренное окно.
MasonryItemDescriptor.estimatedSize, если задан — имеет приоритет надaspectRatio, так как это явное переопределение на уровне карточки.MasonryItemDescriptor.aspectRatio, если задан — посчитанный размер карточки по поперечной оси, делённый (vertical) или умноженный (horizontal) на это соотношение.MasonryOptions.estimateSize(item), если задан.- Угаданный резерв: среднее по всему, что реально уже измерено, либо собственный размер карточки по поперечной оси (грубое предположение «примерно квадратная»), если вообще ничего ещё не измерено. Этот путь также один раз логирует
console.warn— молчаливое угадывание в виртуализированном списке легко пропустить, пока раскладка не начнёт визуально выглядеть неправильно — задайтеestimatedSize,aspectRatioилиestimateSize, чтобы этого избежать.
Карточки в зоне видимости
engine.getVisibleIds() и поле visibleIds события layout возвращают один и тот же набор — все id, у которых прямо сейчас должен быть реальный DOM-элемент. При выключенном virtualize это каждая упакованная карточка. При включённом — только те, что внутри видимой области (± overscan); компонент/render-prop <MasonryGrid> монтирует обёртку (и вызывает слот #item/функцию children) только для id из этого набора — всё остальное существует исключительно как оценка внутри движка, вообще не в DOM.
engine.on('layout', ({ visibleIds }) => {
console.log(`У ${visibleIds.length} карточек прямо сейчас есть реальный DOM-элемент`)
})