Плавность как фича: сравниваем фреймворки по анимации UI на реальных кейсах

от автора

UI-анимации — это не только про красоту, но и про восприятие, структуру и даже скорость. В этой статье рассматриваются популярные фреймворки для создания анимаций в интерфейсах: CSS, Framer Motion, GSAP и Motion One. Сравнение проводится на реальных кейсах с кодом, примерами и субъективным мнением, где каждый инструмент показывает свои сильные и слабые стороны. В конце — небольшие выводы и неожиданные результаты.

Когда «просто появляется» уже не работает

Пару лет назад дизайнер принес макет с комментарием: «Вот тут кнопка должна мягко появляться, как облако, а потом улетать, как мыльный пузырь». Я кивнул, но подумал: «Это тебе не After Effects». Сегодня такие вещи вполне достижимы прямо в браузере — и с минимальными потерями по производительности. Вопрос в другом: чем именно это делать?

Чтобы разобраться, я взял 4 инструмента, с которыми чаще всего сталкивался в боевых и личных проектах:

  • CSS-анимации

  • GSAP

  • Framer Motion (для React)

  • Motion One (от создателей Framer Motion, но в Vanilla JS)

И сравнил их на трех простых, но показательных кейсах:

  1. Анимация появления элемента

  2. Анимация наведения

  3. Анимация между состояниями (shared element transition)

Кейсы и код

Кейc 1. Плавное появление карточки

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" } );

Framer Motion (React)

<motion.div   initial={{ opacity: 0, y: 20 }}   animate={{ opacity: 1, y: 0 }}   transition={{ duration: 0.4, ease: "easeOut" }} >   Контент </motion.div>

Motion One

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 — гибкий и легкий, но ещё не так широко поддержан.

Кейc 2. Hover-анимации: реакция без тормозов

CSS (старый-добрый способ)

.button {   transition: transform 0.3s ease; } .button:hover {   transform: scale(1.05); }

Framer Motion

<motion.button   whileHover={{ scale: 1.05 }}   transition={{ type: "spring", stiffness: 300 }} >   Нажми меня </motion.button>

GSAP с событиями

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» — кайф.

Кейc 3. Shared element transition: переход между состояниями

Это прям боль, особенно если нужно, чтобы элемент «переезжал» из одного экрана в другой. Типичная история в e-commerce: кликаешь на товар, карточка «перелетает» в детальную страницу — не исчезает и появляется заново, а плавно трансформируется.

Framer Motion (Layout animations в React)

На главной странице с карточками товаров:

<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 сам вычисляет позицию и размер обоих элементов, и делает плавный переход между ними при навигации.

GSAP Flip Plugin (для Vanilla JS или любой библиотеки)

<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? Пиши в комментариях — с радостью поделюсь.


ссылка на оригинал статьи https://habr.com/ru/articles/900664/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *