Ленивая загрузка и AOS

Ленивая загрузка (lazy loading) и анимации на прокрутке (AOS — Animate on Scroll) часто используются совместно для оптимизации производительности и улучшения визуального восприятия интерфейса. Однако их взаимодействие требует аккуратной настройки: отложенная загрузка контента напрямую влияет на момент срабатывания анимаций.


Принцип работы ленивой загрузки

Ленивая загрузка откладывает подгрузку ресурсов (изображений, видео, блоков контента) до момента, когда они становятся близки к области видимости (viewport).

Основные механизмы:

  • атрибут loading="lazy" для изображений
  • использование Intersection Observer API
  • кастомные решения на JavaScript

Пример базовой ленивой загрузки изображения:

<img src="placeholder.jpg" data-src="image.jpg" class="lazy">
const images = document.querySelectorAll('.lazy');

const observer = new IntersectionObserver((entries, obs) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.remove('lazy');
      obs.unobserve(img);
    }
  });
});

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

Проблема синхронизации с AOS

AOS отслеживает положение элементов на странице при инициализации и во время прокрутки. При использовании ленивой загрузки возникают сложности:

Основные проблемы:

  • AOS не “видит” элементы, которые ещё не загружены
  • позиции элементов меняются после загрузки изображений
  • анимации могут не запускаться или срабатывать некорректно

Поведение AOS при динамическом изменении DOM

После инициализации:

AOS.init();

библиотека рассчитывает позиции всех элементов с атрибутами data-aos.

Если контент меняется (например, изображение загрузилось и увеличило высоту блока), требуется пересчёт.


Обновление AOS после ленивой загрузки

Для корректной работы необходимо явно уведомлять AOS об изменениях.

Пересчёт позиций

AOS.refresh();

Пересчитывает позиции элементов без полной переинициализации.

Полная переинициализация

AOS.refreshHard();

Используется при серьёзных изменениях DOM (например, добавление новых элементов).


Интеграция с Intersection Observer

Комбинированный подход:

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const el = entry.target;

      if (el.dataset.src) {
        el.src = el.dataset.src;
      }

      observer.unobserve(el);

      // обновление AOS после загрузки
      AOS.refresh();
    }
  });
});

Использование событий загрузки изображений

Более точный способ — отслеживать момент полной загрузки:

img.onl oad = () => {
  AOS.refresh();
};

Это предотвращает ошибки, связанные с изменением размеров после рендера.


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

Частый вызов AOS.refresh() может негативно влиять на производительность.

Дебаунс обновления

let timeout;

function refreshAOS() {
  clearTimeout(timeout);
  timeout = setTimeout(() => {
    AOS.refresh();
  }, 200);
}

Используется при массовой загрузке изображений.


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

Комбинация data-aos и lazy loading:

<img 
  data-aos="fade-up"
  data-src="image.jpg"
  class="lazy"
  alt=""
>

Последовательность:

  1. элемент появляется в зоне наблюдения
  2. начинается загрузка изображения
  3. после загрузки вызывается AOS.refresh()
  4. AOS корректно применяет анимацию

Влияние CLS (Cumulative Layout Shift)

Ленивая загрузка может вызывать “прыжки” интерфейса, что влияет на анимации.

Решение — резервирование пространства:

img {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
}

или:

<img width="800" height="450">

Это стабилизирует layout и улучшает работу AOS.


Параметры AOS для работы с ленивой загрузкой

Некоторые настройки особенно важны:

AOS.init({
  offset: 120,
  delay: 0,
  duration: 600,
  once: true,
  mirror: false
});

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

  • once: true предотвращает повторные анимации при перерасчётах
  • offset помогает компенсировать задержку загрузки контента

Динамическая подгрузка контента (infinite scroll)

При подгрузке новых элементов:

container.insertAdjacentHTML('beforeend', newContent);

AOS.refreshHard();

Без этого новые элементы не получат анимации.


Комбинация с библиотеками ленивой загрузки

Популярные библиотеки (например, lozad.js, vanilla-lazyload) обычно имеют колбэки:

const observer = lozad('.lazy', {
  loaded: function(el) {
    AOS.refresh();
  }
});

observer.observe();

Ошибки и нестабильное поведение

Типичные ситуации:

  • анимация срабатывает до загрузки изображения
  • элемент “пропадает” из зоны триггера
  • повторные анимации при скролле

Причины:

  • отсутствие AOS.refresh()
  • неправильный порядок инициализации
  • отсутствие размеров у элементов

Правильный порядок инициализации

  1. Инициализация lazy loading
  2. Инициализация AOS
  3. Подписка на события загрузки
  4. Обновление AOS

Пример:

initLazyLoad();

AOS.init();

window.addEventListener('load', () => {
  AOS.refresh();
});

Асинхронная загрузка скриптов

При использовании defer или async важно учитывать:

<script src="aos.js" defer></script>

Инициализация должна происходить после полной готовности DOM:

document.addEventListener('DOMContentLoaded', () => {
  AOS.init();
});

Комбинирование с CSS-анимациями загрузки

Пока изображение не загружено:

.lazy {
  opacity: 0;
  transition: opacity 0.3s;
}

.lazy.loaded {
  opacity: 1;
}

После загрузки:

img.onl oad = () => {
  img.classList.add('loaded');
  AOS.refresh();
};

Практический шаблон интеграции

document.addEventListener('DOMContentLoaded', () => {
  const lazyImages = document.querySelectorAll('.lazy');

  const observer = new IntersectionObserver((entries, obs) => {
    entries.forEach(entry => {
      if (!entry.isIntersecting) return;

      const img = entry.target;
      img.src = img.dataset.src;

      img.onl oad = () => {
        AOS.refresh();
      };

      obs.unobserve(img);
    });
  });

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

  AOS.init({
    once: true
  });
});

Ключевые принципы

  • AOS зависит от актуального layout страницы
  • ленивые ресурсы изменяют геометрию DOM
  • требуется явный пересчёт после загрузки
  • важно минимизировать количество пересчётов
  • стабильный layout — основа корректных анимаций

Баланс между производительностью и UX

Сочетание AOS и ленивой загрузки:

  • снижает нагрузку на сеть и CPU
  • ускоряет первичную отрисовку
  • делает интерфейс визуально динамичным

При неправильной интеграции:

  • ломается логика анимаций
  • ухудшается пользовательский опыт
  • возникают визуальные артефакты

Грамотная синхронизация этих механизмов — ключевой элемент современной фронтенд-архитектуры.