transform-origin - это точка, вокруг которой происходит поворот, масштабирование или наклон элемента через transform. По умолчанию она находится точно в центре блока (50% по горизонтали и по вертикали), поэтому scale() и rotate() из прошлых уроков всегда работали симметрично. Сдвинув эту точку к краю или углу, можно заставить элемент расти или поворачиваться не от середины, а от нужного места.
Попробуй прямо сейчас: пунктирная рамка - это флаг в состоянии покоя. Наведи курсор - флаг поворачивается на 15 градусов, как будто его треплет ветер. Обрати внимание: левый верхний угол флага, прибитый к флагштоку, вообще не двигается - поворот происходит именно вокруг него, потому что там стоит transform-origin: top left. Попробуй убрать transform-origin из кода (или поменять на center) - флаг начнёт поворачиваться вокруг своей середины и оторвётся от флагштока.
Как задать точку трансформации
По умолчанию transform-origin равен 50% 50% - центр элемента по обеим осям, именно поэтому rotate() и scale() из прошлых уроков всегда крутили и растягивали элемент строго от середины. Значение можно задать числом, процентом или ключевым словом (left, center, right, top, bottom), а собственная проверка вскрыла тонкость, которую легко понять неправильно: если указано ОДНО число или процент (например 20px), это ВСЕГДА горизонтальная ось - точка сдвигается на 20px от левого края, а по вертикали остаётся в центре. Но если указано одно КЛЮЧЕВОЕ СЛОВО вертикальной оси - top или bottom, - оно задаёт именно вертикаль, а горизонталь сама становится center: transform-origin: top на самом деле означает "center top", а не "20px" в буквальном смысле "первое значение - это всегда горизонталь". Два ключевых слова можно писать в любом порядке: right bottom и bottom right - это одно и то же. А ещё точку можно вынести ЗА пределы блока - отрицательным значением или числом больше 100%, - это пригодится, например, для эффекта вращения по орбите вокруг далёкой точки.
.badge {
transform-origin: 50% 20px;
transform: rotate(20deg);
} - обе оси заданы явно: точка ровно по центру по горизонтали и в 20px от верхнего края по вертикали.
Неправильное ожидание - что одно число сдвигает точку по вертикали, как отступ сверху:
.badge {
transform-origin: 20px; /* хотели: на 20px ниже верхнего края */
transform: rotate(20deg);
} - на самом деле точка окажется в 20px от ЛЕВОГО края, а по вертикали останется точно по центру - совсем не там, где хотели.
Как точка меняет поворот и масштабирование
Как ты уже видел в уроке про scale, по умолчанию рост идёт строго от центра - элемент увеличивается симметрично во все стороны. Если задать transform-origin: top left, эффект меняется: собственная проверка показала, что верхний левый угол блока остаётся АБСОЛЮТНО неподвижным (те же координаты до и после), а сам блок растёт только вправо и вниз - именно так должно вести себя настоящее выпадающее меню, прикреплённое к углу кнопки. То же самое верно для rotate(): элемент поворачивается вокруг угла, а не вокруг середины. Важно и обратное: сам по себе transform-origin ничего не делает - собственная проверка подтвердила, что без единой функции внутри transform (rotate/scale/translate/skew) элемент выглядит и стоит ровно так же, как если бы transform-origin вообще не было задано. И ещё одна деталь: проценты в transform-origin считаются от размеров СОБСТВЕННОГО блока элемента, точно как и в transform: translate() - не от родителя и не от окна браузера.
.menu {
transform-origin: top left;
transform: scale(0);
transition: transform .2s;
}
.wrapper:hover .menu {
transform: scale(1);
} - меню появляется, разворачиваясь именно из угла кнопки, к которому оно прикреплено.
Неправильное ожидание - что меню само "поймёт", откуда ему появляться:
.menu {
transform: scale(0);
transition: transform .2s;
}
.wrapper:hover .menu {
transform: scale(1);
} - transform-origin не задан, значит используется значение по умолчанию (центр) - меню будет расти во все стороны от середины, а не из угла кнопки.
Частые ошибки новичков
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Считать, что одно число в transform-origin сдвигает точку по вертикали | Одно число или процент без ключевого слова - это всегда горизонтальная ось, вертикаль остаётся в центре | Указывать обе оси явно (transform-origin: 50% 20px), если нужен именно сдвиг по вертикали |
| Думать, что ключевые слова top/bottom обязательно писать вместе с center | Одно ключевое слово вроде top само задаёт свою ось - вторая ось автоматически становится center, "top" и "center top" это одно и то же | Не дублировать center - одного ключевого слова достаточно, если вторая ось и так должна быть по центру |
| Задать transform-origin и ждать, что элемент сам сдвинется или изменит вид | transform-origin ничего не меняет сам по себе - это только точка отсчёта; без rotate/scale/translate/skew внутри transform она никак не проявляется | Проверять эффект только вместе с реальной функцией трансформации, в блокноте |
| Думать, что проценты в transform-origin считаются от родителя или от экрана | Проценты всегда считаются от размеров СОБСТВЕННОГО блока элемента, как и в transform: translate() | Для точки за пределами блока (например, для орбитального вращения) использовать отрицательные значения или числа больше 100%, а не проценты "от родителя" |
Шпаргалка
| Запись | Что означает |
|---|---|
| transform-origin: 50% 50%; | Значение по умолчанию - центр элемента |
| transform-origin: 20px; | Одно число - всегда горизонталь (X), вертикаль остаётся center |
| transform-origin: top; | Одно ключевое слово само задаёт свою ось - равно "center top" |
| right bottom = bottom right | Порядок ключевых слов не важен |
| transform-origin: -50% 50%; или 150% 50%; | Точка выходит за пределы блока - пригодится для орбитального вращения |
| transform-origin без rotate/scale/translate/skew | Никакого эффекта - точка "молчит", пока нет самой трансформации |
Коротко: отвечаем на вопросы
Можно ли задать transform-origin относительно центра окна браузера, а не самого элемента?
Нет - transform-origin всегда считается относительно собственного блока элемента, других вариантов у свойства просто нет. Если нужна точка вне блока (например, чтобы элемент вращался по орбите вокруг далёкой точки), придётся сдвинуть саму точку отрицательным значением или числом больше 100% - но отсчёт всё равно идёт от размеров и позиции элемента, а не от окна.
Как сделать, чтобы элемент вращался по кругу вокруг другого объекта, а не вокруг своего центра?
Сдвинуть transform-origin далеко за пределы самого элемента - например, на расстояние будущего радиуса орбиты - а затем применить rotate() с анимацией. Раз точка находится не в центре элемента, а где-то в стороне, поворот превращается в движение по кругу вокруг этой удалённой точки, а не вращение элемента на месте.