Motion / UX / практический материал

Как правильно использовать анимации на сайте

Как добавить анимацию появления блоков, кнопок и интерфейса без тормозов. CSS, Intersection Observer, transform, opacity и reduced motion.

  • Дмитрий Лаврушин
  • 23 августа 2026
  • 5 минут

Анимация объясняет изменение состояния или направляет внимание.

Лаборатория движения

Выберите эффект, перезапустите демонстрацию и скопируйте минимальный CSS.

Четыре безопасных паттерна анимации

СтруктураПонятный порядок блоков
ИнтерфейсОбратная связь на действие
РезультатДвижение поддерживает смысл

Мягкое появление подходит для смысловых блоков без смены геометрии.

CSS / готовый шаблон
.motion-item {
  opacity: 0;
  transition: opacity 420ms ease;
}
.motion-item.is-visible { opacity: 1; }
Дизайн управляет вниманием
Движение полезно, когда помогает заметить изменение состояния и следующий шаг.

Хорошая анимация объясняет действие. Она показывает, что кнопка нажалась, меню открылось, блок появился, а форма успешно отправлена. Плохая анимация существует сама по себе и мешает прочитать страницу.

Для простых состояний достаточно CSS. JavaScript нужен, когда эффект зависит от прокрутки, данных или последовательности действий пользователя.

Безопасная анимация появления блока

css
.reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .5s ease, transform .5s ease;
}

.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
javascript
const items = document.querySelectorAll('.reveal');
const observer = new IntersectionObserver((entries, instance) => {
  entries.forEach((entry) => {
    if (!entry.isIntersecting) return;
    entry.target.classList.add('is-visible');
    instance.unobserve(entry.target);
  });
}, { threshold: 0.15 });

items.forEach((item) => observer.observe(item));

IntersectionObserver сообщает, когда элемент входит в область просмотра. Браузеру не приходится постоянно вычислять положение каждого блока при событии scroll. После первого появления наблюдение отключается.

Матрица вариантов: какой эффект выбрать

ЗадачаЭффектДлительностьТехника
Появление смыслового блокаFade + небольшой translateY400–600 мсCSS + Intersection Observer
Реакция кнопкиScale до 0.97 и возврат120–180 мсCSS :active
Смена вкладкиCrossfade180–260 мсCSS-класс + JavaScript
Раскрытие ответаGrid rows250–350 мс<details> + CSS
Серия карточекStagger60–100 мс между элементамиCSS-переменная --delay

Начните с одного эффекта на один тип действия. Если карточки появляются снизу, не заставляйте соседний блок одновременно вылетать сбоку и вращаться. Повторяемая система воспринимается дороже набора случайных эффектов.

Шаблон токенов движения

Соберите длительности и кривые в переменные. Тогда весь интерфейс можно ускорить или сделать спокойнее в одном месте.

css
:root {
  --motion-fast: 160ms;
  --motion-base: 360ms;
  --motion-slow: 620ms;
  --ease-ui: cubic-bezier(.2, .8, .2, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
}

.button {
  transition:
    transform var(--motion-fast) var(--ease-ui),
    box-shadow var(--motion-fast) var(--ease-ui);
}

.button:hover { transform: translateY(-2px); }
.button:active { transform: translateY(0) scale(.97); }

Шаблон stagger для группы карточек

Каждая карточка получает собственную задержку через CSS-переменную. JavaScript только добавляет класс контейнеру.

html
<div class="feature-grid" data-stagger>
  <article style="--delay: 0ms">Структура</article>
  <article style="--delay: 80ms">Дизайн</article>
  <article style="--delay: 160ms">Разработка</article>
</div>
css
.feature-grid > * {
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity 520ms var(--ease-out) var(--delay),
    transform 520ms var(--ease-out) var(--delay);
}

.feature-grid.is-visible > * {
  opacity: 1;
  transform: none;
}
javascript
const grid = document.querySelector('[data-stagger]');

new IntersectionObserver(([entry], observer) => {
  if (!entry.isIntersecting) return;
  grid.classList.add('is-visible');
  observer.disconnect();
}, { threshold: 0 }).observe(grid);

Используйте threshold: 0 для длинных блоков. Если поставить 0.15, элемент высотой в несколько экранов может никогда не набрать нужную долю пересечения и останется невидимым при открытии страницы по якорю.

Шаблон раскрывающегося блока без библиотеки

Нативный <details> сохраняет работу с клавиатурой. CSS отвечает только за визуальное состояние.

html
<details class="faq-motion">
  <summary>Сколько анимаций нужно странице?</summary>
  <div><p>Столько, сколько объясняют состояние или следующий шаг.</p></div>
</details>
css
.faq-motion > div {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--motion-base) var(--ease-ui);
}

.faq-motion > div > * { overflow: hidden; }
.faq-motion[open] > div { grid-template-rows: 1fr; }

Шаблон переключения вкладок с crossfade

javascript
const tabs = document.querySelectorAll('[data-tab]');
const panels = document.querySelectorAll('[data-panel]');

tabs.forEach((tab) => {
  tab.addEventListener('click', () => {
    tabs.forEach((item) => {
      const active = item === tab;
      item.setAttribute('aria-selected', String(active));
    });

    panels.forEach((panel) => {
      panel.hidden = panel.dataset.panel !== tab.dataset.tab;
    });
  });
});
css
[data-panel] {
  animation: panel-in var(--motion-base) var(--ease-out) both;
}

@keyframes panel-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

Какие свойства лучше анимировать

Старайтесь использовать transform и opacity. Они обычно не меняют геометрию страницы. Анимация width, height, top или left может запускать перерасчет макета и перерисовку.

Для кнопки достаточно от 160 до 250 мс. Для появления крупного блока обычно хватает от 400 до 700 мс. Это не жесткий стандарт. Скорость нужно проверять на реальном интерфейсе и мобильном устройстве.

Не задавайте transition: all. Укажите свойства явно. will-change также не нужно добавлять ко всем элементам. Он резервирует ресурсы и полезен только для действительно тяжелых эффектов.

Где анимация приносит пользу

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

Не скрывайте основной текст до запуска JavaScript. Если скрипт не загрузится, пользователь и робот все равно должны увидеть содержание. Для декоративных эффектов учитывайте prefers-reduced-motion. Пользователь может отключить движение в настройках системы.

Безопасная схема работает наоборот: контент виден по умолчанию, а подготовительное скрытое состояние включается классом html.motion-ready только после запуска JavaScript.

css
.reveal { opacity: 1; transform: none; }

.motion-ready .reveal {
  opacity: 0;
  transform: translateY(20px);
}

.motion-ready .reveal.is-visible {
  opacity: 1;
  transform: none;
}
javascript
document.documentElement.classList.add('motion-ready');

window.addEventListener('load', () => {
  document.querySelectorAll('.reveal').forEach((item) => {
    const rect = item.getBoundingClientRect();
    if (rect.top < window.innerHeight && rect.bottom > 0) {
      item.classList.add('is-visible');
    }
  });
});

Разделяйте микровзаимодействия и декоративное появление. Состояние кнопки после клика важно для управления. Плавный выезд каждой карточки является оформлением. Если нужно сокращать код или ускорять страницу, декоративные эффекты удаляются первыми.

Как проверить результат

Откройте страницу на обычном смартфоне. Проверьте прокрутку, работу меню и скорость реакции кнопок. В DevTools запишите Performance-профиль. Если эффект вызывает постоянный Layout или Paint, упростите его. Затем проверьте CLS и INP в PageSpeed Insights.

Анимация должна поддерживать интерфейс, а не демонстрировать возможности разработчика. Если после ее удаления действие становится менее понятным, эффект полезен. Если ничего не меняется, его можно убрать.

Доказательная база

Источники, на которые можно опереться