Ленивая загрузка изображений

Muuri — это мощная библиотека для создания динамических сеток в JavaScript с поддержкой перетаскивания, анимаций и адаптивного расположения элементов. При работе с большим количеством изображений важно оптимизировать загрузку контента, чтобы улучшить производительность и снизить время отклика страницы. Ленивая загрузка (lazy loading) позволяет загружать изображения только тогда, когда они становятся видимыми в области просмотра.

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


Подключение и базовая настройка

  1. Подключение Muuri через npm или CDN:
<script src="https://cdn.jsdelivr.net/npm/muuri@0.10.5/dist/muuri.min.js"></script>
  1. HTML-разметка сетки:
<div class="grid">
  <div class="item">
    <div class="item-content">
      <img data-src="image1.jpg" alt="Image 1" class="lazy">
    </div>
  </div>
  <div class="item">
    <div class="item-content">
      <img data-src="image2.jpg" alt="Image 2" class="lazy">
    </div>
  </div>
</div>

Важное отличие: вместо атрибута src используется data-src. Это позволяет избежать загрузки всех изображений сразу.

  1. Инициализация сетки:
const grid = new Muuri('.grid', {
  layout: {
    fillGaps: true
  }
});

Реализация ленивой загрузки

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

Современный и эффективный способ реализовать ленивую загрузку — Intersection Observer API. Он позволяет отслеживать, когда элемент становится видимым в окне браузера.

const lazyImages = document.querySelectorAll('img.lazy');

const observer = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.remove('lazy');
      img.addEventListener('load', () => {
        // Обновление расположения сетки после загрузки изображения
        grid.refreshItems().layout();
      });
      observer.unobserve(img);
    }
  });
}, {
  rootMargin: '100px 0px', // загрузка немного заранее
  threshold: 0.1
});

lazyImages.forEach(img => observer.observe(img));

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

  • rootMargin позволяет подгружать изображения заранее, предотвращая появление пустых областей.
  • Вызов grid.refreshItems().layout() после загрузки гарантирует корректное перераспределение элементов сетки.
  • observer.unobserve(img) освобождает ресурсы после загрузки элемента.

Обработка динамически добавляемых элементов

Muuri часто используется для сеток с динамическим контентом. Если новые элементы добавляются после инициализации, необходимо наблюдать их также:

function observeNewItems(newItems) {
  newItems.forEach(item => {
    const img = item.querySelector('img.lazy');
    if (img) observer.observe(img);
  });
}

// Пример добавления нового элемента
const newItemHTML = `
<div class="item">
  <div class="item-content">
    <img data-src="image3.jpg" alt="Image 3" class="lazy">
  </div>
</div>`;
const tempDiv = document.createElement('div');
tempDiv.innerHTML = newItemHTML;
const newItemElement = tempDiv.firstChild;

grid.add(newItemElement);
observeNewItems([newItemElement]);

Это гарантирует, что все новые изображения будут подгружаться лениво и сетка останется корректной.


Анимации и плавная загрузка

Для более плавного появления изображений можно использовать CSS-переходы:

img.lazy {
  opacity: 0;
  transition: opacity 0.5s ease-in-out;
}

img:not(.lazy) {
  opacity: 1;
}

После того как изображение загружается, класс lazy удаляется, и картинка плавно появляется в сетке. Это улучшает визуальное восприятие при динамическом добавлении или прокрутке элементов.


Особенности производительности

  • Отложенная загрузка уменьшает количество сетевых запросов одновременно, что снижает нагрузку на браузер.
  • Обновление сетки после каждой загрузки изображения предотвращает смещение элементов и обеспечивает аккуратное выравнивание.
  • Наблюдение за видимостью с помощью Intersection Observer эффективнее, чем обработка события scroll, так как API оптимизирован для работы с большим количеством элементов.

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

  1. Использовать миниатюры или WebP для ускоренной начальной загрузки.
  2. Группировать обновления сетки, если одновременно загружается несколько изображений, чтобы избежать лишних перерасчетов:
let pendingRefresh = false;

function scheduleRefresh() {
  if (!pendingRefresh) {
    pendingRefresh = true;
    requestAnimationFrame(() => {
      grid.refreshItems().layout();
      pendingRefresh = false;
    });
  }
}
  1. Lazy loading для видео или iframe по аналогии с изображениями, используя data-src и Intersection Observer.
  2. Предварительная загрузка верхней части контента для улучшения восприятия страницы, используя положительный rootMargin.

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