Использование с Vue

Библиотека Muuri предоставляет мощные возможности для создания сеток с анимацией перетаскивания, сортировки и фильтрации элементов. При использовании с Vue необходимо учитывать реактивность и жизненный цикл компонентов, чтобы корректно инициализировать и управлять сеткой.


Установка и подключение

Для начала устанавливается Muuri через npm:

npm install muuri

В компоненте Vue библиотека подключается следующим образом:

import Muuri from 'muuri';

Для динамических сеток лучше инициализировать Muuri после рендера DOM. В Vue 2 это делается в хукe mounted, а в Vue 3 — в onMounted внутри setup.


Инициализация сетки

Простейший пример инициализации:

mounted() {
  this.grid = new Muuri(this.$refs.grid, {
    dragEnabled: true,
    layoutDuration: 400,
    layoutEasing: 'ease'
  });
}

Пояснения ключевых параметров:

  • dragEnabled: true — включает возможность перетаскивания элементов.
  • layoutDuration — продолжительность анимации перестройки сетки.
  • layoutEasing — функция смягчения движения при перестроении сетки.

this.$refs.grid ссылается на DOM-элемент контейнера сетки. Для Vue 3 используется ref из Composition API:

import { ref, onMounted } from 'vue';
import Muuri from 'muuri';

export default {
  setup() {
    const gridRef = ref(null);
    let gridInstance;

    onMounted(() => {
      gridInstance = new Muuri(gridRef.value, {
        dragEnabled: true
      });
    });

    return { gridRef };
  }
}

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

Для сетки с реактивными данными важно корректно обновлять Muuri при добавлении или удалении элементов. Элементы добавляются с помощью метода add:

this.grid.add(this.$refs.newItem);

Удаление осуществляется через remove:

const item = this.grid.get(this.$refs.itemToRemove);
this.grid.remove(item, { removeElements: true });

Важно: после изменения DOM рекомендуется вызвать refreshItems() и layout() для корректного пересчета позиции элементов:

this.grid.refreshItems().layout();

Реактивность Vue и Muuri

Muuri напрямую работает с DOM, а Vue управляет виртуальным DOM. Для синхронизации рекомендуется использовать nextTick, чтобы гарантировать, что все элементы отрендерены:

import { nextTick } from 'vue';

nextTick(() => {
  gridInstance.refreshItems().layout();
});

При работе с массивами Vue 3 можно использовать watch для автоматического обновления сетки при изменении данных:

import { watch } from 'vue';

watch(items, async () => {
  await nextTick();
  gridInstance.refreshItems().layout();
});

События Muuri в Vue

Muuri поддерживает события, которые можно использовать для реакции на действия пользователя. Например:

this.grid.on('dragEnd', (item) => {
  console.log('Элемент перемещен:', item);
});

В Vue 3 с Composition API:

onMounted(() => {
  gridInstance.on('dragEnd', (item) => {
    console.log('Элемент перемещен:', item);
  });
});

Доступны события: add, remove, layoutStart, layoutEnd, dragStart, dragEnd и другие. Использование их позволяет интегрировать Muuri с состоянием приложения Vue.


Фильтрация и сортировка

Muuri позволяет фильтровать элементы по условию:

this.grid.filter((item) => item.getElement().dataset.category === 'active');

Сортировка осуществляется через sort с функцией сравнения:

this.grid.sort((a, b) => {
  return a.getElement().dataset.order - b.getElement().dataset.order;
});

Для динамической сетки Vue можно реализовать реактивную фильтрацию через watch или вычисляемые свойства.


Доступ к отдельным элементам

Для работы с отдельными элементами сетки Muuri предоставляет методы:

  • getItems() — возвращает массив элементов Muuri.
  • get(itemElement) — возвращает объект элемента Muuri по DOM-элементу.
  • show(item) / hide(item) — управление видимостью элементов.

Пример скрытия элементов, не соответствующих условию:

this.grid.getItems().forEach(item => {
  if (item.getElement().dataset.status !== 'active') {
    this.grid.hide(item);
  }
});

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

  • При работе с большими массивами использовать refreshItems() после завершения всех DOM-операций.
  • Использовать layout(false) для мгновенного пересчета без анимации при начальной загрузке.
  • Перетаскивание большого числа элементов может быть оптимизировано отключением событий, которые не требуются.

Интеграция с Vue Router и динамическими страницами

Если сетка находится на компоненте, который подгружается динамически через маршрутизацию, инициализация Muuri должна выполняться только после рендера:

onMounted(() => {
  gridInstance = new Muuri(gridRef.value, { dragEnabled: true });
});

При удалении компонента необходимо уничтожить сетку для освобождения ресурсов:

onBeforeUnmount(() => {
  gridInstance.destroy();
});

Настройка внешнего вида и анимации

Muuri поддерживает индивидуальные настройки элементов:

  • layout — кастомизация поведения перестройки сетки.
  • dragSort — управление тем, как элементы перетаскиваются.
  • CSS-анимации можно комбинировать с Muuri для более плавных переходов.

Пример использования кастомного layout:

const grid = new Muuri(gridRef.value, {
  layout: {
    fillGaps: true,
    horizontal: false
  }
});

Пример полной интеграции с Vue 3

<template>
  <div ref="grid" class="grid">
    <div v-for="item in items" :key="item.id" class="item" :data-order="item.order">
      {{ item.text }}
    </div>
  </div>
</template>

<script>
import { ref, onMounted, nextTick, watch, onBeforeUnmount } from 'vue';
import Muuri from 'muuri';

export default {
  setup() {
    const gridRef = ref(null);
    let gridInstance;

    const items = ref([
      { id: 1, text: 'Элемент 1', order: 1 },
      { id: 2, text: 'Элемент 2', order: 2 },
      { id: 3, text: 'Элемент 3', order: 3 }
    ]);

    onMounted(() => {
      gridInstance = new Muuri(gridRef.value, { dragEnabled: true });
    });

    watch(items, async () => {
      await nextTick();
      gridInstance.refreshItems().layout();
    });

    onBeforeUnmount(() => {
      gridInstance.destroy();
    });

    return { gridRef, items };
  }
};
</script>

<style>
.grid {
  display: flex;
  flex-wrap: wrap;
}
.item {
  width: 100px;
  height: 100px;
  margin: 5px;
  background: #3498db;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>

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