Виртуализация

Packery — это библиотека для динамического размещения элементов на веб-странице с использованием принципа «гибкой сетки» (masonry layout). Она позволяет управлять расположением блоков разной высоты и ширины, обеспечивая плотную компоновку без лишних промежутков. Основной объект библиотеки — это экземпляр Packery, который создается на контейнере с элементами.

const grid = document.querySelector('.grid');
const pckry = new Packery(grid, {
  itemSelector: '.grid-item',
  gutter: 10
});
  • itemSelector — CSS-селектор для элементов, которые будут упорядочиваться.
  • gutter — отступ между элементами.
  • originLeft и originTop — определяют начальную точку отсчета (по умолчанию слева и сверху).

Методика добавления и удаления элементов

Packery позволяет динамически добавлять или удалять элементы без пересоздания всей сетки. Для добавления используется метод appended:

const newItem = document.createElement('div');
newItem.classList.add('grid-item');
grid.appendChild(newItem);
pckry.appended(newItem);

Для удаления элементов применяется метод remove:

const itemToRemove = document.querySelector('.grid-item:first-child');
pckry.remove(itemToRemove);
pckry.layout();

Важно: после удаления необходимо вызвать layout(), чтобы пересчитать позиции остальных элементов.


Перетаскивание элементов (Draggable)

Packery интегрируется с библиотекой Draggabilly для перетаскивания элементов. Создание draggable-элементов выглядит следующим образом:

const draggies = [];
document.querySelectorAll('.grid-item').forEach((item) => {
  const draggie = new Draggabilly(item);
  pckry.bindDraggabillyEvents(draggie);
  draggies.push(draggie);
});
  • bindDraggabillyEvents — связывает Packery с Draggabilly, чтобы при перемещении элементов сетка обновлялась автоматически.
  • Позволяет сохранять плотность сетки, даже когда элементы перемещаются пользователем.

Настройка сетки

Packery поддерживает гибкую конфигурацию сетки:

  • columnWidth — фиксирует ширину колонки. Может быть числом или селектором элемента.
  • rowHeight — устанавливает высоту строки (редко используется, чаще работает на основе реальной высоты элементов).
  • percentPosition — включение вычисления позиций в процентах, полезно для адаптивного дизайна.

Пример адаптивной сетки с процентными позициями:

const pckry = new Packery(grid, {
  itemSelector: '.grid-item',
  columnWidth: '.grid-sizer',
  percentPosition: true
});

События Packery

Packery предоставляет несколько событий для управления жизненным циклом сетки:

  • layoutComplete — срабатывает после завершения компоновки.
  • removeComplete — после удаления элементов.
  • dragItemPositioned — после завершения перетаскивания элемента.

Пример подписки на событие:

pckry.on('layoutComplete', (items) => {
  console.log('Сетка обновлена', items.length, 'элементов');
});

Виртуализация элементов

Для больших сеток с сотнями элементов важно не рендерить все DOM-элементы сразу. Виртуализация позволяет отображать только видимые элементы, улучшая производительность. Packery не имеет встроенной виртуализации, но её можно реализовать вручную:

  1. Определение области видимости — вычислять, какие элементы видны пользователю с помощью IntersectionObserver.
  2. Подгрузка элементов при скролле — добавлять новые блоки в DOM только по мере необходимости.
  3. Удаление невидимых элементов — элементы, вышедшие из области видимости, можно временно удалять, вызывая remove и пересчет layout().

Пример интеграции с IntersectionObserver:

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    const item = entry.target;
    if (entry.isIntersecting) {
      grid.appendChild(item);
      pckry.appended(item);
    } else {
      pckry.remove(item);
    }
  });
}, { root: grid, threshold: 0.1 });

document.querySelectorAll('.grid-item').forEach(item => observer.observe(item));

Оптимизация производительности

  • Использовать CSS-трансформации (transform: translate3d) для анимации, чтобы снизить нагрузку на браузер.
  • Минимизировать пересчёт layout() — вызывать его только после добавления/удаления блока, а не при каждом изменении позиции.
  • Комбинировать Packery с виртуальными списками, если элементов слишком много для одновременного рендеринга.
  • Настраивать gutter и columnWidth так, чтобы уменьшить количество пересчётов сетки при ресайзе окна.

Интеграция с фреймворками

Packery можно использовать с React, Vue и Angular, но важно помнить:

  • Packery напрямую манипулирует DOM, поэтому при использовании React нужно контролировать, чтобы виртуальный DOM не конфликтовал с реальным.
  • Для Vue и Angular рекомендуется оборачивать Packery в компонент с методами mounted, updated и beforeDestroy, чтобы корректно управлять жизненным циклом сетки.

Пример с React:

useEffect(() => {
  const pckry = new Packery(gridRef.current, { itemSelector: '.grid-item' });
  return () => pckry.destroy();
}, []);
  • destroy() освобождает ресурсы и удаляет обработчики событий.

Анимации и стилизация

Packery поддерживает CSS-анимации для плавного перемещения элементов. Для этого достаточно задавать переходы для свойства transform и width/height:

.grid-item {
  transition: transform 0.4s ease, width 0.4s ease, height 0.4s ease;
}
  • Можно комбинировать с динамическим изменением размеров элементов, Packery будет плавно перестраивать сетку.
  • Для сложных анимаций стоит использовать requestAnimationFrame для синхронизации с пересчетом сетки.

Заключение по виртуализации

Виртуализация в Packery требует сочетания стратегий:

  • Использование IntersectionObserver для определения видимых элементов.
  • Добавление и удаление блоков с вызовом методов appended и remove.
  • Минимизация лишних пересчетов layout() для повышения производительности.
  • Оптимизация CSS-анимаций и трансформаций.

Такой подход позволяет создавать крупные, динамичные сетки с сотнями элементов, сохраняя отзывчивость интерфейса и плавность анимации.