← Назад к вопросам

Как отключить анимацию за пределами Viewport?

1.0 Junior🔥 121 комментариев
#Оптимизация и производительность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Отключение анимаций за пределами Viewport

Зачем это нужно

Анимации элементов, которые находятся вне видимой области (Viewport), напрасно потребляют ресурсы браузера и батареи устройства. Отключение таких анимаций улучшает производительность приложения, особенно на мобильных устройствах и при наличии множества анимируемых элементов.

Способ 1: Intersection Observer API

Самый современный и рекомендуемый подход — использование Intersection Observer API, который позволяет отслеживать видимость элемента в Viewport:

function observeElement(element) {
  const observer = new IntersectionObserver(
    (entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          // Элемент видим — запускаем анимацию
          entry.target.classList.add("animate-in");
        } else {
          // Элемент невидим — останавливаем анимацию
          entry.target.classList.remove("animate-in");
        }
      });
    },
    { threshold: 0.1 }
  );

  observer.observe(element);
}

// Применяем ко всем элементам с классом .animated
document.querySelectorAll(".animated").forEach(observeElement);

Способ 2: CSS-решение с prefers-reduced-motion

Учитываем предпочтения пользователя по редукции движений:

const prefersReducedMotion = window.matchMedia(
  "(prefers-reduced-motion: reduce)"
).matches;

if (prefersReducedMotion) {
  document.body.classList.add("reduce-motion");
}

// В CSS:
// .animate-in { animation: slideIn 0.5s ease-out; }
// .reduce-motion .animate-in { animation: none; }

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

Объединяем Intersection Observer с проверкой редукции движений:

class AnimationManager {
  constructor() {
    this.prefersReducedMotion = window.matchMedia(
      "(prefers-reduced-motion: reduce)"
    ).matches;
  }

  observe(element) {
    if (this.prefersReducedMotion) {
      return; // Не наблюдаем, если движения отключены
    }

    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          entry.target.style.animationPlayState = entry.isIntersecting
            ? "running"
            : "paused";
        });
      },
      { threshold: 0.1 }
    );

    observer.observe(element);
  }
}

const manager = new AnimationManager();
document.querySelectorAll(".animated").forEach((el) =>
  manager.observe(el)
);

Способ 4: React Hook для Intersection Observer

Для React приложений рекомендуется создать кастомный хук:

function useInView(ref, options = {}) {
  const [isInView, setIsInView] = React.useState(false);

  React.useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        setIsInView(entry.isIntersecting);
      },
      { threshold: 0.1, ...options }
    );

    if (ref.current) {
      observer.observe(ref.current);
    }

    return () => observer.disconnect();
  }, [ref, options]);

  return isInView;
}

// Использование:
function AnimatedComponent() {
  const ref = React.useRef(null);
  const isInView = useInView(ref);

  return (
    <div
      ref={ref}
      className={isInView ? "animate-in" : ""}
    >
      Анимированный контент
    </div>
  );
}

Лучшие практики

  1. Используйте Intersection Observer — это стандарт W3C, поддерживается всеми современными браузерами
  2. Учитывайте prefers-reduced-motion — соблюдайте WCAG рекомендации для доступности
  3. Установите правильный threshold — определите, на сколько процентов элемент должен быть видимым перед запуском анимации
  4. Отписывайтесь от Observer — вызывайте disconnect() в cleanup функции для предотвращения утечек памяти
  5. Тестируйте на мобильных устройствах — убедитесь, что оптимизация работает корректно

Совместимость

Intersection Observer API поддерживается всеми современными браузерами. Для старых браузеров (IE 11) можно использовать polyfill: intersection-observer npm пакет.

Как отключить анимацию за пределами Viewport? | PrepBro