23 Модуль 5 Живой современный сайт 16 мин урок 23 из 25

Переходы и анимации

transition и transform, кривые ускорения, @keyframes — и главное правило: анимировать дёшево можно только две вещи.

Что ты научишься делать

  • Сделаешь кнопку, которая приятно отзывается на наведение
  • Поймёшь, почему анимация width тормозит, а transform — нет
  • Напишешь свою анимацию появления через @keyframes

#transition: плавно вместо рывком

По умолчанию любое изменение стиля происходит мгновенно. transition говорит браузеру: «меняй это свойство постепенно».

Важная деталь: transition пишут на обычном состоянии, а не на :hover. Тогда переход работает и туда, и обратно.

CSSЧетыре значения
.btn {  background: #6544e0;  transition: background 200ms ease-out;  /*          │          │      └ кривая ускорения              │          └ длительность              └ что анимируем */}.btn:hover {  background: #5334cf;}

Так не надо

.btn:hover {
  background: #5334cf;
  transition: background .2s;
}

Переход существует только пока курсор на кнопке. Наведение плавное, а уход — резкий рывок.

Так надо

.btn {
  transition: background .2s ease-out;
}

.btn:hover {
  background: #5334cf;
}

Свойство описано в базовом состоянии, поэтому переход работает в обе стороны.

#Кривые ускорения

Одинаковая длительность, разное ощущение. Равномерное движение выглядит механически: в жизни ничего не трогается с места и не останавливается мгновенно.

Лаборатория переходов

Кривые ускорения наглядно: одинаковый путь, разный характер движения.

ФункцияХарактер
linearРавномерно, механически
ease-outБыстро стартует, мягко тормозит
ease-inМедленно стартует, резко заканчивает
cubic-bezier(.22,1,.36,1)Живой, «дорогой» выезд
ФункцияХарактерГде применять
linearРавномерноПолоса загрузки, бесконечное вращение
ease-outБыстрый старт, мягкое торможениеПоявление, наведение — самый частый выбор
ease-inМедленный старт, резкий конецИсчезновение, уход за край
ease-in-outПлавно с обоих концовПеремещение между двумя точками
cubic-bezier(.22,1,.36,1)Резкий старт, долгий выездОщущение «дорогого» интерфейса

#transform: сдвиг, масштаб, поворот

transform меняет то, как элемент нарисован, не трогая его место в раскладке. Соседи не сдвигаются, ничего не пересчитывается.

CSSОсновные функции
transform: translateY(-4px);          /* сдвиг */transform: translate(10px, -4px);     /* по обеим осям */transform: scale(1.05);               /* масштаб */transform: rotate(3deg);              /* поворот */transform: skewX(-8deg);              /* наклон *//* Несколько сразу — через пробел, порядок важен */transform: translateY(-4px) scale(1.03);

#Почему анимировать можно только две вещи

Чтобы показать кадр, браузер проходит четыре этапа: считает стили, считает геометрию (layout), рисует пиксели (paint) и складывает слои (composite).

Меняешь width или top — браузер пересчитывает геометрию всей страницы, потом всё перерисовывает. И так шестьдесят раз в секунду. Результат — рывки.

Меняешь transform или opacity — геометрия не меняется вообще. Работает только последний, самый дешёвый этап, который умеет делать видеокарта.

ВместоПиши
width, heighttransform: scale()
top, left, margintransform: translate()
display: none с анимациейopacity + visibility
background-positiontransform: translate() у внутреннего слоя
Кнопка и карточка с микро-анимацией
В песочницу

#@keyframes: анимация по кадрам

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

Таймлайн @keyframes

Показывает, что происходит с элементом на 0 %, 50 % и 100 % анимации.

КлючСостояние
0%Начало: прозрачность 0, сдвиг вниз
60%Проскочили чуть выше цели
100%Конец: на месте, полностью видно
CSSАнимация появления
@keyframes appear {  0%   { opacity: 0; transform: translateY(20px); }  100% { opacity: 1; transform: translateY(0); }}.card {  animation: appear 500ms ease-out both;  /*         │      │      │       └ сохранить конечное состояние             │      │      └ кривая             │      └ длительность             └ имя из @keyframes */}
СвойствоЧто задаёт
animation-nameИмя набора кадров
animation-durationДлительность одного прохода
animation-delayЗадержка перед стартом
animation-iteration-countСколько раз повторить, infinite — бесконечно
animation-directionalternate — туда-обратно
animation-fill-modeboth — держать первый кадр до старта и последний после

#Сколько должна длиться анимация

  • 100–200 мс — реакция на действие: наведение, нажатие. Должно ощущаться мгновенным.
  • 200–400 мс — появление элемента, раскрытие меню.
  • 400–800 мс — «повествовательные» вещи: демонстрация, обучающая подсказка.
  • Больше секунды — почти всегда раздражает. Человек уже готов действовать, а интерфейс ещё едет.

#Уважай тех, кому движение мешает

Для части людей анимация — не украшение, а причина тошноты и головокружения. У них в системе стоит настройка «уменьшить движение», и её нужно уважать.

CSSОбязательный блок в каждом проекте
@media (prefers-reduced-motion: reduce) {  *, *::before, *::after {    animation-duration: 0.001ms !important;    animation-iteration-count: 1 !important;    transition-duration: 0.001ms !important;    scroll-behavior: auto !important;  }}

Где спотыкаются почти все

transition написан внутри :hover

Почему Переход существует только во время наведения. Возврат происходит рывком.

Как надо Пиши transition в базовом состоянии элемента.

Анимируются width, height, top или margin

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

Как надо Переведи на transform и opacity — они не трогают раскладку.

transition: all

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

Как надо Перечисли нужные свойства через запятую.

Нет блока prefers-reduced-motion

Почему Людям с вестибулярными нарушениями сайт становится физически неприятен.

Как надо Добавь медиазапрос, отключающий нефункциональное движение. Это пять строк.

Переход transition
Плавное изменение свойства между двумя состояниями.
Кривая ускорения timing function, easing
Закон, по которому меняется скорость движения.
Трансформация transform
Сдвиг, масштаб, поворот или наклон без влияния на раскладку.
Ключевые кадры keyframes
Именованный набор состояний, между которыми идёт анимация.
Композитинг compositing
Последний, самый дешёвый этап отрисовки. Именно на нём работают transform и opacity.

Проверь себя

  1. Где нужно писать свойство transition?

    Тогда переход работает и при наведении, и при уходе курсора.

  2. Почему анимация <code>width</code> тормозит, а <code>transform: scale()</code> — нет?

    Изменение ширины запускает пересчёт раскладки и перерисовку. <code>transform</code> обходится последним, самым дешёвым этапом.

  3. Что делает <code>animation-fill-mode: both</code>?

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

  4. Сколько должна длиться реакция кнопки на наведение?

    Реакция на действие должна ощущаться мгновенной. Всё, что дольше 300 мс, начинает раздражать.

  5. Что стоит анимировать без опаски?

    Верных вариантов несколько.

    Только эти два свойства обходятся без пересчёта раскладки. Остальные заставляют браузер работать заново каждый кадр.

Задание

Кнопка и карточка с микро-анимацией

Оживи интерфейс:

  • кнопке добавь переход и состояние :hover, где она сдвигается вверх через transform;
  • карточке — переход и лёгкий подъём при наведении;
  • анимируй только transform, opacity и box-shadow, без all;
  • длительность переходов — от 120 до 400 мс;
  • добавь блок prefers-reduced-motion, отключающий движение.
Открыть в песочнице
    Подсказка 1

    Кнопке: transition: transform 180ms ease-out, box-shadow 180ms ease-out; и .btn:hover { transform: translateY(-2px); }

    Подсказка 2

    Карточке — то же самое, только чуть дольше: 260ms и translateY(-4px).

    Подсказка 3

    В конце добавь: @media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition-duration: .001ms !important; animation-duration: .001ms !important; } }

    Показать решение
    HTMLindex.html
    <p><button class="btn" type="button">Заказать</button></p><article class="card">  <h3>Раф на кокосовом</h3>  <p>Наведи курсор.</p></article>
    CSSstyle.css
    * { box-sizing: border-box; }.btn {  padding: 12px 26px;  font: inherit;  font-weight: 600;  color: #fff;  background: #6544e0;  border: 0;  border-radius: 999px;  cursor: pointer;  transition:    transform 180ms cubic-bezier(.22, 1, .36, 1),    box-shadow 180ms ease-out;}.btn:hover {  transform: translateY(-2px);  box-shadow: 0 10px 22px -10px rgb(101 68 224 / .7);}.btn:active {  transform: translateY(0);}.card {  max-width: 280px;  padding: 18px;  background: #f5f5fa;  border-radius: 14px;  transition:    transform 260ms cubic-bezier(.22, 1, .36, 1),    box-shadow 260ms ease-out;}.card:hover {  transform: translateY(-4px);  box-shadow: 0 18px 34px -18px rgb(20 24 48 / .4);}.card h3 { margin: 0 0 6px; font-size: 16px; }.card p { margin: 0; font-size: 13px; color: #666; }@media (prefers-reduced-motion: reduce) {  *, *::before, *::after {    animation-duration: 0.001ms !important;    transition-duration: 0.001ms !important;  }}

    Проверь себя