Как и многие вещи нового поколения CSS, свойство transform предельно замечательно.

На первый взгляд может показаться, что это довольно нишевая вещь. В конце концов, как часто нам нужно что-то поворачивать или искажать? Однако, опыт разработчиков показывает, что использование этого свойства является неотъемлемой частью верстки сайтов, причем способы его применения только увеличиваются.

В данной статье мы углубимся в свойство CSS transform и рассмотрим некоторые крутые и неожиданные вещи, которые можно сделать с ним!

Функции преобразований

Свойство transform способно совершать ряд операций над элементами с помощью функций преобразований. Рассмотрим каждую по очереди.

Перемещение (translate)

Функция translate позволяет переместить элемент относительно своего начального расположения. Синтаксис данной функции:

.element {
  transform: translate(X, Y);
}

Значения X и Y могут быть в любых единицах измерения и показывают величину перемещения элемента вдоль соответствующей оси.

Изменения значения для оси X перемещает элемент влево/вправо, для оси Y - вверх и вниз. Положительные значения перемещают элемент в направлениях вниз и вправо, отрицательные - вверх и влево.

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

В примере ниже у нас есть 3 дочерних элемента, выровненных с помощью флексбокс. Когда мы применяем преобразование к среднему, алгоритм флексбокса его игнорирует и оставляет все дочерние элементы на своих местах.

В целом, поведение очень похоже на то, когда мы пытаемся спозиционировать элемент с помощью свойств top / left / right / bottom. Основное отличие - элемент не вырывается из потока и не влияет на расположение соседних блоков.

Если нам необходимо переместить элемент только по одной оси, мы можем использовать translateX и translateY:

.element-1 {
  transform: translateX(20px);  /* эквивалентно transform: translate(20px, 0px); */
}
.element-2 {
  transform: translateY(20vh);  /* эквивалентно transform: translate(0px, 20vh); */
}

Самое интересное и невероятно мощное в этой функции - выражение перемещения в процентах.

Когда мы используем процентное значение в translate, этот процент относится к собственному размеру элемента, а не к доступному пространству в родительском контейнере.

Таким образом, значение transform: translateY(-100%) переместит элемент вверх ровно на его высоту независимо от ее значения.

Использование перемещения в процентах является одним из ответов на популярный вопрос собеседований "как выровнять элемент по центру?". Вот это решение:

За счет абсолютного позиционирования мы смещаем начало элемента (левый верхний угол) в самый центр родителя, а затем отрицательный translate величиной в половину ширины и высоты элемента делает его полностью отцентрированным. При этом становятся абсолютно не важными ни размеры родителя, ни дочернего элемента.

Стоит также добавить, что значениями для translate могут быть высчитанные с помощью calc() значения и даже переменные.

:root {
  --primary-size: 24px;
}
.element-1 {
  transform: translateX(calc(75% + 120px));
}
.element-2 {
  transform: translate(var(--primary-size));  /* эквивалентно transform: translate(24px, 24px); */
}

Масштаб (scale)

Функция scale позволяет увеличить/уменьшить элемент относительно своего начального размера. Синтаксис данной функции:

.element {
  transform: scale(1);  /* 1 - значение по-умолчанию */
}

Значением scale будет коэффициент, на который будет увеличен (если он больше 1) или уменьшен (если меньше 1) элемент. Одно значение scale изменит масштаб всего элемента, а при двух значениях элемент будет масштабироваться по каждой из осей соответствующе.

На первый взгляд может показаться, что масштабируется ширина и высота элемента. Однако тут есть одно большое отличие. Посмотрите, что будет происходить, если в нашем элементе есть текст:

Как видим, текст масштабируется вместе с элементом. Таким образом, с помощью функции scale мы не масштабируем только размер и форму элемента, а влияем на весь элемент и всех его потомков.

Небольшой пример использования scale в верстке информационных карточек при наведении на них:

Поворот (rotate)

Функция rotate поворачивает элемент по/против часовой стрелки на указанное количество градусов.

.element {
  transform: rotate(45deg);  /* элемент повернется на 45° по часовой стрелке */
}

Как и с функцией scale, поворот элемента затрагивает как сам элемент, так и все его дочерние. Оборот в 360° вернет элемент в то же положение.

Обычно для указания величины поворота используют градусы (deg). Однако, можно использовать еще одну удобную единицу измерения turn, которая показывает, сколько оборотов должен сделать элемент. 1turn = 360°.

Наклон (skew)

Эта функция применяется довольно редко, однако заслуживает не меньшего внимания. Она позволяет исказить элемент путем его наклона вдоль оси.

.element {
  transform: skew(45deg);
}

Размерность указывается в градусах и наклон по-умолчанию осуществляется вдоль оси X.

Чтобы наклонять элемент по оси Y или по обеим осям, запись функции должна быть следующей:

.element-1 {
  transform: skewX(30deg); /* выполнит наклон по оси X на 30° */
}
.element-2 {
  transform: skewY(10deg); /* выполнит наклон по оси Y на 10° */
}
.element-3 {
  transform: skew(30deg, -30deg); /* наклон по оси X на 30°, а по оси Y - на -30° */
}

Свойство transform-origin

Каждый элемент, подвергающийся преобразованию, имеет свой якорь (origin), относительно которого выполняется преобразование. По-умолчанию он расположен в центре элемента. Именно поэтому во всех предыдущих примерах стартовая точка изменения элемента была его центром. Однако, давайте взглянем на то, как, например, будет поворачиваться элемент, если его якорь будет не по центру.

Положение transform-origin задается однократно и будет применяться к любой из функций преобразования. Синтаксис данного свойства:

.element {
  transform-origin: positionX positionY;
}

Значения positionX и positionY могут быть заданы в любых единицах измерения, либо с помощью ключевых слов top / left / right / bottom, обозначающих, к какому краю должен быть прибит якорь. Одинарное значение свойства будет означать, что оно относится и к положению по вертикали, и к положению по горизонтали.

.element-1 {
  transform-origin: left center;  /* якорь прибит к левой границе и по центру вертикали */
}
.element-2 {
  transform-origin: 100% 20px;  /* якорь прибит к правой границе с отступом 20px сверху */
}
.element-3 {
  transform-origin: 50px;  /* якорь имеет отступ 50px слева и сверху */
}

Комбинирование функций

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

.element {
  transform: translate(50%, 50%) rotate(90deg) scale(0.5);
}

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

Вначале мы переместили элемент, а затем повернули. Направление оси не менялось, поэтому поведение преобразования довольно очевидное.

А вот если вначале повернуть элемент - вместе с поворотом элемента повернется его ось, вдоль которой уже он дальше будет перемещаться.

А вот как с помощью комбинирования функций преобразований и анимации сделать бесконечное вращение планет вокруг Солнца:

Строчные элементы

Одна общая проблема с преобразованиями заключается в том, что они не работают со строчными элементами в разметке.

<p>Привет! Меня зовут <span class="inline-element">Иван</span></p>
.inline-element {
  transform: rotate(45deg);  /* не отработает */
}

Самое простое решение - задать строчному элементу свойство display: inline-block или сделать его частью флексбоксов или гридов.

3D-преобразования

Все преобразования, рассмотренные в данной статье, относятся к 2D-преобразованиям, поскольку выполняются в одной плоскости осей X и Y. Однако, видоизменять элементы можно и в 3D-пространстве. Для этого добавляется третья ось Z и свойство perspective.

Более углубленный обзор 3D-преобразований доступен в другой статье. В качестве примера того, как можно использовать преобразования в 3D-пространстве, взгляните на демо ниже (по наведению книга будет раскрываться):