Zakładając, że chcesz obrócić o 90 stopni, jest to możliwe, nawet w przypadku elementów nietekstowych - ale podobnie jak wiele interesujących rzeczy w CSS wymaga odrobiny sprytu. Moje rozwiązanie również technicznie wywołuje niezdefiniowane zachowanie zgodnie ze specyfikacją CSS 2 - więc chociaż przetestowałem i potwierdziłem, że działa w Chrome, Firefox, Safari i Edge, nie mogę obiecać, że nie zepsuje się w przyszłości wydanie przeglądarki.
Krótka odpowiedź
Biorąc pod uwagę taki kod HTML, w którym chcesz obrócić .element-to-rotate
...
<div id="container">
<something class="element-to-rotate">bla bla bla</something>
</div>
... wprowadź dwa elementy opakowujące wokół elementu, który chcesz obrócić:
<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>
... a następnie użyj poniższego CSS, aby obrócić w lewo (lub zobacz komentarz w komentarzu, transform
aby dowiedzieć się, jak zmienić go na obrót w prawo):
.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;
}
Demo fragmentu stosu
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>
Jak to działa?
Zamieszanie w obliczu zaklęć, których użyłem powyżej, jest uzasadnione; dużo się dzieje, a ogólna strategia nie jest prosta i wymaga znajomości zagadnień CSS, aby ją zrozumieć. Przejdźmy przez to krok po kroku.
Sedno problemu polega na tym, że transformacje zastosowane do elementu za pomocą jego transform
właściwości CSS następują po utworzeniu układu. Innymi słowy, użycie transform
na elemencie w ogóle nie wpływa na rozmiar ani położenie jego rodzica ani żadnych innych elementów. Nie ma absolutnie żadnego sposobu, aby zmienić ten fakt, jak transform
działa. Tak więc, aby stworzyć efekt obracania elementu i uwzględnienia jego wysokości nadrzędnej względem obrotu, musimy wykonać następujące czynności:
- W jakiś sposób skonstruuj inny element, którego wysokość jest równa szerokości
.element-to-rotate
- Napisz naszą transformację
.element-to-rotate
tak, aby nałożyć ją dokładnie na element z kroku 1.
Elementem z kroku 1 będzie .rotation-wrapper-outer
. Ale jak możemy spowodować jego wysokość była równa .element-to-rotate
jest szerokości ?
Kluczowym elementem naszej strategii jest tu padding: 50% 0
na .rotation-wrapper-inner
. Ten exploit jest ekscentrycznym szczegółem specyfikacjipadding
: że procent wypełnienia, nawet dla padding-top
i padding-bottom
, jest zawsze definiowany jako procent szerokości kontenera elementu . Dzięki temu możemy wykonać następującą dwuetapową sztuczkę:
- Stawiamy
display: table
na .rotation-wrapper-outer
. Powoduje to, że ma szerokość zmniejszania, aby dopasować , co oznacza, że jego szerokość zostanie ustawiona na podstawie wewnętrznej szerokości jego zawartości - to znaczy na podstawie wewnętrznej szerokości .element-to-rotate
. (Na wsparcie przeglądarek, możemy osiągnąć więcej z czysto width: max-content
, ale w grudniu 2017 roku, max-content
jest nadal nie jest obsługiwana w Krawędzi ).
- Ustawiamy
height
of .rotation-wrapper-inner
na 0, a następnie ustawiamy jego dopełnienie na 50% 0
(czyli 50% na górze i 50% na dole). To powoduje, że zajmuje on w pionie przestrzeń równą 100% szerokości swojego rodzica - która, poprzez sztuczkę z kroku 1, jest równa szerokości .element-to-rotate
.
Następnie pozostaje tylko wykonać faktyczny obrót i pozycjonowanie elementu potomnego. Oczywiście, transform: rotate(-90deg)
czy rotacja; używamy transform-origin: top left;
do wywoływania obrotu wokół lewego górnego rogu obracanego elementu, co sprawia, że późniejsze tłumaczenie jest łatwiejsze do rozważenia, ponieważ pozostawia on obrócony element bezpośrednio nad miejscem, w którym zostałby narysowany. Następnie możemy translate(-100%)
przeciągnąć element w dół o odległość równą szerokości przed obróceniem.
To wciąż nie do końca zapewnia prawidłowe pozycjonowanie, ponieważ nadal musimy przesunąć 50% górnego dopełnienia .rotation-wrapper-outer
. Osiągamy to, upewniając się, że .element-to-rotate
ma display: block
(aby marginesy działały poprawnie), a następnie stosując -50% margin-top
- zwróć uwagę, że marginesy procentowe są również definiowane w stosunku do szerokości elementu nadrzędnego.
I to wszystko!
Dlaczego nie jest to w pełni zgodne ze specyfikacjami?
Ze względu na następującą uwagę z definicji procentowych wypełnień i marginesów w specyfikacji (moje pogrubienie):
Procent jest obliczany w odniesieniu do szerokości bloku zawierającego wygenerowane pole , nawet dla „padding-top” i „padding-bottom” . Jeśli szerokość zawierającego bloku zależy od tego elementu, to wynikowy układ jest niezdefiniowany w CSS 2.1.
Ponieważ cała sztuczka polegała na tym, aby wypełnienie wewnętrznego elementu opakowania było zależne od szerokości jego pojemnika, co z kolei zależało od szerokości jego elementu potomnego, trafiamy w ten warunek i wywołujemy niezdefiniowane zachowanie. Obecnie działa jednak we wszystkich 4 głównych przeglądarkach - w przeciwieństwie do niektórych pozornie zgodnych ze specyfikacją poprawek, które próbowałem, takich jak zmiana .rotation-wrapper-inner
na rodzeństwo .element-to-rotate
zamiast rodzica.