Packery — это JavaScript-библиотека для динамического размещения элементов на странице в виде сетки, оптимизирующей пространство. Основная идея заключается в том, чтобы элементы автоматически “упаковывались” в доступную область, избегая пустот и создавая эффект мозаики. В отличие от стандартных сеток, Packery не требует фиксированных рядов и колонок — каждый элемент может иметь произвольные размеры, а библиотека сама вычисляет его оптимальное положение.
Базовая структура страницы для работы с Packery состоит из контейнера и набора элементов:
<div class="grid">
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
</div>
.grid {
position: relative;
}
.grid-item {
width: 100px;
height: 100px;
float: left;
}
Инициализация Packery выполняется через конструктор:
var grid = document.querySelector('.grid');
var pckry = new Packery(grid, {
itemSelector: '.grid-item',
gutter: 10,
percentPosition: true
});
Ключевые параметры:
itemSelector — CSS-селектор для элементов сетки.gutter — промежуток между элементами.percentPosition — использование процентов вместо
фиксированных пикселей для позиционирования.В современном фронтенде часто используется виртуальный DOM (например, в React или Vue), что меняет подход к управлению элементами Packery. Виртуальный DOM создаёт абстракцию реальных DOM-узлов и минимизирует количество операций изменения DOM, что повышает производительность. Packery же напрямую манипулирует реальными DOM-элементами, поэтому при интеграции важно учитывать синхронизацию состояний виртуального и реального DOM.
Основные моменты интеграции:
useEffect:import { useEffect, useRef } from 'react';
import Packery from 'packery';
function Grid({ items }) {
const gridRef = useRef(null);
const pckryRef = useRef(null);
useEffect(() => {
pckryRef.current = new Packery(gridRef.current, {
itemSelector: '.grid-item',
gutter: 10
});
}, []);
useEffect(() => {
pckryRef.current.reloadItems();
pckryRef.current.layout();
}, [items]);
return (
<div ref={gridRef} className="grid">
{items.map((item) => (
<div key={item.id} className="grid-item">{item.content}</div>
))}
</div>
);
}
appended(elements) — для добавления новых элементов в
Packery.remove(elements) — для удаления элементов.layout() — перестроение сетки после изменений.const newItem = document.createElement('div');
newItem.className = 'grid-item';
gridRef.current.appendChild(newItem);
pckryRef.current.appended(newItem);
pckryRef.current.layout();
transition в CSS:.grid-item {
transition: transform 0.4s ease;
}
Это позволяет при изменении порядка элементов плавно перемещать их, не ломая визуальный поток.
В виртуальном DOM часто размеры элементов могут определяться
асинхронно, например, после загрузки изображений. Packery требует
знания точных размеров для корректного размещения,
поэтому важно использовать событие imagesLoaded:
import imagesLoaded from 'imagesloaded';
imagesLoaded(gridRef.current, () => {
pckryRef.current.layout();
});
Таким образом, элементы с динамическим контентом корректно встраиваются в сетку без смещения.
useEffect и
refs для синхронизации с DOM.onMounted и
watch для отслеживания изменений данных и вызова методов
Packery.afterUpdate для
перерисовки сетки после изменения списка элементов.Во всех случаях критично разделять логическую модель виртуального DOM и физическую модель Packery, чтобы избежать конфликтов между системой рендеринга и библиотекой.
layout() после любых изменений в
DOM.reloadItems() перед вызовом
layout() при массовых изменениях элементов.imagesLoaded
или аналогичные события.Packery в сочетании с виртуальным DOM требует внимательного
управления жизненным циклом элементов и правильной синхронизации
состояния. Правильное использование методов appended,
remove, layout и reloadItems
обеспечивает стабильное поведение сетки независимо от динамики
интерфейса.