Supponendo che tu voglia ruotare di 90 gradi, questo è possibile, anche per elementi non di testo, ma come molte cose interessanti in CSS, richiede un po 'di astuzia. La mia soluzione inoltre richiama tecnicamente un comportamento indefinito secondo le specifiche CSS 2, quindi anche se ho testato e confermato che funziona su Chrome, Firefox, Safari e Edge, non posso prometterti che non si romperà in futuro versione del browser.
Risposta breve
Dato HTML come questo, dove vuoi ruotare .element-to-rotate
...
<div id="container">
<something class="element-to-rotate">bla bla bla</something>
</div>
... introduci due elementi wrapper attorno all'elemento che desideri ruotare:
<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>
... e quindi usa il seguente CSS, per ruotare in senso antiorario (o guarda il commento transform
per un modo per cambiarlo in una rotazione in senso orario):
.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>
Come funziona?
La confusione di fronte agli incantesimi che ho usato sopra è ragionevole; ci sono molte cose da fare e la strategia generale non è semplice e richiede una certa conoscenza delle curiosità CSS per essere capita. Vediamolo passo dopo passo.
Il nocciolo del problema che dobbiamo affrontare è che le trasformazioni applicate a un elemento utilizzando la sua transform
proprietà CSS avvengono tutte dopo che il layout ha avuto luogo. In altre parole, l'utilizzo transform
su un elemento non influisce affatto sulle dimensioni o sulla posizione del suo genitore o di qualsiasi altro elemento. Non c'è assolutamente alcun modo per cambiare questo fatto di come transform
funziona. Quindi, per creare l'effetto di rotazione di un elemento e avere l'altezza del suo genitore rispetto alla rotazione, dobbiamo fare le seguenti cose:
- In qualche modo costruisci qualche altro elemento la cui altezza sia uguale alla larghezza del file
.element-to-rotate
- Scrivi la nostra trasformazione su
.element-to-rotate
tale da sovrapporla esattamente all'elemento del passaggio 1.
L'elemento della fase 1 deve essere .rotation-wrapper-outer
. Ma come possiamo causiamo la sua altezza sia uguale a .element-to-rotate
's di larghezza ?
La componente chiave della nostra strategia qui è l' padding: 50% 0
on .rotation-wrapper-inner
. Questo exploit è un dettaglio eccentrico della specifica perpadding
: che le imbottiture percentuali, anche per padding-top
e padding-bottom
, sono sempre definite come percentuali della larghezza del contenitore dell'elemento . Questo ci consente di eseguire il seguente trucco in due fasi:
- Abbiamo impostato
display: table
su .rotation-wrapper-outer
. Ciò fa sì che abbia una larghezza di restringimento per adattarsi , il che significa che la sua larghezza verrà impostata in base alla larghezza intrinseca del suo contenuto, ovvero in base alla larghezza intrinseca di .element-to-rotate
. (Sui browser che supportano, potremmo ottenere questo risultato in modo più pulito con width: max-content
, ma a dicembre 2017 nonmax-content
è ancora supportato in Edge .)
- Impostiamo
height
of .rotation-wrapper-inner
su 0, quindi impostiamo il suo riempimento su 50% 0
(ovvero, 50% in alto e 50% in basso). Questo fa sì che occupi uno spazio verticale pari al 100% della larghezza del suo genitore - che, attraverso il trucco nel passaggio 1, è uguale alla larghezza di .element-to-rotate
.
Quindi, non resta che eseguire la rotazione e il posizionamento effettivi dell'elemento figlio. Naturalmente, transform: rotate(-90deg)
fa la rotazione; usiamo transform-origin: top left;
per far sì che la rotazione avvenga attorno all'angolo in alto a sinistra dell'elemento ruotato, il che rende più facile ragionare la successiva traslazione, poiché lascia l'elemento ruotato direttamente sopra dove sarebbe stato altrimenti disegnato. Possiamo quindi utilizzare translate(-100%)
per trascinare l'elemento verso il basso di una distanza pari alla sua larghezza di pre-rotazione.
Ciò non riesce ancora a ottenere il posizionamento corretto, perché dobbiamo ancora compensare per il riempimento superiore del 50% .rotation-wrapper-outer
. Otteniamo che garantendo che .element-to-rotate
presenta display: block
(in modo che i margini funzionerà correttamente su di esso) e quindi applicando una -50% margin-top
- nota che i margini percentuali sono anche definiti relativamente alla larghezza dell'elemento genitore.
E questo è tutto!
Perché questo non è completamente conforme alle specifiche?
A causa della seguente nota dalla definizione di imbottiture percentuali e margini nella specifica (in grassetto mio):
La percentuale è calcolata rispetto alla larghezza del blocco contenitore del box generato , anche per 'padding-top' e 'padding-bottom' . Se la larghezza del blocco contenitore dipende da questo elemento, il layout risultante non è definito in CSS 2.1.
Poiché l'intero trucco ruotava attorno al rendere il riempimento dell'elemento di wrapper interno relativo alla larghezza del suo contenitore che a sua volta dipendeva dalla larghezza del suo figlio, stiamo colpendo questa condizione e invocando un comportamento non definito. Attualmente funziona in tutti e 4 i principali browser, tuttavia, a differenza di alcune modifiche all'approccio apparentemente conformi alle specifiche che ho provato, come cambiare .rotation-wrapper-inner
per essere un fratello .element-to-rotate
invece che un genitore.