Трансформации: translate, scale, rotate, 3D

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

transform — это набор геометрических преобразований (сдвиг, масштаб, поворот, наклон), которые меняют визуальное представление элемента, но не его место в потоке документа.

Можно сдвинуть блок через margin или top, но это заставит браузер пересчитывать раскладку соседей. transform работает иначе: он искажает только отрисовку самого элемента, оставляя его исходное место занятым. Поэтому он быстр и не «толкает» соседние элементы — и именно его анимируют в первую очередь.

2D-функции transform

В одно свойство transform можно передать несколько функций через пробел.

ФункцияЧто делает
translate(x, y)сдвиг по горизонтали и вертикали
scale(s) / scale(sx, sy)масштаб; 1.2 — крупнее на 20%
rotate(угол)поворот вокруг центра, напр. rotate(15deg)
skew(ax, ay)наклон (сдвиг-перекос) по осям
.card {
  transition: transform 0.2s ease-out;
}
.card:hover {
  /* приподнять и чуть увеличить */
  transform: translateY(-6px) scale(1.03);
}

У translate есть удобная особенность: проценты считаются от размеров самого элемента. Это даёт знаменитый приём центрирования: сдвинуть элемент на половину собственной ширины и высоты.

.modal {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

transform-origin: точка отсчёта

По умолчанию все преобразования идут относительно центра элемента (50% 50%). transform-origin сдвигает эту точку. Сравните поворот вокруг центра и вокруг левого верхнего угла — это разное движение.

.flag {
  transform-origin: top left;
  transition: transform 0.3s ease;
}
.flag:hover {
  transform: rotate(8deg); /* качается, как закреплённый за угол */
}

Для scale это особенно важно: меню, раскрывающееся transform: scaleY(...) от transform-origin: top, выглядит как «опускающаяся шторка», а от центра — как «раздувающееся из середины».

Комбинирование и его порядок

Функции применяются слева направо, и порядок меняет результат. translateX(100px) rotate(45deg) — сначала сдвиг, потом поворот вокруг новой позиции. А rotate(45deg) translateX(100px) — сначала поворачивается система координат, и сдвиг идёт уже под углом 45°. Это частый источник «почему элемент улетел не туда».

.a { transform: rotate(45deg) translateX(100px); }  /* сдвиг по наклонной оси */
.b { transform: translateX(100px) rotate(45deg); }  /* сдвиг по горизонтали, затем поворот */

Трансформации в 3D

CSS умеет вращать элементы в трёх измерениях. rotateX(угол) опрокидывает элемент «от себя/на себя» вокруг горизонтальной оси, rotateY(угол) вращает как дверь вокруг вертикальной оси, rotateZ совпадает с обычным 2D-rotate. Есть и translateZ — приближение/удаление по глубине.

perspective: откуда мы смотрим

Без перспективы 3D-поворот выглядит как плоское сжатие — нет ощущения глубины. perspective задаёт расстояние от зрителя до плоскости экрана: чем меньше значение, тем сильнее искажение. Его ставят на родителя сцены.

.scene {
  perspective: 800px; /* виртуальная камера в 800px от экрана */
}
.scene:hover .card {
  transform: rotateY(35deg);
}
.card {
  transition: transform 0.5s ease;
}

Альтернатива — функция perspective() прямо в transform самого элемента: transform: perspective(800px) rotateY(35deg). Разница в том, что на родителе одна перспектива объединяет всех детей в общую сцену, а функция в transform даёт каждому элементу свою «камеру».

Эффект переворота карточки

.flip {
  perspective: 1000px;
}
.flip-inner {
  transition: transform 0.6s;
  transform-style: preserve-3d; /* сохранить 3D для детей */
}
.flip:hover .flip-inner {
  transform: rotateY(180deg);
}

transform-style: preserve-3d нужно, чтобы вложенные стороны карточки тоже жили в общем 3D-пространстве, а не сплющивались на плоскость родителя. А чтобы изнанка повёрнутого элемента не просвечивала, ей задают backface-visibility: hidden — тогда видна только лицевая сторона.

Отдельные оси и сокращённые формы

Почти у каждой функции есть «осевые» варианты: translateX(), translateY(), scaleX(), scaleY(), rotateZ(). Они читаются яснее, чем общая форма, и экономят запись, когда меняется только одна ось. Например, индикатор прогресса удобно «расти» через scaleX от левого края — это анимация на этапе композиции, в отличие от анимации width:

.progress-fill {
  transform-origin: left center;
  transform: scaleX(0);
  transition: transform 0.3s ease-out;
}
.progress-fill.is-filled {
  transform: scaleX(1); /* заполнено на 100% */
}

Под капотом все функции — лишь удобные имена для общей матрицы, которую можно записать и напрямую через matrix()/matrix3d(), но в реальном коде так почти не пишут: именованные функции читаемее. Важно помнить про единицы: углы задаются с deg (или turn, rad), длины — с px/%/em, а scale — безразмерным множителем, где 1 означает исходный размер.

Как это работает под капотом

Любую цепочку функций браузер сворачивает в одну матрицу преобразования (2D — 3×3, 3D — 4×4) и применяет её на этапе композиции — уже после того, как раскладка и отрисовка посчитаны. Поэтому transform не меняет geometry потока: соседи остаются на местах, а сам элемент лишь рисуется в новой форме. Это же объясняет производительность: композитор умеет накладывать матрицу на готовый слой, не перезапуская дорогие этапы layout и paint.

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

  • Ожидают, что transform подвинет соседей. Нет: исходное место остаётся занятым, элемент лишь визуально смещается и может перекрыть другие.
  • Перепутан порядок функций. rotate перед translate уводит сдвиг по наклонной оси.
  • 3D-поворот без perspective. Выходит плоское сжатие вместо объёма.
  • Забыт transform-origin. Раскрывающаяся «шторка» раздувается из центра, а не опускается сверху.

Итоги

  • transform объединяет translate, scale, rotate, skew и их 3D-версии.
  • Преобразования не влияют на поток — место элемента остаётся занятым.
  • transform-origin задаёт точку, вокруг которой идёт поворот и масштаб.
  • Функции применяются слева направо; порядок важен.
  • Для 3D нужна perspective на родителе и часто transform-style: preserve-3d.
Проверьте себя
1. Как transform влияет на поток документа?
AДвигает соседние элементы, освобождая место
BНе влияет: исходное место элемента остаётся занятым, меняется только отрисовка
CПолностью убирает элемент из потока, как display: none
DПревращает элемент в inline
2. Чем отличается transform: rotate(45deg) translateX(100px) от translateX(100px) rotate(45deg)?
AНичем, порядок функций не важен
BВ первом случае сначала поворачивается система координат, и сдвиг идёт по наклонной оси
CВторой вариант вызовет ошибку парсинга
DПервый вариант игнорирует translateX
3. Зачем нужно свойство perspective при 3D-поворотах?
AЧтобы анимация шла быстрее
BОно задаёт расстояние до зрителя и создаёт ощущение глубины, иначе поворот выглядит плоским сжатием
CБез него rotateY вообще не работает в браузере
DОно заменяет transform-origin