En supposant que vous souhaitiez faire pivoter de 90 degrés, cela est possible, même pour les éléments non textuels - mais comme beaucoup de choses intéressantes dans CSS, cela nécessite un peu de ruse. Ma solution invoque également techniquement un comportement indéfini selon la spécification CSS 2 - donc, bien que j'aie testé et confirmé qu'elle fonctionne dans Chrome, Firefox, Safari et Edge, je ne peux pas vous promettre qu'elle ne se cassera pas à l'avenir. version du navigateur.
Réponse courte
Compte tenu du HTML comme celui-ci, où vous voulez faire pivoter .element-to-rotate
...
<div id="container">
<something class="element-to-rotate">bla bla bla</something>
</div>
... introduisez deux éléments wrapper autour de l'élément que vous souhaitez faire pivoter:
<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>
... puis utilisez le CSS suivant, pour tourner dans le sens anti-horaire (ou voir le commenté transform
pour un moyen de le changer en une rotation dans le sens horaire):
.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;
}
Démo d'extraits de pile
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>
Comment cela marche-t-il?
La confusion face aux incantations que j'ai utilisées ci-dessus est raisonnable; il se passe beaucoup de choses, et la stratégie globale n'est pas simple et nécessite une certaine connaissance des anecdotes CSS pour la comprendre. Passons en revue étape par étape.
Le cœur du problème auquel nous sommes confrontés est que les transformations appliquées à un élément à l'aide de sa transform
propriété CSS se produisent toutes après la mise en page. En d'autres termes, l'utilisation transform
sur un élément n'affecte pas du tout la taille ou la position de son parent ou de tout autre élément. Il n'y a absolument aucun moyen de changer ce fait de comment transform
fonctionne. Ainsi, pour créer l'effet de rotation d'un élément et que la hauteur de son parent respecte la rotation, nous devons faire les choses suivantes:
- Construisez en quelque sorte un autre élément dont la hauteur est égale à la largeur du
.element-to-rotate
- Écrivez notre transformation sur de
.element-to-rotate
manière à la superposer exactement sur l'élément de l'étape 1.
L'élément de l'étape 1 doit être .rotation-wrapper-outer
. Mais comment faire en sorte que sa hauteur soit égale à .element-to-rotate
la largeur de s ?
L'élément clé de notre stratégie ici est le padding: 50% 0
sur .rotation-wrapper-inner
. Cet exploit est un détail excentrique de la spécification pourpadding
: que les pourcentages de rembourrage, même pour padding-top
et padding-bottom
, sont toujours définis comme des pourcentages de la largeur du conteneur de l'élément . Cela nous permet d'effectuer l'astuce en deux étapes suivante:
- Nous avons mis
display: table
sur .rotation-wrapper-outer
. Cela lui donne une largeur de rétrécissement , ce qui signifie que sa largeur sera définie en fonction de la largeur intrinsèque de son contenu, c'est-à-dire en fonction de la largeur intrinsèque de .element-to-rotate
. (Sur les navigateurs pris en charge, nous pourrions y parvenir plus proprement avec width: max-content
, mais à partir de décembre 2017, ce max-content
n'est toujours pas pris en charge dans Edge .)
- Nous définissons le
height
of .rotation-wrapper-inner
sur 0, puis définissons son remplissage sur 50% 0
(c'est-à-dire 50% haut et 50% bas). Cela l'amène à prendre un espace vertical égal à 100% de la largeur de son parent - ce qui, grâce à l'astuce de l'étape 1, est égal à la largeur de .element-to-rotate
.
Ensuite, il ne reste plus qu'à effectuer la rotation et le positionnement réels de l'élément enfant. Naturellement, transform: rotate(-90deg)
fait la rotation; nous utilisons transform-origin: top left;
pour faire en sorte que la rotation se produise autour du coin supérieur gauche de l'élément pivoté, ce qui rend la translation ultérieure plus facile à raisonner, car elle laisse l'élément pivoté directement au-dessus de l'endroit où il aurait été dessiné autrement. On peut ensuite utiliser translate(-100%)
pour faire glisser l'élément vers le bas d'une distance égale à sa largeur de pré-rotation.
Cela n'obtient toujours pas le bon positionnement, car nous devons toujours compenser le rembourrage supérieur de 50% .rotation-wrapper-outer
. Nous y parvenons en nous assurant que .element-to-rotate
has display: block
(pour que les marges fonctionnent correctement dessus), puis en appliquant un -50% margin-top
- notez que les marges en pourcentage sont également définies par rapport à la largeur de l'élément parent.
Et c'est tout!
Pourquoi n'est-ce pas entièrement conforme aux spécifications?
En raison de la note suivante tirée de la définition des pourcentages de rembourrage et des marges dans la spécification (mine en gras):
Le pourcentage est calculé par rapport à la largeur du bloc contenant la boîte générée , même pour «padding-top» et «padding-bottom» . Si la largeur du bloc conteneur dépend de cet élément, la mise en page résultante n'est pas définie dans CSS 2.1.
Étant donné que toute l'astuce tournait autour de rendre le remplissage de l'élément enveloppe interne par rapport à la largeur de son conteneur qui dépendait à son tour de la largeur de son enfant, nous remplissons cette condition et invoquons un comportement non défini. Cependant, cela fonctionne actuellement dans les 4 principaux navigateurs, contrairement à certains ajustements apparemment conformes aux spécifications que j'ai essayés, comme changer .rotation-wrapper-inner
pour être un frère .element-to-rotate
plutôt qu'un parent.