Переиспользуемые анимации в Velocity.js строятся вокруг идеи отделения описания поведения анимации от конкретного DOM-элемента и момента её вызова. Такой подход устраняет дублирование, упрощает поддержку интерфейса и позволяет масштабировать анимационную систему без роста связности между компонентами.
Основная проблема прямого использования анимаций заключается в повторении одинаковых наборов параметров:
Velocity(element, {
opacity: 1,
translateY: 0
}, {
duration: 300,
easing: "easeOutQuad"
});
При появлении нескольких однотипных элементов подобные блоки начинают копироваться по всему коду. Любое изменение поведения требует правки множества мест.
Переиспользование достигается вынесением конфигурации в отдельные сущности, которые можно вызывать многократно.
Наиболее простой способ организации переиспользуемых анимаций — создание функций-обёрток.
const fadeInUp = (element) => {
return Velocity(element, {
opacity: 1,
translateY: 0
}, {
duration: 300,
easing: "easeOutQuad"
});
};
Такая функция инкапсулирует поведение и превращает анимацию в переиспользуемый блок логики. Вся настройка скрыта внутри функции, интерфейс использования становится минимальным.
Жёстко зашитые значения ограничивают гибкость. Следующий уровень абстракции — параметризация.
const fadeInUp = (element, options = {}) => {
const {
duration = 300,
distance = 20,
easing = "easeOutQuad"
} = options;
return Velocity(element, {
opacity: 1,
translateY: 0
}, {
duration,
easing,
begin: () => {
element.style.transform = `translateY(${distance}px)`;
}
});
};
Параметры позволяют использовать одну и ту же анимацию в разных контекстах без дублирования логики.
Когда количество анимаций растёт, функции-обёртки становятся громоздкими. Более гибкий подход — фабрики, возвращающие настроенные анимационные функции.
const createFadeAnimation = (config = {}) => {
const {
duration = 300,
easing = "easeOutQuad"
} = config;
return (element, custom = {}) => {
return Velocity(element, {
opacity: 1
}, {
duration: custom.duration || duration,
easing: custom.easing || easing
});
};
};
const fade = createFadeAnimation({ duration: 500 });
Фабрика разделяет уровень конфигурации и уровень исполнения, создавая двухступенчатую модель управления.
Переиспользование усиливается через композицию. Несколько базовых анимаций объединяются в последовательности или параллельные цепочки.
const fadeIn = (el) =>
Velocity(el, { opacity: 1 }, { duration: 200 });
const slideUp = (el) =>
Velocity(el, { translateY: 0 }, { duration: 300 });
const fadeInSlideUp = async (el) => {
await fadeIn(el);
return slideUp(el);
};
Последовательная композиция формирует более сложные эффекты без увеличения сложности базовых блоков.
Velocity.js поддерживает одновременные анимации через группы вызовов.
const animateCard = (card) => {
Velocity(card.querySelector(".title"), { opacity: 1 }, { duration: 200 });
Velocity(card.querySelector(".content"), {
opacity: 1,
translateY: 0
}, {
duration: 300
});
Velocity(card.querySelector(".actions"), {
opacity: 1
}, {
duration: 150
});
};
Такая структура позволяет собирать сложные интерфейсные переходы из независимых анимационных единиц.
При большом количестве эффектов возникает необходимость централизованного реестра.
const animations = {
fadeIn: (el) =>
Velocity(el, { opacity: 1 }, { duration: 200 }),
slideUp: (el) =>
Velocity(el, { translateY: 0 }, { duration: 300 }),
scaleIn: (el) =>
Velocity(el, { scale: 1 }, { duration: 250 })
};
Такой подход превращает анимации в именованные команды, что снижает когнитивную нагрузку при разработке интерфейсов.
const runAnimation = (name, element) => {
const animation = animations[name];
if (!animation) return;
return animation(element);
};
runAnimation("fadeIn", modal);
Появляется слой абстракции между логикой интерфейса и реализацией анимации.
Переиспользуемые анимации часто связаны с состояниями интерфейса: открытие, закрытие, активное состояние, скрытие.
const modalAnimations = {
open: (el) =>
Velocity(el, {
opacity: 1,
scale: 1
}, {
duration: 300
}),
close: (el) =>
Velocity(el, {
opacity: 0,
scale: 0.9
}, {
duration: 200
})
};
Каждое состояние становится отдельным переиспользуемым блоком поведения.
Velocity.js поддерживает очереди, что позволяет выстраивать повторяемые сценарии без ручного управления временем.
const openPanel = (el) => {
Velocity(el, "fadeIn", { duration: 200 });
Velocity(el, { height: "300px" }, { duration: 300 });
Velocity(el, { opacity: 1 }, { duration: 150 });
};
Такие последовательности можно оборачивать в функции и использовать как готовые сценарии интерфейса.
Часто переиспользуемая анимация включает подготовку DOM-состояния.
const prepareElement = (el, hiddenHeight) => {
el.style.overflow = "hidden";
el.style.height = hiddenHeight;
el.style.opacity = 0;
el.style.transform = "translateY(20px)";
};
const reveal = (el) => {
prepareElement(el, "0px");
return Velocity(el, {
opacity: 1,
translateY: 0,
height: "auto"
}, {
duration: 350
});
};
Подготовка состояния становится частью переиспользуемого блока.
Анимации могут быть связаны с DOM-метаданными, что позволяет отделить логику от вызовов в коде.
document.querySelectorAll("[data-animate]").forEach((el) => {
const type = el.dataset.animate;
if (type === "fade-in") {
Velocity(el, { opacity: 1 }, { duration: 200 });
}
if (type === "slide-up") {
Velocity(el, { translateY: 0 }, { duration: 300 });
}
});
Такой подход делает анимации декларативными и облегчает повторное использование в шаблонах.
При развитии интерфейса набор переиспользуемых анимаций становится отдельным слоем системы. Он отделяется от компонентов и бизнес-логики, формируя независимый модуль поведения интерфейса.
export const uiAnimations = {
fadeInUp: (el) =>
Velocity(el, { opacity: 1, translateY: 0 }, { duration: 300 }),
fadeOut: (el) =>
Velocity(el, { opacity: 0 }, { duration: 200 }),
popIn: (el) =>
Velocity(el, { scale: 1 }, { duration: 250 })
};
Такой модуль может использоваться во всём приложении, сохраняя единообразие анимационного поведения и снижая вероятность расхождения эффектов между различными частями интерфейса.