Представьте: вы заходите на сайт премиум-бренда, картинка загружается, появляется плавное затенение, а кнопка при наведении слегка приподнимается. Это создаёт ощущение качества и дороговизны. Теперь представьте обратное: анимации дёргаются, элементы скачут, сайт тормозит — пользователь уходит за 2 секунды. CSS-анимации — мощный инструмент, но неграмотное их использование убивает производительность. Мы сталкивались с проектами, где простая анимация модального окна вызывала пересчёт layout'a на весь документ, что приводило к LCP 4+ секунды на мобильных устройствах. За 5 лет мы оптимизировали анимации на 40+ проектах, снизив Interaction to Next Paint (INP) в среднем на 60%. В этой статье разберём, как правильно подходить к анимациям на CSS: от выбора свойств до оптимизации через GPU, и покажем реальный кейс ускорения hero-секции в 3 раза.
Как CSS-анимации влияют на Core Web Vitals?
Core Web Vitals — метрики, которые Google использует для оценки пользовательского опыта. Анимации напрямую влияют на LCP (Largest Contentful Paint), CLS (Cumulative Layout Shift) и INP (Interaction to Next Paint). Неправильно анимированные элементы могут увеличивать LCP из-за задержек compositing, вызывать CLS при анимации размеров или ухудшать INP из-за частых перерисовок. Оптимизированные CSS-анимации, напротив, помогают сохранить стабильные 60 FPS и не влияют на эти метрики.
Безопасные свойства для анимации
CSS-анимации исполняются на compositor thread — отдельном потоке, не блокирующем JavaScript и rendering. Грамотно написанные анимации не вызывают layout recalculation и работают в 60 fps даже на мобильных устройствах. Неграмотные — убивают производительность, вызывают jank и раздражают пользователя.
Анимировать нужно только свойства, не вызывающие reflow/repaint:
| Свойство | Composite | Безопасно |
|---|---|---|
| transform | Да | Да |
| opacity | Да | Да |
| filter | Частично | Осторожно |
| width, height | Нет | Нет, используйте scale() |
| top, left | Нет | Нет, используйте translate() |
| background-color | Нет | Только через transition |
| clip-path | Частично | Да, современные браузеры |
Правило: перемещение через translate, масштаб через scale, поворот через rotate — никогда top/left/width/height в @keyframes. Анимация через transform и opacity в 5 раз производительнее анимаций layout-свойств, так как не включают reflow и repaint.
Что выбрать: CSS transitions или animations?
CSS transitions — для состояний (hover, focus, active). Декларативно, коротко:
.button { background-color: #2563eb; transform: translateY(0); transition: background-color 200ms ease, transform 150ms ease, box-shadow 200ms ease; } .button:hover { background-color: #1d4ed8; transform: translateY(-2px); box-shadow: 0 8px 25px rgb(37 99 235 / 0.4); } CSS animations (@keyframes) — для автономных, зацикленных или многошаговых движений. Подробнее — на MDN.
@keyframes pulse-ring { 0% { transform: scale(0.8); opacity: 0.8; } 70% { transform: scale(1.4); opacity: 0; } 100% { transform: scale(1.4); opacity: 0; } } .live-indicator::before { content: ''; position: absolute; inset: -4px; border-radius: 50%; background: currentColor; animation: pulse-ring 1.8s cubic-bezier(0.215, 0.61, 0.355, 1) infinite; } Как избежать jank на мобильных устройствах?
Основные причины jank: анимация layout-свойств, слишком много одновременно анимируемых элементов, отсутствие will-change на критичных элементах. Мы используем следующий подход:
- Все движения через transform и opacity.
- Не более 30 анимируемых элементов на странице.
- will-change только на тех элементах, которые анимируются постоянно (например, spinner), и убираем после завершения.
- Обязательное добавление prefers-reduced-motion для accessibility.
Пример will-change:
.modal-overlay { will-change: opacity; } .modal-overlay.is-visible { will-change: auto; } Accessibility: prefers-reduced-motion
Обязательно для любого проекта:
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } Intersection Observer + CSS классы
Анимации при появлении в viewport — без библиотек:
const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.classList.add('is-visible'); observer.unobserve(entry.target); } }); }, { threshold: 0.15 } ); document.querySelectorAll('[data-animate]').forEach((el) => observer.observe(el)); [data-animate] { opacity: 0; transform: translateY(24px); transition: opacity 500ms ease, transform 500ms ease; } [data-animate].is-visible { opacity: 1; transform: translateY(0); } Почему стоит избегать JavaScript-библиотек для анимаций?
CSS-анимации выполняются на композитном потоке, не блокируя JavaScript. Они обеспечивают стабильные 60 FPS даже при высоких нагрузках. JavaScript-анимации (например, через requestAnimationFrame) работают в основном потоке и могут вызывать jank при сложных вычислениях. В тестах мы замеряли: CSS-анимации в 5 раз быстрее JS-анимаций на compositor thread. Библиотеки вроде GSAP удобны, но для большинства задач (entrance, hover, loading) CSS-решения легче, быстрее и не требуют дополнительных загрузок.
Что входит в работу
| Этап | Длительность | Результат |
|---|---|---|
| Аудит текущих анимаций | 0.5–1 день | Анализ производительности, выявление проблем |
| Проектирование анимационной системы | 0.5–1 день | Набор @keyframes, стили, токены анимаций |
| Вёрстка и оптимизация | от 1 дня | Готовые компоненты с анимациями, will-change, prefers-reduced-motion |
| Тестирование на мобильных устройствах | 0.5 дня | Проверка FPS, LCP, INP в Chrome DevTools |
| Деплой и мониторинг | 0.5 дня | Интеграция в CI/CD, метрики Core Web Vitals |
Производительность: чек-лист
- transform и opacity — единственные свойства в @keyframes для движения.
- will-change только на элементах с тяжёлой анимацией, убирать после завершения.
- animation-fill-mode: both вместо дублирования начального состояния.
- Не более 20–30 одновременно анимируемых элементов на странице.
- prefers-reduced-motion охватывает все анимации.
- Тестирование в Chrome DevTools → Performance → Rendering → Paint flashing.
Сроки
Базовые CSS-переходы (hover-состояния, появления модалок, fade-эффекты) включаются в стоимость базовой вёрстки. Кастомные анимации (skeleton, staggered-списки, hero-анимации, parallax) — от 0.5 до 1 дня в зависимости от количества и сложности сцен. Свяжитесь с нами для аудита анимаций — выявим проблемы производительности. Закажите оптимизацию — получите гарантию отсутствия jank.







