CSS-анимация

Вступление

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

Анимация играет ключевую роль в создании современного веб-сайта. Она не только украшает страницу, но и значительно улучшает взаимодействие с пользователем.

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

Наведите курсор на кнопки чтобы увидеть разницу. Какой эффект наведения более приятный?

Без анимации резкое изменение цвета может негативно сказаться на пользователе, особенно в условиях слабого освещения. Это может вызвать дискомфорт и даже повлиять на зрение.

Анимация не должна быть ни слишком короткой, ни чрезмерно длинной. Слишком короткая анимация останется незамеченной, а слишком длинная — заставит пользователя ждать.

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

Когда навигация появляется с анимацией, интерфейс выглядит более дружелюбно.

Контролируйте скорость анимации: если она будет слишком быстрой, пользователь не успеет уловить контекст, а если слишком медленной — ему придётся долго ждать. Затянувшаяся анимация может скрыть контент от пользователя.

На некоторых сайтах первый блок (блок-герой) занимает весь экран. Чтобы показать пользователю, что ниже есть дополнительный контент, дизайнеры добавляют стрелку. Для привлечения внимания можно добавить легкую анимацию.

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

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

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

видео

CSS-анимация: правила хорошего тона и практические приёмы

transition — основа плавности

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

Свойство transition CSS — это сокращённое свойство для transition-property, transition-duration, transition-timing-function, transition-delay и transition-behavior.

CSS

.elem {
  transition:  transition-property transition-duration transition-timing-function transition-delay;
}

Вместо названий свойств укажите их значения. Первые два — обязательны.

При настройке плавности анимации важно учитывать особенности каждой из них. Рассмотрим их отдельно.

transition-property определяет анимируемое свойство. Необходимо указывать только те свойства, которые должны плавно менять своё состояние. Использование значения all может негативно сказаться на производительности сайта и добавить нежелательные анимации. Например, при изменении сетки плавное изменение ширины может вызвать неожиданные рывки контента.

HTML

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .container {
      display: flex;
      flex-wrap: wrap;
      transition: all 0.3s;
    }

    .box {
      background-color: #ff6347;
      padding: 20px;
      transition: all 0.3s;
    }

    .box:hover {
      background-color: brown;
    }

    @media (orientation: portrait) {
      .container {
        gap: 50px;
      }

      .box {
        width: 40%;
      }
    }

    @media (orientation: landscape) {
      .container {
        gap: 140px;
      }

      .box {
        width: 30%;
      }
    }
  </style>
</head>

<body>

  <div class="container">
    <div class="box"></div>
    <div class="box"></div>
  </div>

</body>

</html>
Измените ориентацию экрана в эмуляторе браузера или на мобильном телефоне, переключаясь между портретным и альбомным режимами.

transition-duration задаёт время анимации. Слишком короткое время, менее 150 мс, делает анимацию незаметной, а слишком длинное время ожидания может раздражать пользователя и негативно влиять на производительность.

У первой кнопки слишком быстрая анимация, у второй очень медленная.

Оптимальное время анимации составляет от 200 до 500 мс. Для небольших изменений, таких как наведение курсора на кнопку, достаточно 150–200 мс. Более заметные эффекты, например, появление модального окна, требуют времени 250–350 мс. Для анимации выезжающих элементов можно увеличить это время.

transition-timing-function определяет функцию скорости анимации. По умолчанию используется значение ease, что означает быстрое ускорение с последующим замедлением. Для более естественного движения лучше выбрать ease-in-out, при котором анимация начинается и заканчивается медленнее, а в середине происходит ускорение.

схемы работы функций анимаций
На схеме видно что ease-in-out в середине происходит быстрее, а ease только в конце ускоряется.

Если вы планируете анимировать появление элемента, применяйте ease-out. Для скрытия элемента, когда пользователь не сможет его открыть, используйте ease-in. Например, при отображении уведомления о файлах cookie. Если же элемент может быть вызван повторно, применяйте ease-in-out. Это подходит для модальных окон и всплывающих подсказок.

transition-delay устанавливает задержку начала анимации. Обычно этот параметр не используется, так как анимации часто начинаются сразу после изменения свойства. Однако в некоторых случаях задержка может быть полезна для создания более сложных и интересных эффектов. Например, можно использовать transition-delay для последовательного запуска нескольких анимаций, что позволит контролировать порядок их выполнения.

Сначала увеличивается размер псевдоэлемента, а потом добавляется прозрачность.

@keyframes — для сложных сценариев

А как быть, если необходимо сделать сложную анимацию? Например, такую?

По клику на кнопку инпут теряется.

Как видите, элемент делает несколько движений. В таких случаях применяют правило @keyframes

Правило@keyframes CSS at-rule  управляет промежуточными шагами в последовательности CSS-анимации, определяя стили для ключевых кадров (или точек) вдоль последовательности анимации. Это даёт больше контроля над промежуточными этапами последовательности анимации, чем transition.

CSS

@keyframes shake {
  0% {
    transform: translateX(0);
  }

  20% {
    transform: translateX(-10px);
  }

  40% {
    transform: translateX(10px);
  }

  60% {
    transform: translateX(-8px);
  }

  80% {
    transform: translateX(8px);
  }

  100% {
    transform: translateX(0);
  }
}

.shake-animation {
  animation: shake 0.5s ease-in-out;
  border: 2px solid red;
  background-color: #ffe6e6;
}

Использовать правило @keyframes достаточно просто. Сразу после правила указываем название анимации: @keyframes shake { ... } В фигурных скобках указываем этапы:

CSS

@keyframes shake {
  from { ... }
  to { ... }
}

Если у анимации всего два значения, можно использовать ключевые слова from (начальное значение) и to (конечное значение). Если есть промежуточные значения, используйте проценты, как в примере выше.

Далее можно применить анимацию к элементу, используя свойства animation:

CSS свойство animation это короткая запись для animation-name, animation-duration, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, animation-fill-mode и animation-play-state.

CSS

/* Короткая запись */
.shake-animation {
  animation: shake 0.5s ease-in-out;
}

/* Полная запись */
.shake-animation {
  animation-name: shake;
  animation-duration: 0.5s;
  animation-timing-function: ease-in-out;
}

По сути, здесь действуют те же принципы, что и в transition, но добавляются дополнительные настройки. Например, animation-iteration-count со значением infinite зацикливает анимацию.

HTML

<div class="bottom"></div>

CSS

.bottom::after {
  animation: animatedgradient 24s ease alternate infinite;
}

.bottom::after {
  content: "";
  position: absolute;
  top: calc(50% - 15px);
  left: 50%;
  width: 15px;
  height: 30px;
  background-size: 900% 300%;
  transform: translateX(-50%);
  opacity: .6;
  transition: opacity 1s var(--transition-function);
  -webkit-mask-image: url(/img/icons/arrow-down.svg);
  mask-image: url(/img/icons/arrow-down.svg);
  -webkit-mask-size: 30px 30px;
  mask-size: 30px 30px;
  -webkit-mask-position: center;
  mask-position: center;
  background-image: #0039a6;
  background-image: linear-gradient(60deg, #fff, #0039a6, #d52b1e, #0039a6, #f8f8f8);
  animation: animatedgradient 24s ease alternate infinite;
}

@keyframes animatedgradient {
  0% {
    background-position: 0% 0%;
  }

  25% {
    background-position: 100% 0%;
  }

  50% {
    background-position: 100% 100%;
  }

  0% {
    background-position: 0% 100%;
  }

  100% {
    background-position: 0% 0%;
  }
}
Анимация воспроизводится без остановки.

Свойство animation-direction позволяет развернуть анимацию в обратную сторону.

animation-fill-mode позволяет после завершения анимации установить начальное или конечное состояние элемента относительно анимации.

animation-play-state позволяет поставить анимацию на паузу.

Стоит упомянуть, что можно комбинировать несколько анимаций для одного элемента: animation: move 1s, fade 0.5s 0.5s both;.

Доступность и консистентность

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

Кроме того, важно учитывать, что пользователи могут отключить анимацию на своих устройствах. Это означает, что на сайте вся декоративная анимация должна быть либо отключена, либо повторяться только один раз. Для этого можно использовать CSS-медиазапрос prefers-reduced-motion.

CSS

@media screen and (prefers-reduced-motion: reduce) {
  :root {
    --transition-duration: 0;
    --transition-function: ease-in-out;
    --animation-duration: 0.01ms;
    --animation-iteration-count: 1;
    --transition-duration: 0.01ms;
  }
}

Пример с кастомными CSS свойствами был приведён не случайно. Рекомендуется использовать одинаковые длительности и типы анимаций на всём сайте. Это помогает создать целостный и предсказуемый интерфейс, а кастомные свойства значительно упрощают этот процесс.

Для пользователей с высокими требованиями к контрастности или использующими принудительные цветовые схемы (например, режим высокого контраста в Windows или тёмные темы с особыми настройками) анимация может не только отвлекать, но и усложнять восприятие интерфейса. Медиазапрос prefers-contrast позволяет определить, предпочитает ли пользователь повышенную контрастность, и при необходимости отключить или упростить анимацию. Атрибут forced-colors (или медиазапрос forced-colors) выявляет, активен ли принудительный цветовой режим системы; в таких условиях анимация, основанная на цветовых переходах (background-color, border-color и т.п.), может быть невидимой или вредной. Рекомендуется оборачивать декоративные анимации в соответствующие медиаусловия или вовсе отключать их, когда включены специальные режимы отображения, чтобы не создавать помех для людей с нарушениями зрения или светочувствительностью.

Новые и редко используемые технологии

Следующие технологии могут не являться базовыми, поскольку они не работают в некоторых наиболее популярных браузерах.

allow-discrete и @starting-style

CSS активно развивается, и уже сейчас есть возможность анимировать неанимируемые свойства. Но пока что (на момент написания статьи) это возможно не во всех браузерах. Вот пример:

HTML

<button popovertarget="my-manual-popover" popovertargetaction="show">
  Открыть
</button>

<div id="my-manual-popover" popover="manual">
  <p>Я — ручной поповер. Меня можно закрыть только явно.</p>
  <button popovertarget="my-manual-popover" popovertargetaction="hide">
    Закрыть
  </button>
</div>

CSS

#my-popover {
  position: fixed;
  bottom: 20px;
  top: auto;
  margin: 0;
  left: 20px;
  opacity: 0;
  transition: opacity 0.2s, display 0.2s allow-discrete, overlay 0.2s allow-discrete;
}

#my-popover:popover-open {
  opacity: 1;
}

@starting-style {
  #my-popover:popover-open {
    opacity: 0;
  }
}
Поповер появляется с анимацией.

Почему это работает?

  • Свойство overlay и display с allow-discrete разрешают анимировать переходы для «дискретных» свойств.
  • @starting-style задаёт начальные значения для анимации при появлении элемента (когда он меняется с display: none на видимое состояние).

scroll-driven

На некоторых сайтах добавляют прогресс бар привязанный к скролу страницы. Раньше необходимо было писать JavaScript для отслеживания высоты и расчета прогресса. Теперь, с помощью CSS это можно реализовать одной строкой: animation-timeline: scroll();

При прокрутке страницы сверху фрейма заполняется полоса градиентом.

animation-composition

Свойство animation-composition позволяет складывать несколько анимаций одного свойства.

Две анимации: move1 (0→100px) и move2 (0→50px).

  • replace — только 50px (побеждает последняя анимация)
  • add — сумма: 150px
  • accumulate — накопление (здесь тоже ~150px, но для сложных трансформаций отличается)

HTML

<div class="demo">
  <div class="box replace">
    <span>replace</span>
  </div>
  <div class="box add">
    <span>add</span>
  </div>
  <div class="box accumulate">
    <span>accumulate</span>
  </div>
</div>

CSS

.demo {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  gap: 24px;
  justify-content: center;
  align-items: center;
}
.box {
  width: 90px;
  height: 90px;
  background: #0f172a;
  border-radius: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-weight: bold;
  font-size: 0.8rem;
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
  /* обе анимации бесконечно чередуются */
  animation: move1 1.5s infinite alternate, move2 1.5s infinite alternate;
}
.replace {
  animation-composition: replace;
}
.add {
  animation-composition: add;
}
.accumulate {
  animation-composition: accumulate;
}
@keyframes move1 {
  from { transform: translateX(0); }
  to   { transform: translateX(100px); }
}
@keyframes move2 {
  from { transform: translateX(0); }
  to   { transform: translateX(50px); }
}

Подводные камни

Иногда у начинающих возникает вопрос: почему не работает анимация? Давайте разберёмся, в каких ситуациях это может происходить?

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

Ещё одной причиной может быть то, что не все свойства поддаются анимации. Например, свойство opacity можно анимировать, а свойство display — нет. Если вам нужно анимировать появление элемента, можно использовать альтернативные методы. В предыдущей статье о навигации я применял opacity и visibility. Есть и другие подходы, например, изменение прозрачности элемента при его смещении, использование transform: scale; или изменение высоты.

Свойство allow-discrete в контексте transition позволяет применять анимацию к дискретным свойствам — то есть таким, которые обычно не анимируются плавно (например, display, content, visibility). В примере оно включено для свойств display и overlay: благодаря этому браузер может плавно менять состояние элемента, даже когда речь идёт о «резких» переключениях между значениями.

Правило @starting-style задаёт начальное состояние элемента перед запуском анимации — то, как он должен выглядеть в самый первый кадр. В коде оно устанавливает opacity: 0 для открытого поповера (#my-popover:popover-open) до начала перехода: это гарантирует, что анимация появления (от прозрачности к видимости) сработает корректно, без внезапного «проявления» элемента без эффекта.

Проверяйте поддержку браузерами allow-discrete и правила @starting-style.

Кроме того, для корректной работы анимации необходимо указать не только конечное, но и начальное значение анимируемого свойства. При этом значение должно быть измеримым. Например, анимация свойства width от «fit-content» до 100 пикселей не сработает, так как браузер не может вычислить промежуточные значения. Если указать значения, например, от 100 пикселей до 100%, анимация будет работать.

Производительность

Технический прогресс движется вперёд семимильными шагами, однако на полках магазинов всё ещё можно найти устройства с недостаточно мощными характеристиками. Если свойства анимации подобраны неправильно, она будет работать не плавно, а мобильные устройства будут сильнее нагреваться и быстрее расходовать заряд батареи.

Какие же свойства лучше всего использовать для анимации? Это те, которые не заставляют браузер пересчитывать расположение элементов: transform, opacity. Такие свойства как color и background-color тоже вызывает перерисовку, хоть и менее затратную, чем пересчёт layout.

Также следует с осторожностью применять зацикленные анимации. Большое количество анимаций также негативно сказывается на производительности.

С помощью will-change можно увеличить производительность анимации.

Свойство will-change позволяет заранее сообщить браузеру, какие свойства элемента будут изменяться, чтобы он мог подготовить ресурсы для плавной анимации. Например, при анимации transform стоит указать:

CSS

.element {
  will-change: transform;
  transition: transform 0.3s ease;
}

Это особенно полезно для сложных анимаций, так как браузер создаст отдельный слой и предотвратит подёргивания. Однако используйте will-change только для элементов, которые действительно будут анимироваться, чтобы избежать излишней нагрузки на память.

will-change — это мощный инструмент, но его следует использовать с осторожностью. Чрезмерное или неправильное применение может привести к обратному эффекту — снижению производительности из-за перерасхода памяти.

Итоги

CSS-анимация — это мощный инструмент для веб-разработчиков. Правильно применённая анимация улучшает пользовательский опыт и привлекает внимание к важным элементам.

Сначала следует использовать функциональную анимацию, а затем декоративную. Важно помнить, что агрессивная анимация может отпугнуть пользователя и негативно сказаться на производительности.

Для простых анимаций лучше всего подходит свойство transition, а для более сложных и многоэтапных анимаций — @keyframes. Рекомендуется указывать только те свойства, которые нужно плавно изменять, чтобы избежать неожиданного поведения при изменении сетки и улучшить производительность страницы.

Рекомендую попрактиковаться в создании анимаций, чтобы лучше понять их работу. Это поможет увидеть все преимущества их использования. Можно не только создавать собственные анимации, но и использовать готовые решения, например, из библиотеки Animate.css. Регулярное использование таких библиотек поможет быстро освоить создание любых анимаций самостоятельно.

Помните: CSS-анимации покрывают 90% потребностей в анимации на сайте. Сложные сценарии могут потребовать SVG-анимации или JavaScript, но начинать всегда стоит с CSS-решений.

Объявление

Нравится фронтенд — попробуйте самые подробные интерактивные тренажёры от HTML Academy бесплатно. Подойдут и действующим разработчикам, чтобы подтянуть навыки вёрстки.