Angular

Для работы с библиотекой Packery в Angular-проекте сначала необходимо установить её через npm:

npm install packery
npm install imagesloaded

imagesloaded требуется для корректного позиционирования элементов, содержащих изображения, поскольку Packery рассчитывает размеры элементов после их загрузки.

Подключение в компонент:

import { Component, AfterViewInit, ElementRef, ViewChild } from '@angular/core';
import Packery from 'packery';
import imagesLoaded from 'imagesloaded';

Здесь важно использовать AfterViewInit, чтобы инициализация происходила после рендеринга DOM.


Создание контейнера для Packery

HTML-разметка контейнера выглядит следующим образом:

<div #grid class="grid">
  <div class="grid-item">Элемент 1</div>
  <div class="grid-item">Элемент 2</div>
  <div class="grid-item">Элемент 3</div>
  <div class="grid-item">Элемент 4</div>
</div>

Ключевые моменты:

  • Атрибут #grid используется для доступа к контейнеру через ViewChild.
  • Каждый элемент должен иметь класс grid-item.
  • Контейнер не требует фиксированной высоты, Packery рассчитает её автоматически.

CSS для базового оформления:

.grid {
  position: relative;
}

.grid-item {
  width: 100px;
  height: 100px;
  margin: 10px;
  background-color: #3498db;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
}

Инициализация Packery в Angular

В компоненте:

@Component({
  selector: 'app-packery-grid',
  templateUrl: './packery-grid.component.html',
  styleUrls: ['./packery-grid.component.css']
})
export class PackeryGridComponent implements AfterViewInit {
  @ViewChild('grid') grid!: ElementRef;

  private pckry!: Packery;

  ngAfterViewInit() {
    const gridElement = this.grid.nativeElement;

    this.pckry = new Packery(gridElement, {
      itemSelector: '.grid-item',
      gutter: 10,
      percentPosition: true
    });

    // Обеспечение корректного расположения изображений
    imagesLoaded(gridElement, () => {
      this.pckry.layout();
    });
  }

  addItem(content: string) {
    const item = document.createElement('div');
    item.classList.add('grid-item');
    item.textContent = content;

    this.grid.nativeElement.appendChild(item);
    this.pckry.appended(item);
    this.pckry.layout();
  }

  removeItem(index: number) {
    const items = this.pckry.getItemElements();
    if (items[index]) {
      this.pckry.remove(items[index]);
      this.pckry.layout();
    }
  }
}

Особенности и ключевые моменты:

  • itemSelector — CSS-класс элементов, которые будут упорядочены Packery.
  • gutter — отступ между элементами сетки.
  • percentPosition — вычисление позиции элементов в процентах, удобно для адаптивных макетов.
  • Методы append и remove используются для динамического управления элементами сетки.

Работа с динамическими данными

Packery отлично интегрируется с динамическим контентом в Angular, особенно при использовании *ngFor.

Пример с массивом элементов:

items = ['Элемент A', 'Элемент B', 'Элемент C'];

ngAfterViewInit() {
  const gridElement = this.grid.nativeElement;

  this.pckry = new Packery(gridElement, {
    itemSelector: '.grid-item',
    gutter: 10,
    percentPosition: true
  });

  imagesLoaded(gridElement, () => {
    this.pckry.layout();
  });
}

updateGrid() {
  setTimeout(() => {
    this.pckry.reloadItems();
    this.pckry.layout();
  });
}

При изменении массива данных вызывается reloadItems и layout, чтобы сетка корректно пересчитала позиции элементов.


События Packery

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

this.pckry.on('layoutComplete', (laidOutItems) => {
  console.log('Сетка выложена, элементов:', laidOutItems.length);
});

this.pckry.on('removeComplete', (removedItems) => {
  console.log('Элементы удалены:', removedItems.length);
});

Ключевые события:

  • layoutComplete — после завершения перестановки элементов.
  • removeComplete — после удаления элементов.
  • dragItemPositioned — при перемещении перетаскиваемого элемента (требует Draggabilly или взаимодействия с drag&drop).

Интеграция с перетаскиванием

Для реализации drag-and-drop используется библиотека Draggabilly:

npm install draggabilly

Пример инициализации:

import Draggabilly from 'draggabilly';

ngAfterViewInit() {
  const gridElement = this.grid.nativeElement;
  this.pckry = new Packery(gridElement, {
    itemSelector: '.grid-item',
    gutter: 10,
    percentPosition: true
  });

  const items = this.pckry.getItemElements();
  items.forEach((item) => {
    const draggie = new Draggabilly(item);
    this.pckry.bindDraggabillyEvents(draggie);
  });
}

Особенности:

  • bindDraggabillyEvents обеспечивает автоматическое обновление сетки при перемещении элементов.
  • Каждый элемент можно перетаскивать, Packery динамически перестраивает сетку вокруг него.

Адаптивность и медиазапросы

Packery поддерживает адаптивные макеты через процентные размеры или пересчёт позиций при изменении ширины контейнера:

window.addEventListener('resize', () => {
  this.pckry.layout();
});
  • Использование percentPosition: true делает сетку отзывчивой.
  • Рекомендовано пересчитывать layout при изменении размеров контейнера или при динамическом добавлении/удалении элементов.

Настройка и расширенные опции

Параметры Packery позволяют гибко управлять сеткой:

Параметр Описание
itemSelector CSS-класс элементов, которые будут выложены в сетку
gutter Отступ между элементами (px)
percentPosition Расчёт позиции элементов в процентах
stamp Селектор элементов, которые будут исключены из нормального расположения
originLeft Начало сетки слева (true/false)
originTop Начало сетки сверху (true/false)
horizontal Раскладка по горизонтали вместо вертикали

Применение stamp позволяет фиксировать элементы на сетке и обходить их при перестановке других элементов.


Советы по оптимизации

  • Всегда использовать imagesLoaded, если элементы содержат картинки.
  • Для большого числа элементов применять виртуализацию или ленивую загрузку, чтобы избежать падений производительности.
  • При динамическом обновлении элементов использовать методы appended, removed, reloadItems вместо полной перестройки контейнера.
  • Комбинировать с CSS Grid для фоновых сеток и Packery для динамических элементов с различной высотой.

Packery в Angular обеспечивает мощный и гибкий инструмент для динамических, перетаскиваемых и адаптивных сеток, полностью интегрируясь с компонентной архитектурой и системой реактивного обновления данных.