translate, translateX и translateY сдвигают элемент по горизонтали и/или вертикали, не влияя на поток документа (это мы разбирали в прошлом уроке). Самая частая ловушка - проценты внутри translate считаются не от родителя, как в left или top, а от размеров САМОГО элемента. Именно на этом построен знаменитый приём центрирования: translate(-50%, -50%) сдвигает элемент ровно на половину его собственной ширины и высоты, откуда бы он ни начинал.
В этом уроке
Попробуй прямо сейчас: допиши в .centered побольше текста или увеличь padding - блок останется идеально по центру рамки, хотя его размер изменился. Это и есть суть приёма: top: 50%; left: 50% ставит ЛЕВЫЙ ВЕРХНИЙ угол блока в центр рамки, а translate(-50%, -50%) дальше сдвигает блок на половину ЕГО СОБСТВЕННОЙ (уже изменившейся) ширины и высоты - поэтому пересчитывать вручную ничего не нужно.
translateX() и translateY(): сдвиг по одной оси
translateX() сдвигает элемент строго по горизонтали, translateY() - строго по вертикали. Здесь легко перепутать оси, особенно на старте: X - это горизонталь (как ось абсцисс в математике), Y - вертикаль. Оба принимают длину (px, rem и так далее) или процент.
.btn:hover {
transform: translateY(-4px);
} - кнопка приподнимается вверх при наведении (отрицательное значение по Y - движение вверх).
Неправильное ожидание - что раз "приподнять" звучит как движение вверх, значит нужен translateX:
.btn:hover {
transform: translateX(-4px);
} - это сдвинет кнопку влево по горизонтали, а не вверх - визуально совсем не тот эффект.
translate(): сдвиг по обеим осям и приём с центрированием
translate() принимает одно или два значения: одно значение задаёт сдвиг только по X (Y остаётся нулевым - translate(50px) равносильно translate(50px, 0)), два значения через запятую задают сдвиг по X и по Y сразу. Ключевая особенность - проценты в translate() считаются от размеров САМОГО трансформируемого элемента, а не от родителя, как это работает в left, top, margin или width. Именно поэтому translate(-50%, -50%) в сочетании с top: 50%; left: 50% центрирует элемент любого размера без единого пересчёта вручную: сначала верхний левый угол элемента ставится точно в центр родителя, а затем элемент сдвигается на половину собственной ширины и высоты назад - и его собственный центр совпадает с центром родителя.
.modal {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
} - модальное окно любого размера остаётся по центру родителя.
Неправильное ожидание - что для центрирования достаточно top: 50%; left: 50% без transform:
.modal {
position: absolute;
top: 50%;
left: 50%;
} - это поставит ЛЕВЫЙ ВЕРХНИЙ угол окна в центр родителя, а не центр самого окна - визуально элемент окажется смещён вправо-вниз от истинного центра, причём тем сильнее, чем крупнее сам элемент.
Отдельное свойство translate - и как оно сочетается с rotate и scale
Помимо функции translate() внутри transform, в CSS есть отдельное, самостоятельное свойство translate - пишется без transform: translate: 50px 20px;. У него та же семантика (один или два значения, проценты от собственного размера), но главное преимущество раскрывается при сочетании с другими трансформациями: если translate, rotate и scale заданы как отдельные свойства, браузер ВСЕГДА применяет их в фиксированном порядке - сначала translate, потом rotate, затем scale, а уже после них - всё, что написано в transform - и этот порядок не зависит от того, в какой последовательности вы перечислили сами свойства в CSS. Это отличается от функций внутри одного transform: там порядок функций в строке действительно влияет на результат (эту тему подробно разберём в уроке про комбинирование трансформаций).
.card {
translate: 20px 0;
rotate: 8deg;
scale: 1.1;
} - три отдельных свойства, порядок объявления в CSS не важен: сдвиг всегда применится раньше поворота и масштаба.
Неправильное ожидание - что порядок отдельных свойств в CSS определяет порядок их применения, как это происходит с функциями внутри transform:
.card {
rotate: 8deg;
translate: 20px 0;
} - несмотря на то, что rotate написан раньше translate, результат будет ТОЧНО ТАКИМ ЖЕ, как в правильном примере выше: сначала всё равно применится сдвиг, потом поворот.
Из истории
Отдельные свойства translate, rotate и scale появились в браузерах далеко не сразу после transform: Safari получил экспериментальную поддержку в Safari Technology Preview 117 в декабре 2020 года. В официальном анонсе разработчики WebKit процитировали формулировку из спецификации CSS Transforms Level 2: сначала применяются отдельные свойства - translate, rotate, затем scale, - и только после них функции внутри свойства transform. Это буквально та же фраза, что объясняет фиксированный порядок из этого раздела. Анонс в блоге WebKit.
Частые ошибки новичков
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Перепутать translateX и translateY | Элемент двигается не по той оси, которая нужна по замыслу | X - горизонталь, Y - вертикаль; при сомнении проверять в предпросмотре |
| Ожидать, что проценты в translate() считаются от родителя, как в left/top | Результат сдвига не совпадает с ожиданиями, особенно при разных размерах элемента | Помнить: проценты в translate - от размеров самого элемента, а не родителя |
| Использовать top: 50%; left: 50% для центрирования без transform | Центрируется угол элемента, а не сам элемент - смещение тем заметнее, чем крупнее элемент | Добавлять transform: translate(-50%, -50%) вместе с top/left: 50% |
| Думать, что порядок отдельных свойств translate/rotate/scale в CSS влияет на результат | Порядок объявления свойств не имеет значения - браузер всегда применяет их в фиксированном порядке | Не тратить время на подбор порядка отдельных свойств; порядок важен только для функций внутри transform |
Шпаргалка
| Запись | Что означает |
|---|---|
| transform: translateX(20px); | Сдвиг строго по горизонтали |
| transform: translateY(20px); | Сдвиг строго по вертикали |
| transform: translate(20px); | Сдвиг по X, Y равен 0 (то же самое, что translateX(20px)) |
| transform: translate(20px, 10px); | Сдвиг по X и по Y одновременно |
| Проценты в translate | Считаются от размеров самого элемента, а не родителя |
| top: 50%; left: 50%; transform: translate(-50%, -50%); | Универсальное центрирование элемента любого размера внутри позиционированного родителя |
Коротко: отвечаем на вопросы
Почему для центрирования нужны сразу и top: 50%; left: 50%, и transform: translate(-50%, -50%)?
top: 50%; left: 50% ставит верхний левый угол элемента в центр родителя - но это ещё не центр самого элемента. transform: translate(-50%, -50%) дополнительно сдвигает элемент на половину его собственной ширины и высоты назад, и только после этого центр элемента совпадает с центром родителя. Первое работает с процентами от родителя, второе - с процентами от самого элемента, поэтому нужны оба шага вместе.
Чем translate(50px) отличается от translate(50px, 50px)?
Если указано одно значение, оно задаёт сдвиг только по X, а Y автоматически считается равным 0 - translate(50px) полностью равносилен translate(50px, 0) и translateX(50px). Чтобы сдвинуть элемент ещё и по вертикали, нужно явно указать второе значение.