Предполагая, что вы хотите повернуть на 90 градусов, это возможно даже для нетекстовых элементов, но, как и многие другие интересные вещи в CSS, это требует некоторой хитрости. Мое решение также технически вызывает неопределенное поведение в соответствии со спецификацией CSS 2 - поэтому, хотя я протестировал и подтвердил, что оно работает в Chrome, Firefox, Safari и Edge, я не могу обещать вам, что оно не сломается в будущем. выпуск браузера.
Короткий ответ
Учитывая такой HTML, где вы хотите повернуть .element-to-rotate
...
<div id="container">
<something class="element-to-rotate">bla bla bla</something>
</div>
... введите два элемента-оболочки вокруг элемента, который вы хотите повернуть:
<div id="container">
<div class="rotation-wrapper-outer">
<div class="rotation-wrapper-inner">
<something class="element-to-rotate">bla bla bla</something>
</div>
</div>
</div>
... а затем используйте следующий CSS для вращения против часовой стрелки (или посмотрите закомментированный transform
способ изменить его на вращение по часовой стрелке):
.rotation-wrapper-outer {
display: table;
}
.rotation-wrapper-inner {
padding: 50% 0;
height: 0;
}
.element-to-rotate {
display: block;
transform-origin: top left;
/* Note: for a CLOCKWISE rotation, use the commented-out
transform instead of this one. */
transform: rotate(-90deg) translate(-100%);
/* transform: rotate(90deg) translate(0, -100%); */
margin-top: -50%;
/* Not vital, but possibly a good idea if the element you're rotating contains
text and you want a single long vertical line of text and the pre-rotation
width of your element is small enough that the text wraps: */
white-space: nowrap;
}
Демонстрация фрагмента стека
p {
/* Tweak the visuals of the paragraphs for easier visualiation: */
background: pink;
margin: 1px 0;
border: 1px solid black;
}
.rotation-wrapper-outer {
display: table;
}
.rotation-wrapper-inner {
padding: 50% 0;
height: 0;
}
.element-to-rotate {
display: block;
transform-origin: top left;
/* Note: for a CLOCKWISE rotation, use the commented-out
transform instead of this one. */
transform: rotate(-90deg) translate(-100%);
/* transform: rotate(90deg) translate(0, -100%); */
margin-top: -50%;
/* Not vital, but possibly a good idea if the element you're rotating contains
text and you want a single long vertical line of text and the pre-rotation
width of your element is small enough that the text wraps: */
white-space: nowrap;
}
<div id="container">
<p>Some text</p>
<p>More text</p>
<div class="rotation-wrapper-outer">
<div class="rotation-wrapper-inner">
<p class="element-to-rotate">Some rotated text</p>
</div>
</div>
<p>Even more text</p>
<img src="https://i.stack.imgur.com/ih8Fj.png">
<div class="rotation-wrapper-outer">
<div class="rotation-wrapper-inner">
<img class="element-to-rotate" src="https://i.stack.imgur.com/ih8Fj.png">
</div>
</div>
<img src="https://i.stack.imgur.com/ih8Fj.png">
</div>
Как это работает?
Замешательство перед заклинаниями, которые я использовал выше, разумно; там много всего происходит, и общая стратегия непростая и требует некоторых знаний в мелочах CSS для понимания. Давайте рассмотрим это шаг за шагом.
Суть проблемы, с которой мы сталкиваемся, заключается в том, что все преобразования, применяемые к элементу с использованием его transform
свойства CSS, происходят после того, как произошел макет. Другими словами, использование transform
элемента не влияет на размер или положение его родительского элемента или любых других элементов. Нет абсолютно никакого способа изменить этот факт transform
работы. Таким образом, чтобы создать эффект вращения элемента и чтобы высота его родительского элемента соответствовала повороту, нам необходимо сделать следующее:
- Как-нибудь построить какой-нибудь другой элемент, высота которого равна ширине
.element-to-rotate
- Напишите наше преобразование
.element-to-rotate
так, чтобы оно было точно наложено на элемент из шага 1.
Элемент из шага 1 должен быть .rotation-wrapper-outer
. Но как мы можем вызвать его высота будет равна .element-to-rotate
«s ширина ?
Ключевым компонентом нашей стратегии здесь является padding: 50% 0
включение .rotation-wrapper-inner
. Этот эксплойт представляет собой эксцентричную деталь спецификацииpadding
: процентные отступы, даже для padding-top
и padding-bottom
, всегда определяются как проценты от ширины контейнера элемента . Это позволяет нам выполнить следующий двухэтапный трюк:
- Мы ставим
display: table
на .rotation-wrapper-outer
. Это приводит к тому, что он имеет ширину , уменьшающуюся до размера , что означает, что его ширина будет установлена на основе внутренней ширины его содержимого, то есть на основе внутренней ширины .element-to-rotate
. (Что касается поддержки браузеров, мы могли бы добиться этого более четко width: max-content
, но по состоянию на декабрь 2017 года max-content
он все еще не поддерживается в Edge .)
- Мы устанавливаем
height
of .rotation-wrapper-inner
равным 0, а затем устанавливаем его отступы 50% 0
(то есть 50% сверху и 50% снизу). Это приводит к тому, что он занимает вертикальное пространство, равное 100% ширины его родительского элемента, который, с помощью уловки на шаге 1, равен ширине .element-to-rotate
.
Далее все, что остается, - это выполнить фактическое вращение и позиционирование дочернего элемента. Естественно, transform: rotate(-90deg)
делает вращение; мы используем, transform-origin: top left;
чтобы заставить вращение происходить вокруг верхнего левого угла повернутого элемента, что облегчает последующий перевод, поскольку он оставляет повернутый элемент прямо над тем местом, где он в противном случае был бы нарисован. Затем мы можем использовать, translate(-100%)
чтобы перетащить элемент вниз на расстояние, равное его ширине до поворота.
Это все еще не совсем правильно позиционирует, потому что нам все еще нужно смещение для 50% верхнего отступа .rotation-wrapper-outer
. Мы достигаем этого, гарантируя, что он .element-to-rotate
имеет display: block
(чтобы поля правильно работали на нем), а затем применяя -50% margin-top
- обратите внимание, что процентные поля также определяются относительно ширины родительского элемента.
И это все!
Почему это не полностью соответствует спецификации?
Из-за следующего примечания из определения процентных отступов и полей в спецификации (выделено жирным шрифтом):
Процент рассчитывается относительно ширины содержащего блок сгенерированного блока , даже для 'padding-top' и 'padding-bottom' . Если ширина содержащего блока зависит от этого элемента, то результирующий макет не определен в CSS 2.1.
Поскольку весь трюк сводился к тому, чтобы заполнение внутреннего элемента оболочки было относительно ширины его контейнера, который, в свою очередь, зависел от ширины его дочернего элемента, мы выполняем это условие и вызываем поведение undefined. Однако в настоящее время он работает во всех 4 основных браузерах - в отличие от некоторых, казалось бы, совместимых со спецификациями настроек подхода, которые я пробовал, таких как изменение .rotation-wrapper-inner
на родство, .element-to-rotate
а не на родительский.