Виртуальный скроллинг применяется для эффективного отображения больших списков данных, когда одновременный рендер всех элементов приводит к перегрузке DOM, падению производительности и чрезмерному потреблению памяти. Основная идея заключается в том, что в DOM присутствует ограниченное число элементов, соответствующее видимой области плюс небольшой буфер, а остальной контент динамически переиспользуется при прокрутке.
При классическом рендеринге список из десятков тысяч элементов создаёт значительную нагрузку на браузер. Каждый элемент занимает память, участвует в layout-расчётах и вызывает перерасчёт стилей при изменениях.
Виртуальный скроллинг заменяет полный набор элементов фиксированным окном:
Ключевым становится управление позиционированием контейнера, имитирующего полную высоту списка.
Базовая архитектура состоит из трёх уровней:
Высота spacer рассчитывается как:
totalHeight = itemHeight * totalItems
или в случае переменной высоты — динамически, через кэширование измерений.
Реальные элементы позиционируются поверх spacer с помощью
transform: translateY(...).
При прокрутке вычисляется индекс первого и последнего отображаемого элемента:
startIndex = Math.floor(scrollTop / itemHeight)
endIndex = startIndex + visibleCount + buffer
Buffer добавляется для сглаживания прокрутки и предотвращения “пустых” областей при быстрых скроллах.
При переменной высоте элементов используется предварительная карта высот и бинарный поиск по накопленной сумме.
Основной оптимизацией является переиспользование DOM-узлов. Вместо создания новых элементов применяется пул:
Это снижает количество операций layout и repaint.
Библиотека Velocity.js используется для плавного управления
визуальным перемещением списка и отдельных элементов. Вместо прямого
изменения scrollTop или мгновенного обновления transform
применяется анимация, управляемая через Velocity.
Основные сценарии применения:
Velocity работает быстрее классических jQuery-анимаций за счёт
использования requestAnimationFrame и оптимизированного
движка изменения CSS-свойств.
Пример интеграции смещения контейнера:
Velocity(container, {
translateY: -offset
}, {
duration: 120,
easing: "easeOutQuad"
});
Виртуальный скролл часто отделяет фактический scroll контейнера от визуального положения элементов. Возможны два подхода:
scrollTop управляет диапазоном, а элементы смещаются через transform.
scrollTop игнорируется, вместо него используется внутреннее состояние:
let scrollPosition = 0;
container.addEventListener('wheel', (e) => {
scrollPosition += e.deltaY;
render(scrollPosition);
});
Velocity применяется для сглаживания переходов:
Velocity(state, { position: target }, {
duration: 200,
progress: () => render(state.position)
});
При резкой прокрутке важно предотвращать частое пересоздание диапазона. Используются техники:
Буфер обычно равен 2–3 экранам:
buffer = visibleCount * 2
Наиболее сложный сценарий связан с элементами разной высоты. Решение строится на:
Структура кэша:
const heights = new Map();
const offsets = [];
Пересчёт offsets:
offsets[i] = offsets[i - 1] + heights[i - 1];
Velocity может использоваться для сглаживания изменения позиции при перерасчёте списка, предотвращая визуальные скачки.
Частая ошибка виртуального скроллинга — чередование чтения и записи DOM в одном цикле, что вызывает layout thrashing.
Разделение фаз:
Velocity помогает отделить визуальные изменения от логики расчётов.
Для позиционирования элементов применяется исключительно transform:
Пример:
.item {
position: absolute;
width: 100%;
will-change: transform;
}
item.style.transform = `translateY(${y}px)`;
Velocity позволяет эмулировать инерционный скролл, особенно в кастомных реализациях:
Velocity(scrollState, {
velocity: 0
}, {
duration: 500,
easing: "easeOutSine"
});
Параллельно вычисляется текущая позиция и корректируется диапазон видимых элементов.
При обновлении данных важно избегать полного rerender:
Структура обновления:
for (let i = start; i <= end; i++) {
const el = pool[i - start];
el.textContent = data[i];
}
Velocity применяется для анимации изменения содержимого:
Velocity(el, "fadeIn", { duration: 80 });
Основные факторы деградации:
Оптимальный баланс достигается при: