transform - это CSS-свойство для визуальных превращений элемента: сдвига, поворота, масштабирования и наклона. В отличие от margin, top или width, трансформация не влияет на поток документа - соседние элементы не сдвигаются, даже если элемент визуально стал в разы больше. Именно поэтому transform - предпочтительный инструмент для плавных и производительных анимаций.
В этом уроке
Попробуй прямо сейчас: наведи курсор на любой из двух рядов. В первом ряду блок A увеличивается через width - сосед B честно отодвигается вправо, ведь ширина влияет на поток документа. Во втором ряду блок A растёт через transform: scale() и становится даже визуально крупнее, чем в первом случае - но сосед B при этом не двигается ни на пиксель, будто transform для потока документа как будто не существует.
Чем transform отличается от изменения layout-свойств
Свойства вроде width, margin, top или left меняют место элемента в потоке документа - браузеру приходится пересчитывать положение всех соседей заново (это называется layout, или reflow). transform работает иначе: он рисует элемент уже в другом месте/размере/повороте на отдельном визуальном слое, не трогая расчёт положения остальных элементов страницы. Для браузера это ощутимо дешевле: в измерениях web.dev анимация через top/left давала около 37 мс работы рендера на кадр и до половины потерянных кадров, а та же самая анимация через transform: translate() почти не создавала работы для рендера и теряла около 1% кадров.
.card {
transition: transform .3s;
}
.card:hover {
transform: translateY(-8px);
} - движение через transform: браузер анимирует его на отдельном слое, без пересчёта layout.
Неправильное ожидание - что для простого "сдвига при наведении" сгодится любое из привычных свойств:
.card {
transition: margin-top .3s;
}
.card:hover {
margin-top: -8px;
} - визуально похожий результат, но каждый кадр анимации запускает пересчёт layout для этого элемента и его соседей - на слабых устройствах или при множестве анимированных элементов это может привести к дёрганой анимации.
Из истории
Экспериментальную поддержку CSS-трансформаций (тогда ещё под именем -webkit-transform) браузерный движок WebKit получил 26 октября 2007 года - об этом в своём блоге объявил инженер Apple Дэйв Хайетт (Dave Hyatt), тот же человек, что двумя неделями позже, 7 ноября 2007 года, предложил W3C идею CSS-переходов и @keyframes (см. урок 1). Уже в том самом анонсе 2007 года Хайетт прямо написал: трансформации не влияют на layout и в этом смысле похожи на relative-позиционирование - то есть ключевое свойство, о котором говорит этот урок, было заложено в transform с самого первого дня. Оригинальный анонс в блоге WebKit.
К каким элементам transform вообще применяется
Спецификация прямо ограничивает transform: он действует на все элементы, чей layout управляется блочной моделью CSS, КРОМЕ строчных элементов без замены (non-replaced inline) - то есть обычных span, a, em и подобных им, если их display не менялся. У таких элементов transform будет честно показан в вычисленных стилях (getComputedStyle покажет ту же матрицу, что вы написали), но визуально не сработает вообще никак - ни сдвига, ни поворота. Чтобы трансформация подействовала на строчный по умолчанию элемент, ему нужно задать display: inline-block, display: block или любое другое значение, при котором элемент перестаёт быть "чисто строчным".
.tag {
display: inline-block;
transform: rotate(-6deg);
} - span с display: inline-block поворачивается, как и ожидается.
Неправильное ожидание - что transform сработает на любом элементе, если написать его синтаксически верно:
.tag {
transform: rotate(-6deg);
} - если .tag - это span без изменения display, поворота не будет вообще: свойство просто не применяется к строчным элементам без замены, и никакой ошибки при этом не возникает.
Дальше в этой группе разберём функции transform по отдельности: translate для сдвига по осям, scale для масштабирования, rotate для поворота, skew для наклона, как их комбинировать в одном свойстве и что такое transform-origin и matrix() - но в основе всех них лежат именно те два принципа, которые мы разобрали в этом уроке.
Частые ошибки новичков
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Анимировать перемещение через margin-top/left/top вместо transform | Каждый кадр анимации запускает пересчёт layout для элемента и соседей - дороже для браузера, риск дёрганой анимации | Использовать transform: translate() для сдвига, где это возможно |
| Применить transform к span/a/em без изменения display | Трансформация не применяется к строчным элементам без замены - визуально ничего не происходит, хотя ошибки нет | Задать display: inline-block или display: block элементу перед transform |
| Ожидать, что трансформированный элемент "толкает" соседей, раз он визуально стал больше | transform не влияет на поток документа - соседи остаются на исходных местах независимо от масштаба/поворота | Если соседей действительно нужно сдвинуть, делать это отдельно (например, через margin у самих соседей) |
| Доверять панели Computed Style в DevTools как доказательству, что transform виден на странице | Для строчных элементов там всё равно покажется указанная матрица, даже если визуально ничего не изменилось | Проверять результат глазами на самой странице, а не только по вычисленным стилям |
Шпаргалка
| Факт | Что это значит |
|---|---|
| transform | Сдвиг, поворот, масштаб и наклон элемента одним свойством |
| Не влияет на поток документа | Соседние элементы не сдвигаются, даже при сильном увеличении/повороте |
| Дешевле для анимации, чем margin/top/width | Не запускает пересчёт layout - анимируется на отдельном слое |
| Не работает на строчных элементах без замены | span/a/em и подобные нужно перевести в inline-block/block, чтобы transform подействовал |
| Работает на блочных, инлайн-блочных, заменяемых | div, инлайн-блоки, img и подобные - transform действует как ожидается |
| Дальше по программе | translate, scale, rotate, skew - отдельные функции; их комбинирование и matrix() - позже |
Коротко: отвечаем на вопросы
Я написал transform: rotate() для span, но ничего не происходит - в чём может быть дело?
Почти наверняка дело в том, что span по умолчанию строчный (inline) элемент, а transform не применяется к строчным элементам без замены. Решение - задать элементу display: inline-block или display: block, после чего трансформация сразу заработает.
Почему transform считается лучше, чем top/left, для анимации перемещения элемента?
Потому что transform не запускает пересчёт layout - браузер анимирует его на отдельном слое, тогда как изменение top/left/margin заставляет пересчитывать положение элемента и его соседей на каждом кадре. На практике это ощутимая разница в плавности, особенно при нескольких одновременно анимированных элементах.