Abschnitt 13 · Lektion 2
transform
Ein größerer Innenabstand schiebt die Nachbarn weg, ein Außenabstand ebenso. transform tut das nicht. Es nimmt das fertig ausgerechnete Element und zeichnet es woanders hin. Sein Platz im Fluss bleibt dabei, wo er war.
Die vier Funktionen
<div class="reihe">
<div class="platz"><span class="kasten verschoben">translate</span></div>
<div class="platz"><span class="kasten gedreht">rotate</span></div>
<div class="platz"><span class="kasten skaliert">scale</span></div>
<div class="platz"><span class="kasten geneigt">skew</span></div>
</div>
<p class="hinweis">
Der gestrichelte Rahmen ist der Platz, den das Element im
Layout belegt. Er bleibt, wo er ist, egal was mit dem
blauen Kasten darin passiert.
</p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
line-height: 1.5;
}
.reihe {
display: flex;
gap: 24px;
}
.platz {
border: 1px dashed #a1a1aa;
}
.kasten {
display: block;
width: 96px;
padding: 16px 8px;
text-align: center;
font-size: 13px;
color: #ffffff;
background-color: #2563eb;
}
.verschoben {
transform: translate(12px, -12px);
}
.gedreht {
transform: rotate(15deg);
}
.skaliert {
transform: scale(1.2);
}
.geneigt {
transform: skewX(-12deg);
}
.hinweis {
max-width: 60ch;
margin-top: 32px;
color: #52525b;
} translate(x, y) verschiebt, rotate() dreht, scale() vergrößert oder verkleinert, skew() neigt. Zu jeder gibt es Varianten für eine Achse, also translateX(), scaleY() und so weiter.
Dazu zwei Kleinigkeiten. Bei translate() beziehen sich Prozentangaben auf das Element selbst und nicht auf den Elternteil. Deshalb funktioniert translateX(-50%) als Zentrierhilfe.
Bei scale() wird alles mitskaliert, auch Schrift, Rahmen und Schatten. Schrift und Vektorformen zeichnet der Browser bei einem festen Wert in voller Auflösung neu, die bleiben also scharf. Ein Bild im Element wird unscharf, sobald es über seine eigene Auflösung hinaus wächst. Während einer laufenden Animation kann sogar alles unscharf werden, denn dort streckt der Browser oft eine fertig gezeichnete Ebene, statt bei jedem Bild neu zu zeichnen.
Um den gestrichelten Rahmen im Beispiel geht es eigentlich. Der Platz bleibt, wo er war, und der blaue Kasten hängt darüber wie ein Aufkleber. Eine Vergrößerung schiebt deshalb auch nichts zur Seite. Sie kann Nachbarn überdecken.
Das lässt sich nachmessen. Bei den vier blauen Kästen steht oben im Streifen jedes Mal etwas anderes, rund 96 × 52 beim verschobenen, rund 106 × 75 beim gedrehten, rund 115 × 62 beim skalierten. In der Zeile width darunter steht bei allen vieren 96px, und der gestrichelte Platz darum misst durchgehend rund 98 × 54. Oben steht, was du siehst, und unten, was im Layout gilt. transform ändert nur das Obere.
Eine Zeile transform steht dort auch, aber nie so wie im Stylesheet. Aus translate(12px, -12px) wird matrix(1, 0, 0, 1, 12, -12), aus rotate(15deg) wird matrix(0.97, 0.26, -0.26, 0.97, 0, 0). So rechnet der Browser jede Kette von Funktionen zusammen. Die beiden letzten Zahlen sind die Verschiebung, davor steht der Rest.
Die Reihenfolge ist nicht egal
<p class="bahn"><span class="kasten erst-drehen">erst drehen, dann schieben</span></p>
<p class="bahn"><span class="kasten erst-schieben">erst schieben, dann drehen</span></p>
<p class="hinweis">
Beide Kästen tragen dieselben zwei Funktionen mit denselben
Werten, nur in anderer Reihenfolge, und landen an
verschiedenen Stellen. Der Grund: gedreht wird immer das
Koordinatensystem gleich mit.
</p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
line-height: 1.5;
}
.bahn {
margin: 0 0 48px 0;
border-top: 1px dashed #a1a1aa;
}
.kasten {
display: inline-block;
padding: 8px 16px;
font-size: 13px;
color: #ffffff;
background-color: #2563eb;
}
.erst-drehen {
transform: rotate(20deg) translateX(120px);
}
.erst-schieben {
transform: translateX(120px) rotate(20deg);
}
.hinweis {
max-width: 60ch;
color: #52525b;
} Mehrere Funktionen schreibst du hintereinander in einen Wert, getrennt durch Leerzeichen. Der Browser wendet sie von links nach rechts an, und jede arbeitet in dem Koordinatensystem, das die vorherige hinterlassen hat.
Wer erst dreht und dann schiebt, schiebt also in die gedrehte Richtung. Wer erst schiebt und dann dreht, schiebt nach rechts und dreht dann an Ort und Stelle. Dieselben zwei Angaben, zwei verschiedene Ergebnisse. Wenn das Ergebnis nicht stimmt, ist die Reihenfolge die erste Stelle, an der ich nachsehe.
Der Drehpunkt
<div class="reihe">
<div class="platz"><span class="kasten mitte">Mitte</span></div>
<div class="platz"><span class="kasten oben-links">oben links</span></div>
<div class="platz"><span class="kasten unten">unten</span></div>
</div>
<p class="hinweis">
Alle drei sind um dieselben 25 Grad gedreht. Verschieden
ist nur der Punkt, um den sie sich drehen.
</p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 48px 24px;
font-family: system-ui, sans-serif;
line-height: 1.5;
}
.reihe {
display: flex;
gap: 48px;
}
.platz {
border: 1px dashed #a1a1aa;
}
.kasten {
display: block;
width: 110px;
padding: 16px 8px;
text-align: center;
font-size: 13px;
color: #ffffff;
background-color: #2563eb;
rotate: 25deg;
}
.mitte {
transform-origin: center;
}
.oben-links {
transform-origin: top left;
}
.unten {
transform-origin: bottom center;
}
.hinweis {
max-width: 60ch;
margin-top: 48px;
color: #52525b;
} Von allein dreht und skaliert alles um die Mitte des Elements. transform-origin verschiebt diesen Punkt, entweder mit Schlüsselwörtern wie top left, mit Prozentangaben oder mit Längen. Bei einem Menü, das aus einer Ecke aufklappt, macht diese Eigenschaft den Unterschied zwischen “erscheint” und “wächst aus dem Button heraus”.
Im Streifen findest du hier ausnahmsweise nichts, denn transform-origin steht nicht darin. Die drei Kästen des Beispiels messen sogar gleich, rund 121 × 93 und rotate: 25deg an jedem. Verschieden ist nur, wo sie liegen, und die Lage steht nicht im Streifen. Dafür gibt es die gestrichelten Rahmen im Beispiel.
Die getrennten Eigenschaften
<p class="bahn"><span class="kasten shorthand">transform</span></p>
<p class="bahn"><span class="kasten getrennt">getrennte Eigenschaften</span></p>
<p class="hinweis">
Fahr über beide. Der obere soll beim Überfahren nur größer
werden, verliert dabei aber seine Drehung, weil die zweite
transform-Angabe die erste komplett ersetzt. Beim unteren
steht jede Angabe für sich und bleibt stehen.
</p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
line-height: 1.5;
}
.bahn {
margin: 0 0 48px 0;
}
.kasten {
display: inline-block;
padding: 8px 16px;
font-size: 13px;
color: #ffffff;
background-color: #2563eb;
transition: transform 0.2s ease-out, scale 0.2s ease-out;
}
.shorthand {
transform: rotate(-6deg);
}
.shorthand:hover {
transform: scale(1.15);
}
.getrennt {
rotate: -6deg;
}
.getrennt:hover {
scale: 1.15;
}
.hinweis {
max-width: 60ch;
color: #52525b;
} translate, rotate und scale gibt es auch als eigene Eigenschaften, und alle großen Browser können sie seit August 2022. Sie wirken zusätzlich zu transform und lösen zwei Probleme auf einmal.
Das erste ist die Reihenfolge. Sie werden immer in der Folge translate, rotate, scale angewendet, egal wie du sie notierst, und erst danach kommt ein eventuelles transform.
Das zweite ist das Überschreiben. Eine zweite transform-Angabe ersetzt die erste komplett, auch die Teile, die du behalten wolltest. Das ist ganz normale Kaskade. transform ist eine einzige Eigenschaft, und die spätere Deklaration gewinnt, egal wie viele Funktionen darin stehen.
Im Beispiel verliert der obere Kasten beim Überfahren seine Drehung und der untere nicht. Im Streifen ist das schwarz auf weiß zu sehen. Am oberen steht beim Überfahren eine einzige Zeile, transform: matrix(1.15, 0, 0, 1.15, 0, 0). Die 1,15 ist die Vergrößerung, und von der Drehung ist nichts übrig, denn die beiden schrägen Zahlen stehen auf 0. Beim unteren stehen zwei eigene Zeilen untereinander, rotate: -6deg und scale: 1.15. Die Drehung ist noch da, weil sie nie zur Debatte stand.
Für einen einzelnen festen Effekt nehme ich transform, das ist kürzer. Sobald sich mehrere Regeln oder Zustände einzelne Teile teilen, nehme ich die getrennten Eigenschaften.
Warum transform überall auftaucht
transform gehört zusammen mit opacity zu den wenigen Eigenschaften, die der Browser bewegen kann, ohne das Layout neu zu rechnen und ohne die Fläche neu zu zeichnen. Die getrennten Schreibweisen translate, rotate und scale zählen mit, es ist derselbe Mechanismus. Der Browser verschiebt dabei eine fertige Ebene, und das schafft auch ein altes Telefon flüssig. In Lektion 13.4 wird daraus eine Regel.
Zwei Nebenwirkungen kennst du aus Abschnitt 8. Beide hängen nicht an der Schreibweise, transform, translate, rotate und scale lösen sie gleichermaßen aus.
Ein Element mit einer dieser Angaben öffnet einen eigenen Stacking Context. z-index-Werte darin gelten dann nur noch untereinander. Außerdem wird es zum Bezugsrahmen für Nachfahren mit position: absolute und position: fixed, und die richten sich dann an ihm aus statt am nächsten positionierten Vorfahren oder am Viewport. Wenn eine fixierte Kopfzeile plötzlich mitscrollt, steht meistens irgendwo darüber eine dieser vier Angaben.
In der Aufgabe hängt an einer von zwei Karten ein Badge, und ihr Hover springt noch.
Zum Mitnehmen
transform bewegt die Darstellung, ohne den Platz im Layout zu ändern. Die Reihenfolge der Funktionen zählt, und die getrennten Eigenschaften translate, rotate und scale überschreiben einander nicht.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 4 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.