Keyframes

@keyframes — правило CSS, которое определяет последовательность кадров анимации. Свойство animation привязывает эту анимацию к элементу, задавая длительность, плавность, повторения и другие параметры.

Зачем нужно

Transition анимирует только между двумя состояниями (A → B). @keyframes позволяет создавать сложные многошаговые анимации (A → B → C → D...), которые могут запускаться автоматически, повторяться бесконечно и работать независимо от пользовательских действий.

Где используется

  • Загрузочные спиннеры и индикаторы
  • Анимации появления элементов (fade-in, slide-in)
  • Пульсирующие уведомления
  • Бегущие строки и маркизы
  • Сложные декоративные эффекты

Предпосылки

Определение @keyframes

/* Два ключевых кадра */
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* Несколько ключевых кадров через проценты */
@keyframes bounce {
  0% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-30px);
  }
  100% {
    transform: translateY(0);
  }
}

/* Множество шагов */
@keyframes colorCycle {
  0%   { background-color: #ff6b6b; }
  25%  { background-color: #ffd93d; }
  50%  { background-color: #6bcb77; }
  75%  { background-color: #4d96ff; }
  100% { background-color: #ff6b6b; }
}

Свойства animation

animation-name

.element {
  animation-name: fadeIn;        /* Имя @keyframes */
  animation-name: none;          /* Отключить */
}

animation-duration

.element {
  animation-duration: 1s;        /* 1 секунда */
  animation-duration: 300ms;     /* 300 миллисекунд */
}

animation-timing-function

.element {
  animation-timing-function: ease;          /* По умолчанию */
  animation-timing-function: linear;        /* Равномерно */
  animation-timing-function: ease-in;       /* Медленный старт */
  animation-timing-function: ease-out;      /* Медленный конец */
  animation-timing-function: ease-in-out;   /* Плавно */
  animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); /* Кастом */
  animation-timing-function: steps(6);      /* Покадрово */
}

animation-delay

.element {
  animation-delay: 0s;           /* Без задержки */
  animation-delay: 500ms;        /* Полсекунды */
  animation-delay: -200ms;       /* Начать «из середины» */
}

animation-iteration-count

.element {
  animation-iteration-count: 1;          /* Один раз (по умолчанию) */
  animation-iteration-count: 3;          /* Три раза */
  animation-iteration-count: infinite;   /* Бесконечно */
}

animation-direction

.element {
  animation-direction: normal;            /* 0% → 100% */
  animation-direction: reverse;           /* 100% → 0% */
  animation-direction: alternate;         /* 0% → 100% → 0% → ... */
  animation-direction: alternate-reverse; /* 100% → 0% → 100% → ... */
}

animation-fill-mode

.element {
  /* Что происходит ДО и ПОСЛЕ анимации */
  animation-fill-mode: none;       /* Вернуть к исходному (по умолчанию) */
  animation-fill-mode: forwards;   /* Остаться в конечном состоянии */
  animation-fill-mode: backwards;  /* Применить начальный кадр до старта (при delay) */
  animation-fill-mode: both;       /* forwards + backwards */
}

forwards — самый частый. Без него элемент «прыгнет» обратно в исходное состояние после анимации.

animation-play-state

.element {
  animation-play-state: running;  /* Играет */
  animation-play-state: paused;   /* На паузе */
}

/* Пауза при наведении */
.element:hover {
  animation-play-state: paused;
}

Сокращённая запись (shorthand)

.element {
  /* name duration timing delay count direction fill-mode play-state */
  animation: fadeIn 1s ease 0s 1 normal forwards running;

  /* Минимум: имя и длительность */
  animation: fadeIn 1s;

  /* Типичный набор */
  animation: slideIn 500ms ease-out forwards;

  /* Бесконечная */
  animation: spin 2s linear infinite;

  /* Несколько анимаций */
  animation:
    fadeIn 500ms ease forwards,
    slideUp 600ms ease-out 200ms forwards;
}

Практические примеры

Fade-in при загрузке

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.section {
  animation: fadeIn 600ms ease-out forwards;
}

/* Каскадная задержка для элементов */
.section:nth-child(1) { animation-delay: 0ms; }
.section:nth-child(2) { animation-delay: 100ms; }
.section:nth-child(3) { animation-delay: 200ms; }

Спиннер

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #e9ecef;
  border-top-color: #007bff;
  border-radius: 50%;
  animation: spin 800ms linear infinite;
}

Пульсация

@keyframes pulse {
  0%, 100% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.05);
    opacity: 0.8;
  }
}

.notification-dot {
  width: 12px;
  height: 12px;
  background: #dc3545;
  border-radius: 50%;
  animation: pulse 2s ease-in-out infinite;
}

Bounce-вход

@keyframes bounceIn {
  0% {
    transform: scale(0.3);
    opacity: 0;
  }
  50% {
    transform: scale(1.05);
  }
  70% {
    transform: scale(0.9);
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

.modal {
  animation: bounceIn 500ms cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards;
}

Покадровая анимация (спрайты)

@keyframes walk {
  from { background-position: 0 0; }
  to { background-position: -640px 0; }
}

.character {
  width: 80px;
  height: 100px;
  background: url("sprite.png");
  animation: walk 800ms steps(8) infinite;
}

Каскадная задержка через CSS-переменные

@keyframes fadeUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.list-item {
  opacity: 0;
  animation: fadeUp 400ms ease forwards;
  animation-delay: calc(var(--i, 0) * 100ms);
}
<ul>
  <li class="list-item" style="--i: 0">Первый</li>
  <li class="list-item" style="--i: 1">Второй</li>
  <li class="list-item" style="--i: 2">Третий</li>
</ul>

Частые ошибки

  1. Забыли animation-fill-mode: forwards — элемент «прыгает» назад после анимации
  2. Анимация не запускается — проверьте имя: animation-name должно точно совпадать с @keyframes
  3. Дублирование from/to и процентов:
    /* ОШИБКА */
    @keyframes bad {
      from { opacity: 0; }
      0% { opacity: 0.5; } /* Конфликт! */
    }
    
  4. Анимация height: auto — CSS не может анимировать auto. Используйте max-height или transform: scaleY
  5. Слишком много анимаций одновременно — перегружает GPU и тормозит UI

Практика

  • Создать спиннер через @keyframes + rotate
  • Сделать fade-in анимацию с каскадной задержкой
  • Создать пульсирующий элемент с infinite + alternate
  • Сделать покадровую анимацию через steps
  • Создать bounce-эффект с несколькими ключевыми кадрами

Связанные темы

Ресурсы