matrix() и matrix3d() - это "сырой" вид, к которому браузер сводит абсолютно любой transform: translate, rotate, scale и skew из прошлых уроков. matrix() - шесть чисел для двумерных трансформаций, matrix3d() - шестнадцать чисел для трёхмерных (rotateX, rotateY, translateZ). Именно это ты увидишь в DevTools, даже если в коде написаны обычные читаемые функции.
В этом уроке
Попробуй прямо сейчас: в состоянии покоя у блока написано transform: matrix(1, 0, 0, 1, 0, 0) - это "пустая", единичная матрица, которая ничего не меняет, точно как transform: none. При наведении включается transform: matrix(0.866025, 0.5, -0.5, 0.866025, 40, 20) - ОДНА строка, которая одновременно сдвигает блок на 40px вправо и 20px вниз и поворачивает его на 30 градусов. Это ровно то же самое, что transform: translate(40px, 20px) rotate(30deg) - просто уже посчитанное браузером наперёд.
matrix(): шесть чисел вместо translate, rotate, scale и skew
Любая комбинация translate/rotate/scale/skew в итоге превращается в шесть чисел: matrix(a, b, c, d, tx, ty). Первые четыре числа отвечают за поворот и масштаб: a и d - это масштаб (или косинус угла поворота), b и c - наклон (или синус угла поворота). Последние два, tx и ty, - это чистый сдвиг по горизонтали и вертикали. Например, rotate(45deg) даёт matrix(0.707, 0.707, -0.707, 0.707, 0, 0), а translate(40px, 25px) - matrix(1, 0, 0, 1, 40, 25). Собственная проверка показала любопытную деталь: точка трансформации из прошлого урока в эту матрицу вообще не попадает - transform-origin применяется браузером отдельно при отрисовке, поэтому одна и та же матрица получается независимо от того, где стоит точка. А вот что действительно видно в матрице - это разница из урока про порядок функций: translate(80px,0) rotate(45deg) даёт tx=80, а rotate(45deg) translate(80px,0) - tx=56.57 - та же самая асимметрия, только теперь в виде голых чисел.
.card {
transform: translate(40px, 20px) rotate(30deg);
} - понятно, что делает код, и легко поменять только сдвиг или только угол.
Неправильный ход - пытаться "увеличить масштаб в 2 раза", подправив вручную только a и d в готовой матрице с поворотом:
.card {
/* было: matrix(1.732, 1, -1, 1.732, 40, 20) - поворот 30deg, масштаб x2 */
transform: matrix(1.732, 0.5, -0.5, 1.732, 40, 20); /* поменяли только a и d */
} - собственная проверка показала, что так съезжают И угол поворота (получится около 16 градусов, а не 30), И масштаб (около 1.8 раза, а не 2) - менять нужно было все четыре числа a, b, c, d сразу.
matrix3d(): когда нужны все шестнадцать чисел
matrix() устроен так, что описывает только плоские, двумерные превращения. Как только в цепочке transform появляется что-то по-настоящему трёхмерное - rotateX(), rotateY(), translateZ(), translate3d() или сама функция perspective() - браузер переходит на matrix3d(): те же шесть чисел вырастают до шестнадцати, потому что описывается уже трёхмерная матрица 4x4. Собственная проверка подтвердила: rotateY(45deg) и translateZ(10px) в getComputedStyle возвращаются именно как matrix3d(...), а не matrix(...), даже если в коде была написана одна простая функция. Формально matrix() - это просто частный случай matrix3d() с нулями и единицами в "третьем измерении". Но matrix3d() сам по себе не гарантирует, что элемент действительно будет выглядеть объёмным: собственная проверка показала, что rotateY(50deg) без свойства perspective на родительском элементе выглядит просто как плоское горизонтальное сжатие, без всякого ощущения глубины - настоящий наклон появляется, только если у родителя задан perspective (или использована функция perspective() прямо внутри transform).
Из истории
Обычные, плоские CSS-трансформации появились в 2007 году, а вот трёхмерные - позже и отдельно: в официальном блоге WebKit 16 июля 2009 года Саймон Фрейзер (Simon Fraser) объявил о поддержке 3D-трансформаций в WebKit на Mac OS X, уточнив, что на iPhone они были доступны уже с версии OS 2.0 - то есть ещё в 2008 году. Оригинальный анонс в блоге WebKit.
.card {
transform: perspective(600px) rotateY(30deg);
} - perspective() прямо внутри transform - блок наклоняется по-настоящему объёмно.
Неправильное ожидание - что rotateY() сам по себе даёт объёмный эффект:
.card {
transform: rotateY(30deg);
} - без perspective блок просто визуально сжимается по горизонтали, как будто применили scaleX() - никакой глубины не видно.
Частые ошибки новичков
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Пытаться "увеличить масштаб" в готовой matrix(), меняя только числа a и d | Если в матрице уже есть поворот (b и c не равны 0), угол поворота и итоговый масштаб съезжают одновременно - результат не совпадает ни с ожидаемым углом, ни с размером | Собирать трансформацию через отдельные функции translate/rotate/scale и не редактировать сырые числа матрицы вручную |
| Путать порядок параметров matrix(a, b, c, d, tx, ty) | Сдвиг (tx, ty) - это всегда два ПОСЛЕДНИХ числа, а не первые - первые четыре отвечают за поворот и масштаб | Сверяться с формулой matrix(scaleX(), skewY(), skewX(), scaleY(), translateX(), translateY()) перед ручной правкой |
| Думать, что matrix3d() сам по себе даёт объёмный эффект | Без perspective на родителе (или perspective() в самом transform) rotateX()/rotateY() выглядят как плоское сжатие, без ощущения глубины | Всегда задавать perspective - на родительском элементе или прямо внутри transform |
| Удивляться, что getComputedStyle().transform показывает matrix3d(...) с 16 числами, хотя в CSS был написан один простой rotateX() или translateZ() | Браузер всегда "сворачивает" любую трёхмерную функцию в matrix3d() - это нормальное внутреннее представление, а не ошибка | Воспринимать matrix3d() в DevTools как норму и, если нужно узнать исходные значения, смотреть в саму CSS-таблицу стилей, а не в computed-стиль |
Шпаргалка
| Запись | Что означает |
|---|---|
| matrix(a, b, c, d, tx, ty) | 6 чисел для 2D: a, d - масштаб/косинус, b, c - наклон/синус, tx, ty - сдвиг |
| rotate(45deg) | matrix(0.707, 0.707, -0.707, 0.707, 0, 0) |
| translate(40px, 25px) | matrix(1, 0, 0, 1, 40, 25) |
| scale(2, 3) | matrix(2, 0, 0, 3, 0, 0) |
| matrix3d(16 чисел) | 3D-версия matrix() - появляется, как только в цепочке есть rotateX/Y, translateZ, perspective и т.п. |
| transform-origin (урок 13) | Не входит в саму матрицу - браузер применяет точку трансформации отдельно при отрисовке |
Коротко: отвечаем на вопросы
Почему в getComputedStyle().transform всегда получается matrix3d(), хотя в CSS был написан простой translate3d() с rotateX/Y?
Потому что браузер всегда нормализует любую трёхмерную трансформацию в единую матрицу 4x4 - matrix3d() с 16 числами - независимо от того, сколько отдельных функций было написано в исходном CSS. Достать обратно исходные читаемые значения (например, конкретный сдвиг по Z) из готовой матрицы просто так нельзя - нужно либо хранить эти значения отдельно в JavaScript, либо разбирать матрицу вручную по формулам.
Я применил transform: rotateY(-25deg) к картинке, но она просто сжалась по горизонтали, а не наклонилась в перспективе. Почему?
Потому что одного rotateY() недостаточно для объёмного эффекта - без свойства perspective (на родительском элементе или через функцию perspective() прямо в transform) браузеру не из чего строить перспективу, и поворот вокруг вертикальной оси выглядит просто как плоское сжатие. Нужно добавить, например, perspective: 600px на обёртку или переписать как transform: perspective(600px) rotateY(-25deg).