Abschnitt 13 · Lektion 2
transform
Bis hierher hat jede Eigenschaft, die etwas bewegt hat, auch das Layout bewegt. 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, während sein Platz im Fluss unverändert stehen bleibt.
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. Es gibt zu jeder die achsenweisen Varianten translateX(), scaleY() und so weiter.
Zwei Kleinigkeiten dazu. Bei translate() beziehen sich Prozentangaben auf das Element selbst, nicht auf den Elternteil, und genau 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. Unscharf wird ein Bild im Element, sobald du es über seine eigene Auflösung hinaus vergrößerst, und unscharf wird alles während einer laufenden Animation: dort streckt der Browser eine fertig gezeichnete Ebene, statt bei jedem Bild neu zu zeichnen.
Im gestrichelten Rahmen des Beispiels siehst du den eigentlichen Punkt: Der Platz bleibt, wo er war. Der blaue Kasten hängt darüber wie ein Aufkleber. Deshalb schiebt eine Vergrößerung auch nichts zur Seite und kann Nachbarn überdecken.
Das lässt sich nachmessen. Zeig mit dem Inspector nacheinander auf die vier blauen Kästen: In der Kopfzeile des Streifens steht jedes Mal etwas anderes, rund 96 × 52 beim verschobenen, rund 106 × 75 beim gedrehten, rund 115 × 62 beim skalierten. Die Zeile width darunter sagt bei allen vieren 96px, und der gestrichelte Platz darum misst durchgehend rund 98 × 54. Oben steht, was du siehst, unten, was im Layout gilt, und transform ändert nur das Obere.
Eine Zeile transform steht dort ebenfalls, aber nie so, wie du sie geschrieben hast: 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). Das ist keine Marotte des Werkzeugs, sondern die Form, in die der Browser jede Kette von Funktionen zusammenrechnet. 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 56px 0;
border-top: 1px dashed #a1a1aa;
}
.kasten {
display: inline-block;
padding: 8px 12px;
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. Sie werden von links nach rechts angewendet, und jede arbeitet im Koordinatensystem, das die vorherige hinterlassen hat.
Erst drehen und dann schieben heißt deshalb: schieben in die gedrehte Richtung. Erst schieben und dann drehen heißt: schieben nach rechts, dann an Ort und Stelle drehen. Dieselben zwei Angaben, zwei verschiedene Ergebnisse, und wenn das Ergebnis nicht stimmt, ist die Reihenfolge die erste Stelle, an der du nachsiehst.
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: 40px 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: 40px;
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. Für ein Menü, das aus einer Ecke aufklappt, ist das die Eigenschaft, die den Unterschied macht zwischen “erscheint” und “wächst aus dem Knopf heraus”.
Hier hilft dir der Streifen ausnahmsweise nicht: transform-origin steht nicht darin, und die drei Kästen des Beispiels messen sogar gleich, rund 121 × 93 und rotate: 25deg an jedem. Verschieden ist allein, wo sie liegen, und die Lage zeigt der Streifen nicht. Dafür sind die gestrichelten Rahmen im Beispiel da.
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 40px 0;
}
.kasten {
display: inline-block;
padding: 10px 14px;
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;
} Seit einiger Zeit gibt es translate, rotate und scale auch als eigenständige Eigenschaften. Sie lösen zwei Probleme auf einmal.
Erstens die Reihenfolge: Sie werden immer in der Folge translate, rotate, scale angewendet, egal wie du sie notierst, und danach kommt erst ein eventuelles transform. Zweitens das Überschreiben. Eine zweite transform-Angabe ersetzt die erste komplett, auch die Teile, die du behalten wolltest. Der Grund steht oben: Am Ende steht eine einzige Matrix, und eine einzige kann nur einmal gesetzt werden.
Im Beispiel verliert der obere Kasten beim Überfahren seine Drehung, der untere nicht, und im Streifen ist das schwarz auf weiß zu sehen. Zeig auf den oberen, während du ihn überfährst: Dort steht eine 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, 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.
Faustregel: Für einen einzelnen festen Effekt ist transform kürzer. Sobald mehrere Regeln oder Zustände sich einzelne Teile teilen, nimm die getrennten Eigenschaften.
Warum transform überall auftaucht
Zusammen mit opacity gehört transform 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 dabei mit, sie sind derselbe Mechanismus. Er verschiebt eine fertige Ebene, und das schafft auch ein altes Telefon flüssig. Lektion 13.4 macht daraus eine Regel.
Zwei Nebenwirkungen musst du kennen, beide aus Abschnitt 8, und 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 also nur noch untereinander. Und es wird zum Bezugsrahmen für Nachfahren mit position: absolute und position: fixed, die sich dann an ihm ausrichten statt am nächsten positionierten Vorfahren oder am Fenster. Wenn eine klebende Kopfzeile plötzlich mitscrollt, steht meistens irgendwo darüber eine dieser vier Angaben.
Zum Mitnehmen
transform verändert, wie ein Element aussieht, nicht wie viel Platz es braucht. Der Kasten bleibt genau da stehen, wo er im Fluss stand, und nur sein Bild wird verschoben, gedreht oder gestreckt. Deshalb ruckelt nichts, wenn du damit animierst.
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.