Плавность как фича: сравниваем фреймворки по анимации UI на реальных кейсах
- вторник, 15 апреля 2025 г. в 00:00:03
UI-анимации — это не только про красоту, но и про восприятие, структуру и даже скорость. В этой статье рассматриваются популярные фреймворки для создания анимаций в интерфейсах: CSS, Framer Motion, GSAP и Motion One. Сравнение проводится на реальных кейсах с кодом, примерами и субъективным мнением, где каждый инструмент показывает свои сильные и слабые стороны. В конце — небольшие выводы и неожиданные результаты.
Пару лет назад дизайнер принес макет с комментарием: «Вот тут кнопка должна мягко появляться, как облако, а потом улетать, как мыльный пузырь». Я кивнул, но подумал: «Это тебе не After Effects». Сегодня такие вещи вполне достижимы прямо в браузере — и с минимальными потерями по производительности. Вопрос в другом: чем именно это делать?
Чтобы разобраться, я взял 4 инструмента, с которыми чаще всего сталкивался в боевых и личных проектах:
CSS-анимации
GSAP
Framer Motion (для React)
Motion One (от создателей Framer Motion, но в Vanilla JS)
И сравнил их на трех простых, но показательных кейсах:
Анимация появления элемента
Анимация наведения
Анимация между состояниями (shared element transition)
CSS (чистый подход)
.card {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.4s ease, transform 0.4s ease;
}
.card.visible {
opacity: 1;
transform: translateY(0);
}
<div class="card">Контент</div>
<script>
document.querySelector('.card').classList.add('visible');
</script>
GSAP
import gsap from "gsap";
gsap.fromTo(".card",
{ opacity: 0, y: 20 },
{ opacity: 1, y: 0, duration: 0.4, ease: "power2.out" }
);
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, ease: "easeOut" }}
>
Контент
</motion.div>
import { animate } from "motion";
animate(".card", { opacity: [0, 1], transform: ["translateY(20px)", "translateY(0)"] }, {
duration: 0.4,
easing: "ease-out"
});
Вывод:
CSS — просто и быстро, но ограничено. GSAP — супергибкий, особенно когда нужна последовательность анимаций. Framer Motion — прекрасно работает в React, почти магия. Motion One — гибкий и легкий, но ещё не так широко поддержан.
.button {
transition: transform 0.3s ease;
}
.button:hover {
transform: scale(1.05);
}
<motion.button
whileHover={{ scale: 1.05 }}
transition={{ type: "spring", stiffness: 300 }}
>
Нажми меня
</motion.button>
const btn = document.querySelector('.button');
btn.addEventListener("mouseenter", () => {
gsap.to(btn, { scale: 1.05, duration: 0.3 });
});
btn.addEventListener("mouseleave", () => {
gsap.to(btn, { scale: 1, duration: 0.3 });
});
Комментарий:
CSS — самый производительный, потому что GPU делает всё сам. Но когда нужна упругая, резиновая, «живущая» анимация — тут лучше справляются GSAP или Framer Motion. Особенно Framer: hover-анимации с "spring" — кайф.
Это прям боль, особенно если нужно, чтобы элемент "переезжал" из одного экрана в другой. Типичная история в e-commerce: кликаешь на товар, карточка "перелетает" в детальную страницу — не исчезает и появляется заново, а плавно трансформируется.
На главной странице с карточками товаров:
<Link to={`/product/${id}`}>
<motion.div layoutId={`product-image-${id}`} className="product-card">
<img src="item.jpg" alt="item" />
<p>Супертовар</p>
</motion.div>
</Link>
На детальной странице:
<motion.div layoutId={`product-image-${id}`} className="product-detail">
<img src="item.jpg" alt="item" />
<h2>Супертовар</h2>
<p>Описание товара, характеристики и прочее</p>
</motion.div>
Ключевой момент: layoutId
должен совпадать. Тогда Framer Motion сам вычисляет позицию и размер обоих элементов, и делает плавный переход между ними при навигации.
<div class="product-card" data-id="123">
<img src="item.jpg" />
</div>
import { Flip } from "gsap/Flip";
const card = document.querySelector(".product-card");
const detail = document.querySelector(".product-detail");
const state = Flip.getState(card);
detail.appendChild(card); // перемещаем DOM-элемент
Flip.from(state, {
duration: 1,
ease: "power1.inOut"
});
Комментарий:
Фреймер — магия без боли. Но работает только в пределах React и требует чуть понимания AnimatePresence
. GSAP с Flip — пушка, но требует настройки и оплаты. CSS здесь, честно говоря, просто не справляется.
GSAP идеально подходит для длинных цепочек и сложных последовательных анимаций. Особенно когда всё строится динамически.
Framer Motion ощущается как нативный язык анимаций в React.
Motion One — легковесная альтернатива, которая приятно удивляет.
CSS — быстрый и надёжный, но ограниченный.
Я сделал несколько замеров через Chrome DevTools и увидел, что...
CSS и Motion One — лидеры по FPS и плавности.
Framer Motion — чуть тяжелее, но приемлемо.
GSAP — самый тяжелый, особенно при множестве элементов, но и самый мощный.
Универсального ответа нет. Всё зависит от задачи:
Нужно просто и быстро? CSS
Анимация в React-приложении? Framer Motion
Сложные переходы и динамика? GSAP
Минимализм и гибкость? Motion One
Если вы делаете сложные интерфейсы, обязательно посмотрите, что происходит под капотом. Плавность — это не «вау», это структурный элемент восприятия. И она не должна быть случайной.
Хочешь примеры этих кейсов в CodeSandbox или Stackblitz? Пиши в комментариях — с радостью поделюсь.