Media queries в анимациях

Responsive-анимации в интерфейсах строятся не через CSS-only подход, а через связку JavaScript-логики и медиавыражений. В случае Velocity.js управление поведением анимаций переносится на уровень условий, где ключевую роль играет window.matchMedia.

Velocity.js не интерпретирует media queries напрямую, поэтому адаптивность реализуется через внешние проверки состояния viewport и системных предпочтений пользователя.


Базовый механизм: window.matchMedia

Основой адаптивной логики выступает API медиавыражений:

const isMobile = window.matchMedia("(max-width: 768px)").matches;
const isTablet = window.matchMedia("(min-width: 769px) and (max-width: 1024px)").matches;
const isDesktop = window.matchMedia("(min-width: 1025px)").matches;

Эти проверки формируют условия, на основе которых выбираются параметры анимации Velocity.


Принцип разделения анимационной логики

Анимации в Velocity.js целесообразно делить на три уровня:

  • базовая анимация (общая логика)
  • адаптация под экран
  • адаптация под производительность устройства

Такой подход предотвращает дублирование кода и упрощает управление эффектами.

function animatePanel($el) {
  if (window.matchMedia("(max-width: 768px)").matches) {
    Velocity($el, { translateY: 20, opacity: 1 }, { duration: 200 });
  } else {
    Velocity($el, { translateX: 0, opacity: 1, scale: 1 }, { duration: 400 });
  }
}

Переключение анимаций через breakpoint-менеджер

При росте проекта условные проверки внутри функций приводят к усложнению кода. Используется централизованный менеджер брейкпоинтов.

const Breakpoints = {
  mobile: "(max-width: 768px)",
  tablet: "(min-width: 769px) and (max-width: 1024px)",
  desktop: "(min-width: 1025px)"
};

function getBreakpoint() {
  if (window.matchMedia(Breakpoints.mobile).matches) return "mobile";
  if (window.matchMedia(Breakpoints.tablet).matches) return "tablet";
  return "desktop";
}

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

function animateCard($card) {
  const bp = getBreakpoint();

  const animations = {
    mobile: { translateY: 15, opacity: 1 },
    tablet: { translateY: 25, opacity: 1, scale: 1.02 },
    desktop: { translateY: 0, opacity: 1, scale: 1.05 }
  };

  Velocity($card, animations[bp], { duration: 300 });
}

Реакция на изменение viewport

Однократная проверка недостаточна при изменении ориентации или ресайзе окна. Используется подписка на MediaQueryList.

const mq = window.matchMedia("(max-width: 768px)");

mq.addEventListener("change", (e) => {
  if (e.matches) {
    Velocity(".menu", { opacity: 1, translateX: 0 }, { duration: 200 });
  } else {
    Velocity(".menu", { opacity: 1, translateX: -50 }, { duration: 400 });
  }
});

Такой подход позволяет динамически переключать поведение интерфейса без перезагрузки состояния.


Адаптация сложных последовательностей анимаций

В Velocity.js часто используются цепочки анимаций. При адаптивности такие цепочки пересобираются в зависимости от media query.

function animateModal($modal) {
  const mobile = window.matchMedia("(max-width: 768px)").matches;

  if (mobile) {
    Velocity($modal, { opacity: 1, translateY: 10 }, { duration: 200 });
  } else {
    Velocity($modal, { opacity: 1, scale: 1 }, { duration: 300 })
      .then(() => Velocity($modal, { translateY: 0 }, { duration: 200 }));
  }
}

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


prefers-reduced-motion как часть media queries

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

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

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

function fadeIn($el) {
  if (reduceMotion) {
    Velocity($el, { opacity: 1 }, { duration: 0 });
  } else {
    Velocity($el, { opacity: 1, translateY: 10 }, { duration: 250 });
  }
}

Приоритет этого условия выше любых breakpoint-решений, поскольку оно связано с доступностью интерфейса.


Комбинирование media queries и производительности

В сложных интерфейсах адаптивность включает не только ширину экрана, но и предполагаемую мощность устройства.

Распространённый паттерн — снижение количества анимационных свойств на мобильных устройствах:

function safeAnimate($el) {
  const mobile = window.matchMedia("(max-width: 768px)").matches;

  const props = mobile
    ? { opacity: 1 }
    : { opacity: 1, translateY: 20, scale: 1.02 };

  const options = mobile
    ? { duration: 150 }
    : { duration: 350, easing: "ease-out" };

  Velocity($el, props, options);
}

Абстракция адаптивного слоя

При масштабировании проекта вводится слой конфигурации, отделяющий логику анимаций от их применения.

const AnimationConfig = {
  breakpoints: {
    mobile: "(max-width: 768px)",
    desktop: "(min-width: 769px)"
  },

  card: {
    mobile: { translateY: 10, opacity: 1 },
    desktop: { translateY: 0, opacity: 1, scale: 1.03 }
  }
};

function applyAnimation($el, key) {
  const bp = window.matchMedia(AnimationConfig.breakpoints.mobile).matches
    ? "mobile"
    : "desktop";

  Velocity($el, AnimationConfig[key][bp], { duration: 300 });
}

Такой слой позволяет централизовать управление визуальным поведением интерфейса.


Частые ошибки при использовании media queries с Velocity.js

Избыточные проверки viewport внутри каждой анимации приводят к дублированию логики и усложнению поддержки.

Повторное создание matchMedia без кеширования снижает производительность при частых вызовах.

Игнорирование события изменения media query приводит к рассинхронизации интерфейса при ресайзе.

Жёстко заданные длительности анимаций без адаптации под устройство создают избыточную нагрузку на слабых устройствах.


Организация масштабируемой системы адаптивных анимаций

На уровне архитектуры анимации в Velocity.js выгодно выстраивать как набор декларативных сценариев, где media queries выступают переключателями состояний.

Основные принципы:

  • разделение конфигурации и исполнения
  • минимизация логики внутри анимационных функций
  • централизованный контроль breakpoints
  • обязательная поддержка prefers-reduced-motion
  • пересборка анимаций при изменении viewport

Такой подход позволяет поддерживать предсказуемое поведение интерфейса при любых условиях отображения и устройствах.