Повернутые элементы в CSS, которые правильно влияют на высоту их родителя


допустим, у меня есть несколько столбцов, из которых некоторые я хотел бы повернуть значения:

http://jsfiddle.net/MTyFP/1/

<div class="container">
    <div class="statusColumn"><span>Normal</span></div>
    <div class="statusColumn"><a>Normal</a></div>
    <div class="statusColumn"><b>Rotated</b></div>
    <div class="statusColumn"><abbr>Normal</abbr></div>
</div>

С этим CSS:

.statusColumn b {
  writing-mode: tb-rl;
  white-space: nowrap;
  display: inline-block;
  overflow: visible;
  transform: rotate(-90deg);
  transform-origin: 50% 50%;
}

это в конечном итоге выглядит так:

можно ли написать любой CSS, который заставит повернутый элемент влиять на высоту его родителя, так что текст не будет перекрывать другие элементы? Что-то вроде этого:

7 75

7 ответов:

предполагая, что вы хотите повернуть на 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;
}

Stack snippet demo

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 trivia, чтобы понять. Давайте пройдем через это шаг за шагом.

суть проблемы, с которой мы сталкиваемся, заключается в том, что преобразования применяются к элементу с использованием его transform свойство CSS все происходит после того, как макет состоялся. Другими словами, используя transform на элемент не влияет на размер или положение его родитель или любые другие элементы на всех. Нет абсолютно никакого способа изменить этот факт как transform строительство. Таким образом, чтобы создать эффект вращения элемента и иметь высоту его родителя относительно вращения, нам нужно сделать следующие вещи:

  1. как построить какой-нибудь другое элемент, высота которого равна ширине .element-to-rotate
  2. напишите наше преобразование на .element-to-rotate например, чтобы наложить его точно на элемент из Шаг 1.

элемент из Шага 1 должен быть .rotation-wrapper-outer. Но как мы можем вызвать его высота будет равна .element-to-rotate ' s ширина?

ключевым компонентом нашей стратегии здесь является padding: 50% 0 on .rotation-wrapper-inner. Этот подвиг эксцентричная деталь спецификации для padding: этот процент отступов, даже для padding-top и padding-bottom, всегда определяются как процент от ширина элемента контейнер. Это позволяет нам выполнить следующий двухэтапный трюк:

  1. мы display: table on .rotation-wrapper-outer. Это заставляет его иметь сокращение-к-пригонки ширины, что означает, что его ширина будет установлена на основе внутренней ширины его содержимого - то есть на основе внутренней ширины .element-to-rotate. (При поддержке браузеров, мы могли бы достичь этого более чисто с width: max-content, но по состоянию на декабрь 2017, max-content и по-прежнему не поддерживается Край.)
  2. ставим height на .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.

так как весь трюк вращался вокруг того, чтобы обивка внутреннего элемента обертки была относительно ширины его контейнера что, в свою очередь, зависело от ширины его дочернего элемента, мы сталкиваемся с этим условием и вызываем неопределенное поведение. В настоящее время он работает во всех 4 основных браузерах, хотя - в отличие от некоторых, казалось бы, spec-совместимых настроек, чтобы приблизиться к тому, что я пробовал, например, изменить .rotation-wrapper-inner быть родным братом .element-to-rotate вместо родительского.

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

представьте, что он вращается менее чем на 90° (например,transform: rotate(45deg)): вы должны были бы ввести такую невидимую коробку, которая теперь имеет неоднозначные размеры, основанные на исходных размерах объекта, который вы вращаете и фактическое значение поворота.

Rotated object

вдруг у вас не только width и height объекта, который вы повернули, но у вас также есть width и height из "невидимой коробки" вокруг него. Представьте себе запрос внешней ширины этого объекта - что бы он вернул? Ширина объекта или наша новая коробка? Как бы мы различали и то и другое?

поэтому нет CSS, который вы можете написать, чтобы исправить это поведение (или я должен сказать, "автоматизировать" его). Конечно, вы можете увеличить размер родительского контейнера вручную или написать некоторый JavaScript для обработки этого.

(просто чтобы быть ясно, вы можете попробовать использовать element.getBoundingClientRect чтобы получить прямоугольник упоминал ранее).

как описано в спецификации:

в пространстве имен HTML свойство transform не влияет на поток содержимого, окружающего преобразованный элемент.

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

единственное, что учитывается при преобразовании объекта-это область переполнения:

(...) экстент области переполнения учитывает преобразованные элементы. Это поведение аналогично что происходит, когда элементы смещения через относительная позиционирование.

смотрите этот jsfiddle, чтобы узнать больше.

на самом деле неплохо сравнить эту ситуацию со смещением объекта с помощью: position: relative - окружающий контент не меняется, даже если вы перемещаете свой объект вокруг (пример).


если вы хотите справиться с этим с помощью JavaScript, посмотри этой вопрос.

использовать проценты для padding и псевдо-элемент для передачи контента. В JSFiddle я оставил псевдо-элемент красным, чтобы показать его, и вам придется компенсировать сдвиг текста, но я думаю, что это путь.

.statusColumn {
    position: relative;
    border: 1px solid #ccc;
    padding: 2px;
    margin: 2px;
    width: 200px;
}

.statusColumn i, .statusColumn b, .statusColumn em, .statusColumn strong {
  writing-mode: tb-rl;
  white-space: nowrap;
  display: inline-block;
  overflow: visible;
  -webkit-transform: rotate(-90deg);
  -moz-transform: rotate(-90deg);
  -ms-transform: rotate(-90deg);
  -o-transform: rotate(-90deg);
  transform: rotate(-90deg);
  -webkit-transform: rotate(-90deg);

  /* also accepts left, right, top, bottom coordinates; not required, but a good idea for styling */
  -webkit-transform-origin: 50% 50%;
  -moz-transform-origin: 50% 50%;
  -ms-transform-origin: 50% 50%;
  -o-transform-origin: 50% 50%;
  transform-origin: 50% 50%;

  /* Should be unset in IE9+ I think. */
  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
}
.statusColumn b:before{ content:''; padding:50% 0; display:block;  background:red; position:relative; top:20px
}
<div class="container">
    <div class="statusColumn"><span>Normal</span></div>
    <div class="statusColumn"><a>Normal</a></div>
    <div class="statusColumn"><b>Rotated</b></div>
    <div class="statusColumn"><abbr>Normal</abbr></div>
</div>

http://jsfiddle.net/MTyFP/7/

запись этого решения можно найти здесь:http://kizu.ru/en/fun/rotated-text/

пожалуйста, смотрите обновленную версию в мой другой ответ. Этот ответ больше не действителен и просто больше не работает. Я оставлю его здесь по историческим причинам.


этот вопрос довольно старый, но с текущей поддержкой .getBoundingClientRect()

как комментарий от G-Cyr справедливо указывает, текущая поддержка writing-mode более чем прилично. В сочетании с простым вращением, вы получите точный результат, который вы хотите. См. пример ниже.

.statusColumn {
  position: relative;
  border: 1px solid #ccc;
  padding: 2px;
  margin: 2px;
  width: 200px;
}

.statusColumn i,
.statusColumn b,
.statusColumn em,
.statusColumn strong {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  white-space: nowrap;
  display: inline-block;
  overflow: visible;
}
<div class="container">
  <div class="statusColumn"><span>Normal</span></div>
  <div class="statusColumn"><a>Normal</a></div>
  <div class="statusColumn"><b>Rotated</b></div>
  <div class="statusColumn"><abbr>Normal</abbr></div>
</div>

свойство режима записи сделает это. текущая поддержка writing-mode более чем прилично. В сочетании с простым вращением, вы получите точный результат, который вы хотите. См. пример ниже.

.statusColumn {
  position: relative;
  border: 1px solid #ccc;
  padding: 2px;
  margin: 2px;
  width: 200px;
}

.statusColumn i,
.statusColumn b,
.statusColumn em,
.statusColumn strong {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  white-space: nowrap;
  display: inline-block;
  overflow: visible;
}
<div class="container">
  <div class="statusColumn"><span>Normal</span></div>
  <div class="statusColumn"><a>Normal</a></div>
  <div class="statusColumn"><b>Rotated</b></div>
  <div class="statusColumn"><abbr>Normal</abbr></div>
</div>

(разделить от ответа на Брэм Vanroy)

Я знаю, что это старый пост, но я нашел его, борясь с точно такой же проблемой. Решение, которое работает для меня, является довольно грубым" низкотехнологичным " методом, просто окружающим div, который я вращаю на 90deg с большим количеством

<br>

зная приблизительную ширину (которая становится высотой после вращения) div, я могу компенсировать разницу, добавив br вокруг этого div, поэтому содержимое выше и ниже соответственно подталкивается.