Responsive-анимации в интерфейсах строятся не через CSS-only подход,
а через связку JavaScript-логики и медиавыражений. В случае Velocity.js
управление поведением анимаций переносится на уровень условий, где
ключевую роль играет window.matchMedia.
Velocity.js не интерпретирует media queries напрямую, поэтому адаптивность реализуется через внешние проверки состояния viewport и системных предпочтений пользователя.
Основой адаптивной логики выступает 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 });
}
}
При росте проекта условные проверки внутри функций приводят к усложнению кода. Используется централизованный менеджер брейкпоинтов.
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 });
}
Однократная проверка недостаточна при изменении ориентации или
ресайзе окна. Используется подписка на 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 }));
}
}
В мобильной версии последовательности упрощаются, чтобы снизить нагрузку и уменьшить задержки отображения.
Системные настройки пользователя интегрируются через отдельное медиавыражение:
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-решений, поскольку оно связано с доступностью интерфейса.
В сложных интерфейсах адаптивность включает не только ширину экрана, но и предполагаемую мощность устройства.
Распространённый паттерн — снижение количества анимационных свойств на мобильных устройствах:
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 });
}
Такой слой позволяет централизовать управление визуальным поведением интерфейса.
Избыточные проверки viewport внутри каждой анимации приводят к дублированию логики и усложнению поддержки.
Повторное создание matchMedia без кеширования снижает
производительность при частых вызовах.
Игнорирование события изменения media query приводит к рассинхронизации интерфейса при ресайзе.
Жёстко заданные длительности анимаций без адаптации под устройство создают избыточную нагрузку на слабых устройствах.
На уровне архитектуры анимации в Velocity.js выгодно выстраивать как набор декларативных сценариев, где media queries выступают переключателями состояний.
Основные принципы:
Такой подход позволяет поддерживать предсказуемое поведение интерфейса при любых условиях отображения и устройствах.