$ sudo teach IT
Урок 5.2 — Анимации

Урок 5.2: Анимации

Модуль 5 — Продвинутый CSS

transition — плавное изменение

Веб без анимаций — как машина без амортизаторов. Всё жёстко, дёргано, неприятно. transition — это самый простой способ сделать интерфейс плавным.

Когда ты меняешь свойство у элемента (например, цвет при наведении), браузер переключает его мгновенно. Transition говорит: «Эй, сделай это плавно, за 0.3 секунды».

.button {
  background: #e94560;
  transition: background 0.3s ease;
}

.button:hover {
  background: #10b981;
}

Вот как это выглядит. Наведи мышку на квадратики — они меняются плавно:

наведи
меняю
цвет
и форму
и размер

Синтаксис transition:

/* Полный синтаксис */
transition: property duration timing-function delay;

/* Примеры */
transition: all 0.3s ease;              /* всё, 0.3s, плавно */
transition: transform 0.2s ease-out;    /* только transform, 0.2s */
transition: opacity 0.5s ease 0.1s;     /* opacity, 0.5s, с задержкой 0.1s */
transition: background 0.3s, transform 0.2s; /* несколько свойств */

/* Отдельные свойства */
transition-property: transform;
transition-duration: 0.3s;
transition-timing-function: ease;
transition-delay: 0s;

Параметры подробно:

  • property — какое свойство анимировать. all — все, или конкретное: opacity, transform, background, color и т.д.
  • duration — длительность. 0.3s (секунды) или 300ms (миллисекунды). 0.2-0.3s — стандарт для интерфейсов.
  • timing-function — как именно меняется скорость. ease (плавно), linear (равномерно), ease-in (с ускорением), ease-out (с замедлением), ease-in-out (ускорение и замедление).
  • delay — задержка перед началом. Необязательный параметр.
💡 Совет: Используй transition: transform 0.2s ease и transition: opacity 0.2s ease — это самые производительные свойства для анимации. Браузер может их анимировать на видеокарте, без тормозов. А вот height и width лучше не анимировать — они дёрганые и тормозят.

transform — трансформация

transform — это свойство, которое меняет форму, размер и положение элемента. Оно не влияет на соседей (как position: relative) и отлично анимируется.

Основные функции transform:

/* Масштабирование */
transform: scale(1.5);         /* увеличить в 1.5 раза */
transform: scaleX(2);          /* растянуть по горизонтали */
transform: scaleY(0.5);        /* сжать по вертикали */

/* Поворот */
transform: rotate(45deg);      /* на 45 градусов по часовой */
transform: rotate(-90deg);     /* против часовой */

/* Сдвиг */
transform: translateX(30px);   /* вправо на 30px */
transform: translateY(-20px);  /* вверх на 20px */
transform: translate(30px, -20px); /* по X и Y */

/* Наклон */
transform: skew(10deg);        /* наклон на 10 градусов */
transform: skew(10deg, 5deg);  /* по X и Y */

/* Комбинация */
transform: scale(1.2) rotate(15deg) translateX(10px);

Наведи на прямоугольники — каждый анимирует свою трансформацию:

scale(1.5)
rotate(45deg)
translate + scale
skew

Обрати внимание: все трансформации плавные, потому что у них есть transition: transform 0.3s. Сами по себе transform — мгновенные. Плавность даёт transition.

Очень важно: transform не вынимает элемент из потока и не влияет на соседей. Элемент трансформируется визуально, но его место в раскладке остаётся прежним. Это значит, что можно безопасно увеличивать кнопку при наведении — текст под ней не дёрнется.

💡 Совет: Комбинируй transform с transition для создания ховер-эффектов. transform: scale(1.05) на кнопке при наведении — это самый простой и эффективный способ сделать интерфейс «живым».

:hover — при наведении мыши

:hover — это псевдокласс (мы уже встречались с ними в CSS). Он срабатывает, когда пользователь наводит мышку на элемент. В комбинации с transition и transform :hover творит чудеса.

/* Базовая кнопка */
.btn {
  background: #e94560;
  color: #fff;
  padding: 14px 36px;
  border: none;
  border-radius: 10px;
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

/* Поведение при наведении */
.btn:hover {
  transform: scale(1.05);
  box-shadow: 0 6px 20px rgba(233, 69, 96, 0.4);
}

/* Можно сделать эффект нажатия */
.btn:active {
  transform: scale(0.97);
}

Вот кнопка с эффектом при наведении и нажатии. Попробуй навести и кликнуть:

Наведи на меня

Кнопка увеличивается при наведении и слегка сжимается при клике (:active)

Примеры эффектов для :hover:

/* Подъём карточки */
.card {
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
  transform: translateY(-6px);
  box-shadow: 0 12px 28px rgba(0,0,0,0.15);
}

/* Изменение цвета фона */
.nav-link {
  transition: background 0.2s ease, color 0.2s ease;
}
.nav-link:hover {
  background: #f0f0f0;
  color: #e94560;
}

/* Подчёркивание ссылки */
.link {
  text-decoration: none;
  position: relative;
}
.link::after {
  content: '';
  position: absolute;
  bottom: -2px;
  left: 0;
  width: 0;
  height: 2px;
  background: #e94560;
  transition: width 0.3s ease;
}
.link:hover::after {
  width: 100%;
}

Последний пример — красивое подчёркивание ссылки. Оно «выезжает» слева направо при наведении. Выглядит намного интереснее, чем обычное text-decoration: underline.

🌟 Карточка

Наведи — приподнимется

🔥 Ещё карточка

С тенью и подъёмом

🌿 Третья

Hover — магия

Карточки выше имеют transition: transform 0.3s, box-shadow 0.3s. При наведении они поднимаются на 8px вверх (translateY(-8px)) и получают большую тень. Это стандартный паттерн для карточек в современных интерфейсах.

@keyframes — сложная анимация

transition хорош для простых эффектов (из точки А в точку Б). Но что, если нужно сделать сложную анимацию с несколькими этапами? Например, чтобы элемент сначала уменьшился, потом увеличился, потом изменил цвет и уехал в сторону. Для этого есть @keyframes.

@keyframes myAnimation {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.2);
    opacity: 0.6;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

@keyframes определяет последовательность кадров (frames). 0% — начало анимации, 100% — конец. Между ними можно добавлять промежуточные точки: 25%, 50%, 75%.

🪀 bounce
💗 pulse
🔄 spin

Что здесь происходит:

  • bounce: элемент прыгает вверх-вниз. Начало — внизу (translateY: 0), конец — вверху (translateY: -20px). alternate означает, что анимация идёт туда-обратно.
  • pulse: элемент пульсирует — увеличивается и уменьшается. 50% кадр — максимальное увеличение.
  • spin: бесконечное вращение. От 0 до 360 градусов.

Вот как выглядят их keyframes в коде:

/* Прыжок */
@keyframes bounce {
  0%   { transform: translateY(0); }
  100% { transform: translateY(-20px); }
}

/* Пульсация */
@keyframes pulse {
  0%, 100% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.1);
    opacity: 0.7;
  }
}

/* Вращение */
@keyframes spin {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

/* Появление слева */
@keyframes slideIn {
  0%   { transform: translateX(-100px); opacity: 0; }
  100% { transform: translateX(0); opacity: 1; }
}

Ещё один полезный приём — анимация появления элемента на странице. Когда пользователь только открывает страницу, элементы могут плавно выезжать или проявляться. Это называется entry animation:

👋 Привет! Я только что приехал

Анимация slideIn срабатывает один раз при загрузке

В отличие от :hover, который срабатывает по действию пользователя, @keyframes может срабатывать автоматически — при загрузке страницы, с задержкой или по триггеру через JavaScript.

animation — применить анимацию

Ты создал @keyframes — отлично. Теперь нужно применить их к элементу. Для этого есть свойство animation.

.element {
  animation: spin 1.5s linear infinite;
}

/* Полный синтаксис */
animation: name duration timing-function delay iteration-count direction fill-mode play-state;

/* Примеры */
animation: bounce 0.6s ease infinite alternate;
animation: pulse 1s ease-in-out infinite;
animation: slideIn 0.5s ease-out 0.2s both; /* с задержкой 0.2s */
animation: spin 2s linear infinite, pulse 1s ease infinite; /* две анимации */

Параметры animation подробно:

ПараметрЧто делает
nameИмя @keyframes
duration0.5s или 500ms
timing-functionease, linear, ease-in-out и др.
delayЗадержка перед стартом
iteration-countСколько раз: 1, 3, infinite
directionnormal, reverse, alternate, alternate-reverse
fill-modenone, forwards, backwards, both
play-staterunning, paused

direction: normal — от 0% до 100%, reverse — от 100% до 0%, alternate — туда-обратно, alternate-reverse — обратно-туда.

fill-mode: что делать до и после анимации. forwards — остаться на последнем кадре, backwards — начать с первого кадра ещё до старта, both — и то, и другое. both удобен для entry-анимаций: элемент не показывается до анимации, потом плавно появляется и остаётся на месте.

Самые частые комбинации:

/* Бесконечное вращение (лоадер) */
.loader {
  animation: spin 1s linear infinite;
}

/* Пульсирующая кнопка */
.btn-pulse {
  animation: pulse 1.5s ease infinite;
}

/* Появление элемента при загрузке */
.fade-in {
  animation: slideIn 0.5s ease-out both;
}

/* Анимация с задержкой для списка */
.item:nth-child(1) { animation: slideIn 0.3s ease-out 0s both; }
.item:nth-child(2) { animation: slideIn 0.3s ease-out 0.1s both; }
.item:nth-child(3) { animation: slideIn 0.3s ease-out 0.2s both; }

Последний приём — задержка для каждого элемента списка. Это создаёт эффект «волны»: элементы появляются один за другим. Выглядит очень эффектно.

💡 Совет: Не перебарщивай с анимациями. Бесконечно вращающиеся элементы раздражают. Пульсация хороша для кнопки «купить» или индикатора загрузки, но не для всего подряд. Анимации должны быть незаметными — пользователь чувствует их, но не отвлекается.

Практика: кнопка, которая увеличивается при наведении, карточка с тенью

Давай сделаем страницу с несколькими анимациями. Кнопка растёт при наведении, карточка приподнимается и получает тень, а внизу — лоадер с бесконечным вращением.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Анимации в действии</title>
<style>
  *, *::before, *::after {
    box-sizing: border-box;
  }
  body {
    font-family: 'Segoe UI', system-ui, sans-serif;
    background: #f5f5f5;
    margin: 0;
    padding: 40px 20px;
    color: #333;
  }
  .container {
    max-width: 700px;
    margin: 0 auto;
  }
  h1 {
    text-align: center;
    margin-bottom: 40px;
    font-size: 2em;
  }
  h1 span {
    display: inline-block;
    animation: wave 0.6s ease infinite alternate;
    transform-origin: center bottom;
  }
  @keyframes wave {
    0% { transform: rotate(-5deg); }
    100% { transform: rotate(5deg); }
  }

  /* Кнопка */
  .btn {
    display: inline-block;
    padding: 16px 40px;
    background: linear-gradient(135deg, #e94560, #c23152);
    color: #fff;
    border: none;
    border-radius: 12px;
    font-size: 1.1em;
    font-weight: 600;
    cursor: pointer;
    transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.3s ease;
    margin-bottom: 40px;
  }
  .btn:hover {
    transform: scale(1.08);
    box-shadow: 0 8px 24px rgba(233,69,96,0.4);
    background: linear-gradient(135deg, #c23152, #a01d3a);
  }
  .btn:active {
    transform: scale(0.96);
  }

  /* Карточка */
  .card {
    background: #fff;
    border-radius: 16px;
    padding: 32px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.06);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
    margin-bottom: 40px;
  }
  .card:hover {
    transform: translateY(-8px);
    box-shadow: 0 16px 40px rgba(0,0,0,0.1);
  }
  .card h2 {
    margin: 0 0 8px;
    font-size: 1.4em;
  }
  .card p {
    color: #666;
    line-height: 1.6;
    margin: 0;
  }

  /* Список с появлением */
  .items {
    list-style: none;
    padding: 0;
    margin: 0 0 40px;
  }
  .items li {
    background: #fff;
    padding: 16px 20px;
    border-radius: 10px;
    margin-bottom: 8px;
    box-shadow: 0 1px 4px rgba(0,0,0,0.04);
    animation: fadeInUp 0.3s ease both;
  }
  .items li:nth-child(1) { animation-delay: 0s; }
  .items li:nth-child(2) { animation-delay: 0.1s; }
  .items li:nth-child(3) { animation-delay: 0.2s; }
  .items li:nth-child(4) { animation-delay: 0.3s; }
  .items li:nth-child(5) { animation-delay: 0.4s; }

  @keyframes fadeInUp {
    0% {
      opacity: 0;
      transform: translateY(20px);
    }
    100% {
      opacity: 1;
      transform: translateY(0);
    }
  }

  /* Лоадер */
  .loader {
    display: flex;
    gap: 8px;
    justify-content: center;
    margin-bottom: 40px;
  }
  .loader .dot {
    width: 16px;
    height: 16px;
    background: #e94560;
    border-radius: 50%;
    animation: dotBounce 0.6s ease infinite alternate;
  }
  .loader .dot:nth-child(2) { animation-delay: 0.2s; }
  .loader .dot:nth-child(3) { animation-delay: 0.4s; }

  @keyframes dotBounce {
    0% { transform: translateY(0); }
    100% { transform: translateY(-16px); background: #10b981; }
  }

  /* Скелетон (имитация загрузки) */
  .skeleton {
    display: flex;
    gap: 16px;
    margin-bottom: 40px;
  }
  .skeleton .sk-item {
    flex: 1;
    height: 120px;
    background: linear-gradient(90deg, #e0e0e0 25%, #f5f5f5 50%, #e0e0e0 75%);
    background-size: 200% 100%;
    border-radius: 12px;
    animation: shimmer 1.5s ease infinite;
  }
  @keyframes shimmer {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
  }

  .center {
    text-align: center;
  }
</style>
</head>
<body>
  <div class="container">
    <h1>
      <span style="animation-delay:0s;">А</span>
      <span style="animation-delay:0.1s;">н</span>
      <span style="animation-delay:0.2s;">и</span>
      <span style="animation-delay:0.3s;">м</span>
      <span style="animation-delay:0.4s;">а</span>
      <span style="animation-delay:0.5s;">ц</span>
      <span style="animation-delay:0.6s;">и</span>
      <span style="animation-delay:0.7s;">и</span>
    </h1>

    <div class="center">
      <button class="btn">✨ Наведи на меня</button>
    </div>

    <div class="card">
      <h2>📄 Карточка с тенью</h2>
      <p>Наведи на меня — я приподнимусь и тень станет больше. Это эффект «подъёма» (elevation), который делает интерфейс тактильным и отзывчивым. Попробуй навести на карточку выше — она плавно приподнимается.</p>
    </div>

    <ul class="items">
      <li>📌 Первый пункт — появляется сразу</li>
      <li>📌 Второй пункт — через 0.1s</li>
      <li>📌 Третий пункт — через 0.2s</li>
      <li>📌 Четвёртый пункт — через 0.3s</li>
      <li>📌 Пятый пункт — через 0.4s</li>
    </ul>

    <div class="loader">
      <div class="dot"></div>
      <div class="dot"></div>
      <div class="dot"></div>
    </div>

    <div class="skeleton">
      <div class="sk-item"></div>
      <div class="sk-item"></div>
      <div class="sk-item"></div>
    </div>
  </div>
</body>
</html>

Давай разберём каждую анимацию:

  • Заголовок: каждая буква качается (rotate(-5deg) → rotate(5deg)) с задержкой. Создаёт волну.
  • Кнопка: transition на transform и box-shadow. При наведении масштабируется на 1.08, при клике — на 0.96. Плавно.
  • Карточка: transition на transform и box-shadow. При наведении поднимается на 8px и тень становится больше.
  • Список: @keyframes fadeInUp с разными задержками для каждого пункта. Эффект «волны».
  • Лоадер: три точки прыгают с задержками. Простой и красивый индикатор загрузки.
  • Скелетон: имитация загрузки контента. Градиент «ездит» туда-сюда, создавая мерцание.

Давай подведём итог. Вот шпаргалка по анимациям:

/* АНИМАЦИИ — ШПАРГАЛКА */

/* 1. Плавное изменение (transition) */
.element {
  transition: property 0.3s ease;
}
.element:hover {
  transform: scale(1.1);
  background: #10b981;
}

/* 2. Трансформации (transform) */
transform: scale(1.2);         /* увеличение */
transform: rotate(45deg);      /* поворот */
transform: translate(10px, 20px); /* сдвиг */
transform: skew(5deg);         /* наклон */
transform: scale(1.1) rotate(10deg); /* комбо */

/* 3. Сложная анимация (@keyframes) */
@keyframes myAnim {
  0%   { opacity: 0; transform: translateY(20px); }
  50%  { opacity: 0.5; transform: translateY(-10px); }
  100% { opacity: 1; transform: translateY(0); }
}

/* 4. Применение анимации (animation) */
.element {
  animation: myAnim 0.5s ease-out both;
  /* или покороче */
  animation: pulse 1.5s ease infinite;
}

/* 5. Частые приёмы */
/* Hover-подъём карточки */
.card { transition: transform 0.3s, box-shadow 0.3s; }
.card:hover { transform: translateY(-6px); box-shadow: 0 8px 24px rgba(0,0,0,0.1); }

/* Появление элементов по очереди */
.item:nth-child(1) { animation-delay: 0s; }
.item:nth-child(2) { animation-delay: 0.1s; }
.item:nth-child(3) { animation-delay: 0.2s; }

/* Бесконечный лоадер */
@keyframes spin { 100% { transform: rotate(360deg); } }
.loader { animation: spin 1s linear infinite; }

Главное, что нужно запомнить:

  • transition — для простых ховер-эффектов (из А в Б)
  • transform — изменение формы/размера/положения (производительно)
  • :hover + transition + transform — база современных интерфейсов
  • @keyframes — для сложных многоэтапных анимаций
  • animation — применяет keyframes к элементу
  • Анимируй transform и opacity — они самые быстрые
  • Не перебарщивай: анимации должны быть незаметными и тактильными, а не отвлекающими

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

💡 Задание: Возьми любую кнопку на своей странице и добавь ей transition: transform 0.2s ease + :hover { transform: scale(1.1); }. Потом добавь :active { transform: scale(0.95); } — почувствуй разницу. Это занимает 2 минуты, а интерфейс становится намного приятнее.
Урок 5.2 — Анимации | Модуль 5: Продвинутый CSS

Урок 5.2: Анимации

5 вопросов