Обернені елементи в CSS, які правильно впливають на зріст батьків


119

Скажімо, у мене є кілька стовпців, з яких я хотів би повернути значення:

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, який призведе до того, що повернутий елемент вплине на висоту батьків, щоб текст не перекривав інші елементи? Щось на зразок цього:

Текст третього стовпця тепер впливає на висоту його батьківського поля таким чином, що текст вписується в поле.


1
може бути корисним stackoverflow.com/questions/7565542/…
Піт

7
режим письма тепер доступний для більшості браузерів (раніше це функція IE5). Я б робив сьогодні jsfiddle.net/MTyFP/698, див .: developer.mozilla.org/en-US/docs/Web/CSS/writing-mode
G-Cyrillus

1
@ G-Cyr writing-modeце доступно для більшості браузерів, але жахливо глючить. Над пов’язаним питанням, я переглянув кілька ітерацій, намагаючись вирішити проблеми, пов’язані з браузером, перед тим, як відмовитися; ви можете побачити мою послідовність відмов на stackoverflow.com/posts/47857248/reitions , де я починаю щось із Chrome, але не Firefox чи Edge, а потім перериваю Chrome у процесі виправлення двох інших і закінчую повернення мого відповісти та позначити його лише Chrome. Будьте обережні, якщо хочете піти цією стежкою. (Також зауважте, що це не має користі з нетекстовими елементами.)
Марк Амерді

1
Режим письма
Фредрік Йоханссон

Відповіді:


51

Якщо припустити, що ви хочете повернути на 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;
}

Демонстраційний фрагмент стека

Як це працює?

Плутанина перед закликами, які я використав вище, є розумною; багато чого відбувається, і загальна стратегія не є однозначною і вимагає певних знань про дрібниці CSS, щоб зрозуміти. Пройдемося покроково.

Основною проблемою, з якою ми стикаємося, є те, що перетворення, застосовані до елемента, використовуючи його transformвластивість CSS, відбуваються після того, як відбулася компонування. Іншими словами, використання transformелемента не впливає на розмір чи положення його батьківського чи будь-якого іншого елемента. Існує абсолютно ніякий спосіб змінити цей факт того, як transformпрацює. Таким чином, для того, щоб створити ефект обертання елемента і мати висоту його батьків щодо повороту, нам потрібно зробити наступні дії:

  1. Якось побудуйте якийсь інший елемент, висота якого дорівнює ширині.element-to-rotate
  2. Напишіть наше перетворення .element-to-rotateтаким чином, щоб накласти його точно на елемент із кроку 1.

Елемент кроку 1 повинен бути .rotation-wrapper-outer. Але як ми можемо змусити його висоту дорівнювати ширині.element-to-rotate ' ?

Ключовим компонентом нашої стратегії тут є padding: 50% 0включення .rotation-wrapper-inner. Цей експлуатувальний ексцентричний опис специфікації дляpadding : цей відсоток прокладок, навіть для padding-topі padding-bottomзавжди визначається як відсотки від ширини контейнера елемента . Це дає нам змогу виконати наступний двоетапний трюк:

  1. Ми ставимо display: tableна .rotation-wrapper-outer. Це призводить до того, що вона має ширину , що відповідає розміру , що означає, що її ширина буде встановлена ​​на основі внутрішньої ширини її вмісту - тобто на основі внутрішньої ширини .element-to-rotate. (На підтримку браузерів, ми могли б домогтися цього більш чисто з width: max-content, але за станом на грудень 2017 року, max-contentце ще не підтримуються в краї .)
  2. Ми встановили heightна .rotation-wrapper-inner0, а потім встановити його заповнення до 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.

Оскільки вся хитрість оберталася навколо того, щоб набивання внутрішнього елемента обгортки було відносно ширини його контейнера, що в свою чергу залежало від ширини його дочірньої частини, ми вражаємо цю умову і посилаємось на невизначене поведінку. Наразі він працює у всіх чотирьох основних браузерах, однак - на відміну від деяких, здавалося б, специфічних налаштувань наближення до того, що я намагався, як, наприклад, змінити, .rotation-wrapper-innerщоб бути рідним братом, .element-to-rotateа не батьком.


1
На даний момент я прийняв це як відповідь. Ці writing-modeрішення будуть найкращий підхід , якщо IE 11 не потребує підтримки.
Кріс

1
Помітний улов: Не працює для інших обертів, таких як 45 градусів.
Е. Віллігер

хоча це прийнято, це не є актуальною правильною відповіддю. Див. Нижче відповіді в режимі письма.
GilShalit

@ mark-amery, мені довелося коригувати CSS для мого коду: imgur.com/a/ZgafkgE 1-е зображення з вашим CSS, 2-е з коригуваннями я видалив transformOrigin: 'верхній лівий', і змінив перетворення: 'translate (-100% ) ', щоб перетворити:' перекласти (20%) ', <div style = {{flexDirection:' стовпець ', alignItems:' center ',}}> <div style = {{display:' table ', запас: 30 ,}}> <div style = {{padding: '50% 0 ', висота: 0}}> <img src = {image} style = {{display:' block ', marginTop:' -50% ', перетворити : 'rotate (-90deg) translate (20%)', maxWidth: 300, maxHeight: 400,}} /> </div> </div> <div> {title} </div> </div>
Dan Гірко

44

Як справедливо зазначає коментар 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>


1
Радий, що я прокрутився вниз - це врятувало мене від болю, використовуючи обертання та переклади.
Джеймс Маклафлін

3
Щоб отримати ротацію, про яку йдеться у запитанні, використовуйтеwriting-mode: vertical-lr; transform: rotate(180deg);
Джеймс Маклафлін

41

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

Уявіть, що обертаєте його менше ніж на 90 ° (наприклад transform: rotate(45deg)): вам доведеться ввести таке невидиме поле, яке тепер має неоднозначні розміри, виходячи з оригінальних розмірів об'єкта, який ви обертаєте, та фактичного значення обертання.

Обертається об'єкт

Раптом у вас не тільки обертається widthі heightоб'єкт, який ви обернули, але ви також маєте widthі height"невидимий ящик" навколо нього. Уявіть, що вимагаєте зовнішньої ширини цього об’єкта - що б він повернувся? Ширина предмета, або наш новий ящик? Як би ми розрізняли обидва?

Тому немає CSS, який ви можете написати, щоб виправити таку поведінку (або я повинен сказати, "автоматизувати" її). Звичайно, ви можете збільшити розмір батьківського контейнера вручну або написати трохи JavaScript, щоб це впоратися.

(Просто для того, щоб було зрозуміло, ви можете спробувати використовувати, element.getBoundingClientRectщоб отримати прямокутник, згаданий раніше).

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

У просторі імен HTML властивість перетворення не впливає на потік вмісту, що оточує трансформований елемент.

Це означає, що зміст навколо об'єкта, який ви трансформуєте, не буде змінено, якщо ви не зробите їх вручну.

Єдине, що враховується при перетворенні об'єкта - це область переповнення:

(...) розмір площі переливу враховує трансформовані елементи. Така поведінка схожа на те, що відбувається, коли елементи зміщуються через відносне розташування .

Дивіться цю jsfiddle, щоб дізнатися більше.

Насправді досить добре порівнювати цю ситуацію із зміщенням об'єкта, використовуючи: position: relative- оточуючий вміст не змінюється, навіть якщо ви переміщуєте об'єкт ( приклад ).


Якщо ви хочете вирішити це за допомогою JavaScript, погляньте на це питання.


8
Я б сказав, що в нормальних рамках інтерфейсу користувача ширина і висота ваших батьків повинні бути обгорнуті навколо ширини і висоти обмежувальної коробки дитини, на яку впливає, як ви згадували, обертання дитини. Ширина і висота дітей - це їх ширина і висота перед обертанням, а ширина і висота батьків визначаються коробкою дітей. Це дуже принципово, і я б сказав, що CSS / HTML повинен працювати так ... Якби ніхто не ставив запитання на цю тему, які можна виправити лише в незадовільних манерах хакерів.
користувач18490

25

Використовуйте відсотки для 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/uk/fun/rotated-text/


3
Проблема з цим рішенням полягає в тому, що елемент зберігає свою початкову ширину, тому в (наприклад) таблиці, якби це була найширша комірка, вона виштовхнула б цей стовпець у ширину із зайвим пробілом, який би зайнявся, якби текст мав не обертається. Це дратує.
Jez

@litek, Чи можете ви подивіться на jsfiddle.net/MTyFP/7 ? У мене майже однакова проблема із зображенням. Питання: stackoverflow.com/questions/31072959 / ...
Awlad Liton

11

Будь ласка, дивіться оновлену версію в іншій моїй відповіді . Ця відповідь більше не є дійсною і просто більше не працює. Я залишу його тут з історичних причин.


Це питання досить старий, але з поточної підтримкою .getBoundingClientRect()об'єкта і його widthі heightцінність, в поєднанні з можливістю використовувати цей акуратний метод разом з transform, я думаю , що моє рішення слід згадати також.

Дивіться це в дії тут . (Випробувано в Chrome 42, FF 33 і 37.)

getBoundingClientRectобчислює фактичну ширину коробки та висоту елемента. Простіше просто, ми можемо провести крізь усі елементи і встановити його мінімальну висоту на фактичну висоту коробки їхніх дітей.

$(".statusColumn").each(function() {
    var $this = $(this),
        child = $this.children(":first");
    $this.css("minHeight", function() {
        return child[0].getBoundingClientRect().height;
    });
});

(З деякою модифікацією ви можете провести цикл через дітей і дізнатись, хто з них найвищий, і встановити висоту батьків для найвищої дитини. Однак, я вирішив зробити приклад більш простим. Ви також можете додати прокладки з батьків до висота, якщо ви хочете, або ви можете використовувати відповідне box-sizingзначення у CSS.)

Зауважте, що я додав a translateі transform-originваш CSS, щоб зробити позиціонування більш гнучким і точним.

transform: rotate(-90deg) translateX(-100%);
transform-origin: top left;

Дійсно? Css запитання з відповіддю JQUERY, і ви називаєте це ідеальною відповіддю @MichaelLumbroso? Це працює так, але немає необхідності в JavaScript або цілій бібліотеці js, як jQuery
Nebulosar

1
@Nebulosar Приємне копання. У той час (вже два з половиною роки тому!) Це була єдина відповідь, яка не викликала проблем із позиціонуванням чи псевдоелементами, які не спрацювали. На щастя, підтримка браузера для деяких властивостей покращилася. Будь ласка, дивіться мою редакцію.
Брам Ванрой

2
Це по суті два абсолютно не пов'язаних відповіді в одному. Ідеально в рамках правил розміщення декількох відповідей на одне питання; Я думаю, що було б краще, якби ваше нове рішення, яке було додано у 2017 році, було новою відповіддю, щоб обидва цілком окремі відповіді можна було проголосувати та коментувати окремо.
Марк Амері

@MarkAmery Ви праві. Я редагував питання і додав новий відповідь тут .
Брам Ванрой

Я зміг використати цю відповідь та подію завантаження зображення, щоб отримати робоче рішення для зображень за допомогою: $ pic.on ('load', function () {$ pic.css ('min-height', $ pic [0] .getBoundingClientRect (). висота);});
Мійка Л.

-18

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

<br>

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


10
Можливо, це працює, але ти ніколи цього не повинен робити.
МММ

Дякуємо за ваш відгук! Так, кращим способом я використовую властивість div-margin-top для регулювання висоти в пікселях при обертанні об'єкта.
vic_sk
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.