Der folgende Beitrag ist ein Gastbeitrag von Ana Tudor. Ana leistet immer großartige Arbeit, wenn es darum geht, die Mathematik hinter dem, was wir grafisch im Web tun können, zu entschlüsseln. In diesem Fall ist das besonders nützlich, da es mehrere Möglichkeiten gibt, SVG-Transformationen zu handhaben, und diese einiges an mathematischem Denken erfordern, insbesondere die Umwandlung von einem Typ in einen anderen für die beste Cross-Browser-Unterstützung. Hier ist Ana.
Genau wie HTML-Elemente können SVG-Elemente mit Transformationsfunktionen manipuliert werden. Allerdings funktionieren viele Dinge bei SVG-Elementen nicht auf die gleiche Weise wie bei HTML-Elementen.
Zunächst einmal funktionieren CSS-Transformationen auf SVG-Elementen in IE nicht. Natürlich gibt es die Option, die SVG-Transformationsattribute für IE zu verwenden, wenn wir nur 2D-Transformationen auf unsere Elemente anwenden müssen.
Update: Edge unterstützt CSS-Transformationen auf SVG-Elementen ab EdgeHTML 17, veröffentlicht am 30. April 2018.
Wenn wir jedoch den Ansatz mit dem transform-Attribut verwenden, sind alle Parameter für Transformationsfunktionen Zahlen, was bedeutet, dass wir Einheiten nicht mehr steuern und kombinieren können. Zum Beispiel können wir keine %-Werte für Translate-Funktionen verwenden (obwohl %-Werte auch in Firefox nicht für CSS-Transformationen funktionieren würden, egal ob wir über transform-origin-Werte oder translate()-Parameter sprechen) und alle Rotations- oder Schrägwinkelwerte sind in Grad, wir können die anderen Einheiten, die uns in CSS zur Verfügung stehen, nicht verwenden.
%-Werte für transform-origin, aber sie sind relativ zum SVG und nicht zum Element, auf dem transform-origin gesetzt wurde, wie es in Chrome der Fall ist. Firefox scheint in diesem Fall korrekt zu funktionieren.Ebenfalls problematisch ist die Tatsache, dass die JavaScript-Feature-Erkennung in IE fehlschlägt (das Lesen des CSS-transform-Werts über JS gibt den Matrix-Äquivalenten des transform zurück, den wir in unserem Stylesheet gesetzt haben). Das bedeutet, wir brauchen entweder eine andere Methode, um IE zu erkennen, oder wir verwenden durchgängig die transform-Attribute (was insgesamt weniger Arbeit bedeutet).
Das Hauptunterschied zwischen HTML- und SVG-Elementen ist das lokale Koordinatensystem des Elements. Jedes Element, egal ob HTML oder SVG, hat eines.
Bei HTML-Elementen hat dieses Koordinatensystem seinen Ursprung am 50% 50%-Punkt des Elements.
Bei SVG-Elementen ist der Ursprung, vorausgesetzt, es wurde keine Transformation auf das Element selbst oder auf einen seiner Vorfahren innerhalb des <svg>-Elements angewendet, der 0 0-Punkt der SVG-Leinwand.
Die unterschiedlichen Ursprünge führen zu unterschiedlichen Ergebnissen bei rotate-, scale- oder skew-Transformationen, wenn der 50% 50%-Punkt des SVG-Elements nicht mit dem 0 0-Punkt der SVG-Leinwand zusammenfällt.
Um dies besser zu verstehen, sehen wir uns an, wie Transformationsfunktionen funktionieren.
Wie Transformationsfunktionen funktionieren
Eine Sache, die wir bei Transformationen verstehen müssen, ist, dass sie eine kumulative Wirkung haben, wenn sie auf verschachtelte Elemente angewendet werden. Das bedeutet, dass eine Transformation, die auf ein Element mit Nachfahren angewendet wird, auch alle seine Nachfahren zusammen mit ihren eigenen Koordinatensystemen und den Ergebnissen aller Transformationen dieser Nachfahren beeinflusst. Der Einfachheit halber gehen wir in den folgenden Fällen immer davon aus, dass unsere Elemente keine Vorfahren mit darauf angewendeten Transformationen haben. Wir gehen auch davon aus, dass unsere Elemente keine Nachfahren haben.
Translation
Eine Translation verschiebt alle Punkte eines Elements in die gleiche Richtung und um den gleichen Betrag. Translationen erhalten Parallelität, Winkel und Abstände. Sie kann als Verschiebung des Ursprungs des Koordinatensystems des Elements interpretiert werden – wenn das passiert, wird auch jedes Element, dessen Position relativ zu diesem Ursprung beschrieben wird (das Element selbst und alle Nachfahren, die es haben mag), verschoben. Sein Ergebnis hängt nicht von der Position des Koordinatensystems ab.

Die obige Abbildung zeigt den HTML-Fall (links) im Vergleich zum SVG-Fall (rechts). Die verblassten Versionen sind die ursprünglichen (bevor eine Translation angewendet wurde). Das Anwenden einer translate-Transformation verschiebt unsere Elemente und ihre Koordinatensysteme mit ihnen. Sie würde auch Nachfahren verschieben, wenn diese vorhanden wären.
Wie wir bereits wissen, ist der Unterschied zwischen den beiden die Position des Koordinatensystems. Für den HTML-Fall befindet sich der Ursprung des Koordinatensystems am 50% 50%-Punkt des Elements. Für den SVG-Fall befindet er sich am 0 0-Punkt der SVG-Leinwand (wir haben angenommen, dass keine Transformationen auf einem der möglichen Vorfahren des Elements innerhalb des <svg>-Elements angewendet werden). Bei einer Translation beeinflusst die Position des Koordinatensystems relativ zum Element jedoch nicht die endgültige Position des Elements.
Sowohl für HTML- als auch für SVG-Elemente haben wir bei der Verwendung von CSS-Transformationen drei Translate-Funktionen für 2D zur Verfügung: translateX(tx), translateY(ty) und translate(tx[, ty]). Die ersten beiden wirken sich nur auf die x- bzw. y-Richtungen (gemäß dem System des Elements) aus. Beachten Sie, dass die x- und y-Richtungen möglicherweise nicht mehr horizontal bzw. vertikal sind, wenn eine andere Transformation vor der Translation angewendet wird. Die dritte Translate-Funktion verschiebt das Element um tx entlang der x-Achse und um ty entlang der y-Achse. ty ist in diesem Fall optional und hat standardmäßig den Wert Null, wenn es nicht angegeben wird.
SVG-Elemente können auch mit transform-Attributen übersetzt werden. In diesem Fall haben wir nur eine translate(tx[ ty])-Funktion. Hier können die Werte auch leerzeichengetrennt sein, nicht nur kommagetrennt wie bei der ähnlichen CSS transform-Funktion. Also im sehr einfachen Fall, wo 1 SVG-Benutzereinheit gleich 1px ist, sind die folgenden beiden Methoden zum Übersetzen eines SVG-Elements äquivalent
• Verwendung einer CSS-Transformation
rect {
/* doesn't work in IE/ older Edge */
transform: translate(295px, 115px);
}
• Verwendung eines SVG-Transformationsattributs
<!-- works everywhere -->
<rect width='150' height='80' transform='translate(295 115)' />
Das SVG transform-Attribut und die CSS transform-Eigenschaft werden zusammengeführt.
Aufeinanderfolgende translate()-Transformationen sind additiv, was bedeutet, dass wir eine Kette wie translate(tx1, ty1) translate(tx2, ty2) als translate(tx1 + tx2, ty1 + ty2) schreiben können. Beachten Sie, dass dies nur gilt, wenn die beiden Translationen aufeinander folgen, ohne dass eine andere Art von transform zwischen den beiden liegt. Das Umkehren einer Translation translate(tx, ty) erfolgt über eine weitere Translation translate(-tx, -ty).
Rotation
Eine 2D-Rotation bewegt ein Element und alle Nachfahren, die es haben mag, um einen festen Punkt (einen Punkt, dessen Position nach der transform erhalten bleibt). Das Endergebnis hängt von der Position dieses festen Punktes ab. Ausgehend vom gleichen Element führen zwei Rotationen identischer Winkel um zwei verschiedene Punkte zu unterschiedlichen Ergebnissen. Genau wie die Translation verzerrt die Rotation das Element nicht und erhält Parallelität, Winkel und Abstände.
Aufeinanderfolgende rotate()-Transformationen um denselben festen Punkt sind additiv, genau wie Translationen, was bedeutet, dass rotate(a1) rotate(a2) äquivalent zu rotate(a1 + a2) ist (aber nur, wenn unsere beiden Rotationen aufeinander folgen, ohne dass eine andere Art von transform dazwischen liegt).
Das Umkehren einer Rotation rotate(a) erfolgt über eine weitere Rotation desselben Winkels in die entgegengesetzte Richtung rotate(-a) um denselben festen Punkt.

Die obige Abbildung zeigt den HTML-Fall (links) im Vergleich zum grundlegenden SVG-Fall (rechts). Die verblassten Versionen sind die ursprünglichen (bevor eine Rotation angewendet wurde). Das Anwenden einer Rotation bewegt die Elemente und ihre Koordinatensysteme um die festen Ursprünge, und es würde dasselbe mit Nachfahren unserer Elemente geschehen, wenn diese vorhanden wären.
Im HTML-Fall befindet sich der Ursprung des Koordinatensystems des Elements am 50% 50%-Punkt des Elements, sodass sich alles um diesen Punkt dreht. Im SVG-Fall befindet sich der Ursprung jedoch am 0 0-Punkt der SVG-Leinwand (wir haben angenommen, dass keine Transformationen auf einem der möglichen Vorfahren des Elements innerhalb des <svg>-Elements angewendet werden), was dazu führt, dass sich alles um diesen Punkt bewegt.
Die 2D-Rotationsfunktion ist im Fall der CSS transform-Eigenschaft ziemlich unkompliziert: nur rotate(angle). Der angle-Wert kann in Grad (deg), Radiant (rad), Umdrehungen (turn) oder Gon (grad) ausgedrückt werden. Wir könnten auch einen calc()-Wert verwenden (zum Beispiel so etwas wie calc(.25turn - 30deg)), aber das funktioniert derzeit nur in Chrome 38+/Opera 25+.
Update: Firefox 59+ unterstützt auch die Verwendung von calc() als Winkelwert für rotate()-Funktionen.
Wenn wir einen positiven Winkelwert verwenden, ist die Rotation eine im Uhrzeigersinn (und umgekehrt gibt ein negativer Winkelwert eine Drehung gegen den Uhrzeigersinn).
Bei SVG transform-Attributen ist die Rotationsfunktion etwas anders – rotate(angle[ x y]). Der angle-Wert funktioniert genauso wie bei der ähnlichen CSS-Transformationsfunktion (positiver Wert bedeutet Drehung im Uhrzeigersinn, negativer Wert bedeutet Drehung gegen den Uhrzeigersinn), muss aber ein einheitenloser Gradwert sein. Die optionalen einheitenlosen Parameter x und y geben die Koordinaten des festen Punktes an, um den das Element (und sein Koordinatensystem) gedreht wird. Wenn beide weggelassen werden, ist der feste Punkt der Ursprung des Koordinatensystems. Die Angabe nur des angle und des x-Parameters macht den Wert ungültig und es wird keine transform angewendet.
Genau wie bei einer translate()-Funktion können die Parameter leerzeichengetrennt oder kommagetrennt sein.
Beachten Sie, dass die Anwesenheit der Parameter x und y nicht bedeutet, dass der Ursprung des Koordinatensystems zu diesem Punkt verschoben wird. Das Koordinatensystem, genau wie das Element selbst (und alle Nachfahren, die es haben mag), wird einfach um den Punkt x y gedreht.
Das bedeutet, dass wir zwei Möglichkeiten haben, ein SVG-Element zu drehen (das Ergebnis ist in der rechten Hälfte der vorherigen Abbildung zu sehen)
• Verwendung einer CSS-Transformation
rect {
/* doesn't work in IE/ early Edge */
transform: rotate(45deg);
}
• Verwendung eines SVG-Transformationsattributs
<!-- works everywhere -->
<rect x='65' y='65' width='150' height='80' transform='rotate(45)' />
Wir können auch einen transform-origin-Wert in unserem CSS angeben, um die Verwendung der Parameter x und y zu emulieren. Längenwerte sind relativ zum Koordinatensystem des Elements, aber Prozentwerte sind relativ zum Element selbst, daher scheinen sie perfekt für das, was wir wollen. Wir sollten jedoch ein paar Dinge beachten.
Erstens, der CSS transform-origin und der feste Punkt, der innerhalb der rotate()-Funktion angegeben ist, sind *nicht identisch*. Für ein sehr einfaches Beispiel, sagen wir eines, das nur eine Drehung um den 50% 50%-Punkt des SVG-Elements verwendet, wird dies keine Rolle spielen. Betrachten Sie die folgenden beiden
rect {
transform: rotate(45deg);
/* doesn't work as intended in Firefox
* % values are taken relative to the SVG, not the element
* which actually seems to be correct */
transform-origin: 50% 50%;
}
<rect x='65' y='65' width='150' height='80'
transform='rotate(45 140 105)' />
<!-- 140 = 65 + 150/2 -->
<!-- 105 = 65 + 80/2 -->
Beide drehen das Element in Chrome auf die gleiche Weise, wie in der folgenden Abbildung zu sehen ist

Dies zeigt den Unterschied zwischen den beiden. Bei Verwendung von CSS wird das Koordinatensystem des Elements zuerst vom 0 0-Punkt der SVG-Leinwand zum 50% 50%-Punkt des Elements verschoben. Dann wird das Element gedreht. Bei Verwendung eines SVG-Transformationsattributs werden das Element und sein Koordinatensystem einfach um den Punkt gedreht, der durch das zweite und dritte Argument der rotate()-Funktion angegeben ist, ein Punkt, dessen Koordinaten wir so berechnet haben, dass er sich am 50% 50%-Punkt des Elements befindet. Der Ursprung des Koordinatensystems des Elements liegt immer noch weit außerhalb des Elements, und dieser Ursprung wird jede nachfolgende Transformation beeinflussen, die davon abhängt.
Um dies besser zu verstehen, verketten wir eine weitere Drehung nach der ersten, die das Element um 45° in die entgegengesetzte Richtung dreht
rect {
transform: rotate(45deg) rotate(-45deg);
transform-origin: 50% 50%; /* Chrome, Firefox behaves differently */
}
<rect x='65' y='65' width='150' height='80'
transform='rotate(45 140 105) rotate(-45)' />
<!-- 140 = 65 + 150/2 -->
<!-- 105 = 65 + 80/2 -->

Wie die obige Abbildung zeigt, heben sich bei Verwendung einer CSS-Transformation und Einstellung von transform-origin auf 50% 50% die beiden Rotationen gegenseitig auf, aber bei Verwendung des SVG-transform-Attributs unterscheidet sich der feste Punkt, um den das Element gedreht wird, von einer Rotation zur anderen – er ist der 50% 50%-Punkt des Elements für die erste Rotation und der Ursprung des Koordinatensystems des Elements für die zweite. In dieser Situation müssen wir rotate(-45 140 105) anstelle von rotate(-45) verwenden, um die Drehung rückgängig zu machen.
Dies ändert jedoch nichts an der Tatsache, dass *wir nur einen transform-origin haben (weil das Koordinatensystem des Elements nur einen Ursprung hat), aber bei Verwendung des SVG-transform-Attributs können wir mehrere Rotationen anwenden, die das Element jeweils um einen anderen Punkt drehen*. Wenn wir also unser Rechteck zuerst um 90° um seine untere rechte Ecke drehen und dann um weitere 90° um seine obere rechte Ecke, ist das mit einem SVG-Transformationsattribut einfach – wir geben für jede Drehung einen anderen festen Punkt an.
<rect x='0' y='80' width='150' height='80'
transform='rotate(90 150 160) rotate(90 150 80)'/>
<!--
bottom right:
x = x-offset + width = 0 + 150 = 150
y = y-offset + height = 80 + 80 = 160
top right:
x = x-offset + width = 0 + 150 = 150
y = y-offset = 80
-->

Aber wie erreichen wir denselben Effekt mit CSS-Transformationen? Für die erste Drehung ist das einfach, weil wir transform-origin auf right bottom setzen können, aber was ist mit der zweiten Drehung? Wenn wir sie einfach danach verketten, dreht sie das Element nur um weitere 90° um denselben festen Punkt (right bottom).
Wir benötigen drei verkettete Transformationen, um ein Element unabhängig von seinem transform-origin um einen festen Punkt zu drehen. Die erste ist eine translate(x, y)-Transformation, die den Ursprung des Koordinatensystems des Elements verschiebt, damit er mit dem festen Punkt übereinstimmt, um den wir alles drehen wollen. Die zweite ist die eigentliche Drehung. Und schließlich ist die dritte eine translate(-x, -y) – die Umkehrung der ersten Translation.
In diesem Fall würde unser Code so aussehen
rect {
/* doesn't work as intended in Firefox
* % values are taken relative to the SVG, not the element
* which actually seems to be correct */
transform-origin: right bottom; /* or 100% 100%, same thing */
transform:
rotate(90deg)
translate(0, -100%) /* go from bottom right to top right */
rotate(90deg)
translate(0, 100%);
}
Die folgende Abbildung zeigt schrittweise, wie dies funktioniert

Das zweite Problem mit transform-origin ist, dass nur Längenwerte in Firefox funktionieren. Prozente und Schlüsselwörter nicht, daher müssten wir sie durch Längenwerte ersetzen. Und Prozentwerte, die in translate()-Transformationen verwendet werden, funktionieren auch in Firefox nicht.
Update: Prozentwerte funktionieren jetzt auch in Firefox als transform-origin-Werte, aber sie verhalten sich nicht so, wie sie es in Chrome tun. Darüber hinaus scheint Firefox in diesem Fall richtig zu liegen, also verwenden Sie diese Methode nicht!
Skalierung
Die Skalierung ändert den Abstand vom Ursprung des Koordinatensystems des Elements zu jedem Punkt des Elements (und zu allen Nachfahren, die es haben mag) um denselben Faktor in der angegebenen Richtung. Sofern der Skalierungsfaktor nicht in allen Richtungen gleich ist – in diesem Fall haben wir eine **uniforme** (oder **isotrope**) Skalierung – wird die Form des Elements nicht beibehalten.
Ein Skalierungsfaktor im Bereich (-1, 1) lässt das Element schrumpfen, während ein Skalierungsfaktor außerhalb dieses Bereichs es vergrößert. Ein negativer Skalierungsfaktor führt zusätzlich zu einer Größenänderung auch zu einer Punktspiegelung am Ursprung des Koordinatensystems des Elements. Wenn nur ein Skalierungsfaktor von 1 abweicht, handelt es sich um eine **gerichtete** Skalierung.
Das Ergebnis einer scale-Transformation hängt von der Position des Ursprungs des Koordinatensystems ab. Ausgehend vom gleichen Element führen zwei Skalierungstransformationen mit demselben Faktor zu unterschiedlichen Ergebnissen für unterschiedliche Ursprünge.

Die obige Abbildung zeigt den HTML-Fall (links) im Vergleich zum SVG-Fall (rechts). In beiden Fällen skalieren wir das Element mit einem Skalierungsfaktor von sx entlang der x-Achse und einem Faktor von sy entlang der y-Achse. Was sich unterscheidet, ist die Position des Ursprungs des Koordinatensystems des Elements, das sich im HTML-Fall am 50% 50%-Punkt des Elements und im SVG-Fall am 0 0-Punkt der SVG-Leinwand befindet (wir haben angenommen, dass keine Transformationen auf einem der möglichen Vorfahren des Elements innerhalb des <svg>-Elements angewendet werden).
Bei der Verwendung der CSS transform-Eigenschaft haben wir drei Skalierungsfunktionen für 2D zur Verfügung: scale(sx[, sy]), scaleX(sx) und scaleY(sy). Die erste Skalierungsfunktion skaliert das Element um sx entlang der x-Achse und um sy entlang der y-Achse. Der Parameter sy ist optional und wird, wenn er nicht angegeben wird, als gleich sx angenommen, wodurch die Skalierung isotrop wird. sx und sy sind immer einheitenlose Werte. Die beiden anderen Funktionen wirken sich nur auf die x- bzw. y-Richtung (gemäß dem System des Elements) aus. scaleX(sx) ist äquivalent zu scale(sx, 1), während scaleY(sy) äquivalent zu scale(1, sy) ist. Wenn eine andere Transformation vor der Skalierung angewendet wird, sind die x- und y-Richtungen möglicherweise nicht mehr horizontal bzw. vertikal.
Bei der SVG transform-Attribut haben wir nur eine scale(sx[ sy])-Funktion. Auch hier können die Werte leerzeichengetrennt sein, nicht nur kommagetrennt wie bei der ähnlichen CSS transform-Funktion.
Für SVG-Elemente sind die folgenden beiden Methoden zum Skalieren äquivalent
• Verwendung einer CSS-Transformation
rect {
/* doesn't work in IE/ early Edge */
transform: scale(2, 1.5);
}
• Verwendung eines SVG-Transformationsattributs
<!-- works everywhere -->
<rect x='65' y='65' width='150' height='80' transform='scale(2 1.5)' />
Diese beiden ergeben dasselbe Ergebnis, wie in der rechten Hälfte von Abbildung 7 gezeigt. Aber was ist, wenn wir denselben Effekt erzielen wollen, den wir erzielen, wenn wir diese exakte Skalierungsfunktion auf ein HTML-Element anwenden? Nun, auf dieselbe Weise, wie wir es bei Rotationen tun können.
Bei der Verwendung von CSS-Transformationen haben wir die Möglichkeit, das entsprechende transform-origin auf unserem SVG-Element zu setzen oder Translationen vor und nach der Skalierung zu verketten – zuerst übersetzen wir das Koordinatensystem, damit sein Ursprung am 50% 50%-Punkt unseres SVG-Elements liegt, dann wenden wir die Skalierung an, und dann kehren wir die erste Translation um. Bei Verwendung eines SVG-transform-Attributs haben wir nur die Möglichkeit, Transformationen zu verketten. Der Code für unseren obigen Fall würde also lauten
• Verwendung einer CSS-Transformation mit transform-origin (tun Sie das nicht)
rect {
/* doesn't work in IE/ early Edge */
transform: scale(2, 1.5);
/* doesn't work as intended in Firefox
* % values are taken relative to the SVG, not the element
* which actually seems to be correct */
transform-origin: 50% 50%;
}
• Verwendung von verketteten CSS-Transformationen
rect {
/* doesn't work in IE/ early Edge */
transform: translate(140px, 105px)
scale(2 1.5)
translate(-140px, -105px);
}
• Verwendung von verketteten transform-Funktionen als Wert für ein SVG-transform-Attribut
<rect x='65' y='65' width='150' height='80'
transform='translate(140 105) scale(2 1.5) translate(-140 -105)'/>
<!-- works everywhere -->
Das folgende Demo veranschaulicht genau, wie die Verkettungsmethode funktioniert (klicken Sie auf die Wiedergabe ►, um zu starten)
Siehe den Pen Verkettung auf SVG-Elementen, um bezüglich eines bestimmten Punktes zu skalieren von Ana Tudor (@thebabydino) auf CodePen.
Etwas anderes, das man sich bei der Skalierung merken sollte, ist, dass zwei aufeinanderfolgende scale()-Transformationen scale(sx1, sy1) scale(sx2, sy2) als scale(sx1*sx2, sy1*sy2) geschrieben werden können und das Umkehren einer scale(sx1, sy1)-Transformation mit einer scale(1/sx1, 1/sy1)-Transformation erfolgt. Wenn alle Skalierungsfaktoren im Absolutwert gleich 1 sind, dann ist diese Skalierung ihre eigene Inverse.
Schrägstellen (Skewing)
Das Schrägstellen eines Elements entlang einer Achse verschiebt jeden seiner Punkte (außer denen, die sich genau auf der Schräg-Achse befinden) in diese Richtung um einen Betrag, der vom Schrägwinkel und dem Abstand zwischen diesem Punkt und der Schräg-Achse abhängt. Das bedeutet, dass sich nur die Koordinate entlang der Schräg-Achse ändert, während die Koordinate entlang der anderen Achse unverändert bleibt.
Im Gegensatz zu Translation oder Rotation verzerrt das Schrägstellen das Element, verwandelt Quadrate in nicht-gleichseitige Parallelogramme und Kreise in Ellipsen. Es erhält keine Winkel (bei einer Schrägstellung um den Winkel α werden die 90°-Winkel eines rechteckigen Elements zu 90° ± α) oder die Länge von Segmenten, die nicht parallel zur Schräg-Achse sind. Die Fläche des Elements wird jedoch erhalten.
Im Gegensatz zu Translation oder Rotation ist Schrägstellen nicht additiv. Das Schrägstellen eines Elements entlang einer Achse um einen Winkel α1 und dann erneut entlang derselben Achse um einen weiteren Winkel α2 ist *nicht* äquivalent zum Schrägstellen entlang dieser Achse um einen Winkel α1 + α2.
Das folgende Demo veranschaulicht, wie das Schrägstellen funktioniert – ändern Sie den Winkel und/oder die Achse, um zu sehen, wie sich dies auf das anfängliche Quadrat auswirkt.
Siehe den Pen Wie die Schrägstellungs-Transformation funktioniert von Ana Tudor (@thebabydino) auf CodePen.
Der Schrägwinkel ist der Winkel zwischen der End- und Anfangsposition der Achse, die sich nach Anwendung der Transformation ändert (nicht die Achse, entlang derer wir das Element schrägstellen). Ein positiver Schrägwinkel im Intervall [0°, 90°] addiert einen Wert mit demselben Vorzeichen wie die unveränderte Koordinate zum ursprünglichen Wert der sich ändernden Koordinate (die Koordinate entlang der Schräg-Achse), während ein negativer Wert im Intervall [-90°, 0°] einen Wert hinzufügt, dessen Vorzeichen dem der festen Koordinate entgegengesetzt ist.
Wenn wir eine Schrägstellung entlang der x-Achse durchführen, bleibt für jeden Punkt unseres Elements die y-Koordinate dieses Punktes gleich, während sich die x-Koordinate um einen Betrag d ändert, der vom Schrägwinkel und dem Abstand zwischen diesem Punkt und der Schräg-Achse abhängt ( dieser Vortrag erklärt, wie der Betrag d etwa ab Minute 15 berechnet wird). Die obere und untere Kante (und jede andere zur x-Achse parallele Linie) bleiben gleich lang, während die linken und rechten Kanten länger werden, wenn wir den Schrägwinkel erhöhen und ins Unendliche gehen, im Falle eines ±90°-Winkels. Sobald dieser Wert überschritten ist, werden sie kürzer, bis wir einen ±180°-Winkel erreichen, bei dem sie wieder ihre ursprüngliche Länge haben.
Beachten Sie, dass das Ergebnis einer Schrägstellung mit einem Winkel α im Intervall (90°, 180°] äquivalent zum Ergebnis einer Schrägstellung um den Winkel α - 180° ist (der im Intervall [-90°, 0°] liegen würde). Ebenso ist das Ergebnis einer Schrägstellung um einen Winkel α im Intervall (-180°, -90°] äquivalent zum Ergebnis einer Schrägstellung um den Winkel α + 180° (der im Intervall [0°, 90°) liegen würde).
Wenn wir eine Schrägstellung entlang der y-Achse durchführen, bleibt die x-Koordinate für jeden Punkt unseres Elements gleich, während sich die y-Koordinate um einen Betrag d ändert, der vom Schrägwinkel und der festen x-Koordinate abhängt. Die rechte und linke Kante (und jede andere zur y-Achse parallele Linie) bleiben gleich lang, während die obere und untere Kante länger werden, wenn wir den Schrägwinkel erhöhen und ins Unendliche gehen, im Falle eines ±90°-Winkels. Sobald dieser Wert überschritten ist, werden sie kürzer, bis wir einen ±180°-Winkel erreichen, bei dem sie wieder ihre ursprüngliche Länge haben.
Genau wie bei der Skalierung hängt das Ergebnis einer Schrägstellungsoperation von der Position des Ursprungs des Koordinatensystems des Elements ab. Ausgehend vom gleichen Element führen zwei Schrägstellungs-Transformationen mit demselben Winkel entlang derselben Achse zu unterschiedlichen Ergebnissen für unterschiedliche Ursprünge.

Die obige Abbildung zeigt den HTML-Fall (links) im Vergleich zum SVG-Fall (rechts). In beiden Fällen schrägstellen wir unsere Elemente entlang der x-Achse um denselben Winkel. Was sich unterscheidet, ist die Position des Ursprungs des Koordinatensystems des Elements, das sich im HTML-Fall am 50% 50%-Punkt des Elements und im SVG-Fall am 0 0-Punkt der SVG-Leinwand befindet. Wir haben im SVG-Fall angenommen, dass keine Transformationen auf einem der möglichen Vorfahren des Elements innerhalb des <svg>-Elements angewendet werden.
Der Einfachheit halber konzentrieren wir uns darauf, was mit nur einem Punkt unserer Elemente passiert: der oberen rechten Ecke. In beiden Fällen wird die y-Koordinate beibehalten – der Punkt bewegt sich nicht vertikal, nur horizontal. Horizontal sehen wir jedoch, dass sich diese Ecke im HTML-Fall nach links (in die negative Richtung der x-Achse) und im SVG-Fall nach rechts (in die positive Richtung der x-Achse) bewegt. Und die untere rechte Ecke bewegt sich in beiden Fällen, HTML und SVG, nach rechts, dem Schrägzug folgend. Wie funktioniert das also?
Wie bereits erwähnt, bleibt bei einem Schrägzug entlang der x-Achse die y-Koordinate jedes Punktes gleich, während zu der ursprünglichen x-Koordinate desselben Punktes ein Betrag d addiert wird, der vom Schrägwinkel und der festen y-Koordinate abhängt. Dieser Betrag d hat das Vorzeichen der festen Koordinate y (bezogen auf das lokale Koordinatensystem des Elements), wenn der Schrägwinkel im Intervall [0°, 90°] liegt, und das entgegengesetzte Vorzeichen, wenn der Schrägwinkel im Intervall [-90°, 0°] liegt.
Unser Winkel beträgt in beiden Fällen 60°, daher ist das Vorzeichen der y-Koordinate der oberen rechten Ecke der entscheidende Unterschied. Im HTML-Fall, bei dem der Ursprung des Koordinatensystems des Elements am Punkt 50% 50% des Elements liegt, ist die y-Koordinate der oberen rechten Ecke des Elements negativ, da die y-Achse nach unten zeigt. Im SVG-Fall hingegen, bei dem der Ursprung des Koordinatensystems des Elements am Punkt 0 0 der SVG-Leinwand liegt, ist die y-Koordinate der oberen rechten Ecke des Elements positiv. Das bedeutet, dass im HTML-Fall ein negativer Betrag zur ursprünglichen x-Koordinate der oberen rechten Ecke addiert wird, wodurch sie sich nach links bewegt, während im SVG-Fall ein positiver Betrag zur ursprünglichen x-Koordinate der oberen rechten Ecke addiert wird, wodurch sie sich nach rechts bewegt.
Ob wir ein SVG-Element mit CSS-Transforms oder dem SVG-Transform-Attribut schrägziehen, wir haben zwei Funktionen zur Verfügung: skewX(angle) und skewY(angle). Die erste verzerrt das Element entlang der x-Achse, die zweite entlang der y-Achse.
Für die CSS-Eigenschaft transform ist der angle ein Wert mit einer Einheit. Er kann in Grad (deg), Radiant (rad), Umdrehungen (turn), Gradian (grad) oder sogar mit calc() ausgedrückt werden, um beliebige dieser Einheiten zu kombinieren (beachten Sie jedoch, dass die Verwendung von calc() mit Winkeleinheiten derzeit nur in Blink-Browsern funktioniert).
Beim Schrägziehen des Elements mithilfe eines SVG-transform-Attributes ist unser angle-Wert immer ein grad-Wert ohne Einheit.
Das bedeutet, dass wir zwei äquivalente Möglichkeiten haben, ein SVG-Element zu schrägziehen (das Ergebnis ist rechts in der vorherigen Abbildung zu sehen)
• Verwendung von CSS-Transforms
rect {
transform: skewX(60deg); /* doesn't work in IE/ early Edge */
}
• Verwendung eines SVG-Transformationsattributs
<!-- works everywhere -->
<rect x='65' y='65' width='150' height='80' transform='skewX(60)' />
Wenn wir den gleichen Effekt erzielen möchten, der beim Anwenden dieser genauen Schrägzugfunktion auf ein HTML-Element erzielt wird, haben wir drei Möglichkeiten, dies zu erreichen, genau wie beim Skalieren
• Verwendung einer CSS-Transformation mit transform-origin (tun Sie das nicht)
rect {
/* doesn't work in IE/ early Edge */
transform: skewX(60deg);
/* doesn't work as intended in Firefox
* % values are taken relative to the SVG, not the element
* which actually seems to be correct */
transform-origin: 50% 50%;
}
• Verwendung von verketteten CSS-Transformationen
rect {
/* doesn't work in IE/ early Edge */
transform: translate(140px, 105px)
skewX(60deg)
translate(-140px, -105px);
}
• Verwendung von verketteten Transform-Funktionen als Wert für ein SVG-Transform-Attribut
<!-- works everywhere -->
<rect x='65' y='65' width='150' height='80'
transform='translate(140 105) skewX(60) translate(-140 -105)' />
Das folgende Demo veranschaulicht genau, wie die Verkettungsmethode funktioniert
Siehe den Stift Chaining on SVG elements to skew wrt a certain point von Ana Tudor (@thebabydino) auf CodePen.
Kette verkürzen
Nun, das Verketten von Transformationen erledigt die Aufgabe. Wir können SVG-Elemente drehen, skalieren und schrägziehen, und sie verhalten sich genauso wie HTML-Elemente mit denselben Transformationen. Und wenn wir verkettete Transformationen als Wert für ein SVG-Attribut verwenden, können wir sogar das gewünschte Ergebnis in IE erzielen. Aber es ist hässlich! Gibt es keine einfachere Methode, dies zu tun?
Wenn wir mit unserem SVG-Rechteck beginnen, das mit seinem 50% 50%-Punkt am 0 0-Punkt der SVG-Leinwand positioniert ist, können wir eine Übersetzung aus der Kette entfernen und unseren Rotationscode reduzieren auf
<rect x='-75' y='-40' width='150' height='80'
transform='translate(140 105) rotate(45)'/>
<!-- 75 = 150/2, 40 = 80/2 -->
Siehe den Stift
Chaining on SVG elements to rotate wrt a certain point #1 von Ana Tudor (@thebabydino) auf CodePen.
Wir könnten auch die erste Übersetzung loswerden, indem wir ein richtig gewähltes viewBox-Attribut für das <svg>-Element verwenden, das unser Rechteck enthält. Das viewBox-Attribut besteht aus vier durch Leerzeichen getrennten Komponenten. Die ersten beiden geben die x- und y-Koordinaten der oberen linken Ecke der SVG-Leinwand in Benutzereinheiten an, während die anderen beiden seine width und height in Benutzereinheiten angeben. Wenn kein viewBox-Attribut angegeben ist, sind die Koordinaten der oberen linken Ecke 0 0.
Unten sehen Sie den Unterschied zwischen einem <svg>-Element ohne angegebenen viewBox und einem mit viewBox='-140 -105 280 210'

<svg>-Element ohne viewBox vs. <svg>-Element mit viewBoxZurück zu unserem Beispiel: Wenn wir die viewBox so einstellen, dass der 0 0-Punkt der SVG-Leinwand dort positioniert wird, wo wir den 50% 50%-Punkt unseres Rechtecks haben wollen, wird unser Code
<svg viewBox='-140 -105 650 350'>
<rect x='-75' y='-40' width='150' height='80' transform='rotate(45)'/>
</svg>
Siehe den Stift Setting proper `viewBox` to rotate wrt a certain point #1 von Ana Tudor (@thebabydino) auf CodePen.
Praktische Anwendung
Wenn wir den 0 0-Punkt unserer SVG-Leinwand und jedes andere Element, das wir möglicherweise benötigen, genau in die Mitte legen, erleichtert dies die Arbeit mit Transformationen, da der 0 0-Punkt der SVG-Leinwand mit dem 50% 50%-Punkt des zu transformierenden Elements zusammenfällt. Das folgende Demo (klicken zum Abspielen/Pausieren) zeigt drei vierzackige Sterne, die anfangs in der Mitte positioniert und dann gedreht, verschoben, schräggezogen und skaliert werden, ohne dass ein transform-origin gesetzt oder zusätzliche Verschiebungen zur Kette hinzugefügt werden müssen
Siehe den Stift SVG Stars – final von Ana Tudor (@thebabydino) auf CodePen.
Schauen wir uns dieses Demo Schritt für Schritt an.
Der Stern selbst ist ein Polygon mit acht Punkten. Das folgende Demo zeigt, wie sie relativ zum Ursprung (0 0) der SVG-Leinwand positioniert sind. Fahren Sie mit der Maus über die x,y-Paare im Code oder die Punkte selbst, um zu sehen, welche mit welchen korrespondieren.
Siehe den Stift 4 point star – the points von Ana Tudor (@thebabydino) auf CodePen.
Wir haben drei solche Sterne. Wir wiederholen den Code für das Polygon nicht dreimal – stattdessen legen wir ihn in <defs> und <use> ihn später dreimal.
<svg viewBox='-512 -512 1024 1024'>
<defs>
<polygon id='star' points='250,0 64,64 0,250 -64,64 -250,0 -64,-64 0,-250 64,-64'/>
</defs>
<g>
<use xlink:href='#star'/>
<use xlink:href='#star'/>
<use xlink:href='#star'/>
</g>
</svg>
Das erste, was wir tun, ist, unsere Sterne von 0 auf 1 zu skalieren
use {
animation: ani 4s linear infinite;
}
@keyframes ani {
0% { transform: scale(0); }
25%, 100% { transform: scale(1); }
}
Dies ergibt das folgende Ergebnis.
Siehe den Stift SVG Stars – step #1 von Ana Tudor (@thebabydino) auf CodePen.
Als Nächstes möchten wir unserer Keyframe-Animation eine Rotation hinzufügen. Aber wir möchten für jeden Stern eine andere Rotation – sagen wir einen zufälligen Winkel plus einen bestimmten Winkel, der auf dem Index des Sterns basiert. Das bedeutet, wir können nicht mehr die gleiche Keyframe-animation für alle drei verwenden; wir brauchen drei verschiedene Animationen. Das hilft uns auch, die Füllungen unterschiedlich zu gestalten.
$n: 3;
$α: 360deg/$n;
$β: random($α/1deg)*1deg;
@for $i from 1 through $n {
$γ: $β + ($i - 1)*$α;
use:nth-of-type(#{$i}) {
fill: hsl($γ, 100%, 80%);
animation: ani-#{$i} 4s linear infinite;
}
@keyframes ani-#{$i} {
0% { transform: scale(0); }
25% { transform: scale(1); }
50%, 100% { transform: rotate($γ); }
}
}
Sie können das Ergebnis dieses Codes unten sehen
Siehe den Stift SVG Stars – step #2 von Ana Tudor (@thebabydino) auf CodePen.
Der nächste Schritt ist das Verschieben und Verkleinern unserer Sterne
@keyframes ani-#{$i} {
0% { transform: scale(0); }
25% { transform: scale(1); }
50% { transform: rotate($γ); }
75%, 100% {
transform: rotate($γ) translate(13em) scale(.2);
}
}
Siehe den Stift SVG Stars – step #3 von Ana Tudor (@thebabydino) auf CodePen.
Wir sind fast fertig! Wir müssen nur noch unsere Sterne schrägziehen und eine transform-Skalierung verwenden, um ihre Abmessungen nach dem Schrägziehen zu korrigieren.
@keyframes ani-#{$i} {
0% { transform: scale(0); }
25% { transform: scale(1); }
50% { transform: rotate($γ); }
75% {
transform: rotate($γ) translate(13em) scale(.2);
}
83% {
transform: rotate($γ) translate(13em) scale(.2)
skewY(30deg) scaleX(.866);
}
91% {
transform: rotate($γ) translate(13em) scale(.2)
skewY(60deg) scaleX(.5);
}
100% {
transform: rotate($γ) translate(13em) scale(.2)
skewY(90deg) scaleX(0);
}
}
Hier habe ich mehr als einen neuen Keyframe zur Präzision hinzugefügt. Während sich der Schrägwinkel linear ändert, tut dies der korrigierende Skalierungsfaktor nicht – sein Wert ist der Kosinus des Schrägwinkels und, wie die nächste Abbildung zeigt, ist der Graph der Kosinusfunktion zwischen 0° und 90° keine gerade Linie.

Dieses reine CSS-Demo ist jedoch in Firefox etwas fehlerhaft und funktioniert unter IE überhaupt nicht, da keine IE-Version CSS-Transforms auf SVG-Elemente unterstützt. Wir können all dies beheben, wenn wir SVG-Transform-Attribute verwenden und deren Wertänderungen mit JavaScript animieren. Sie können die JavaScript-Version unten sehen (klicken zum Starten).
Siehe den Stift SVG Stars – step #3 von Ana Tudor (@thebabydino) auf CodePen.
Wow, tolle Erklärung, Ana!
Wie Sie gezeigt haben, kann Animation aufgrund all der Browser-Inkonsistenzen ziemlich herausfordernd sein. Wie Sie, bin ich zu dem Schluss gekommen, dass der einzige Weg, es gut zu machen, die Verwendung von JS ist. Interessanterweise erscheint die Bewegung auf diese Weise auch reibungsloser, insbesondere auf iOS-Geräten im Vergleich zu CSS.
Neugierig: Haben Sie gesehen, was GSAP tut, um diese Probleme zu lösen? Zum Beispiel das Transform-Origin-Dilemma: https://css-tricks.de/svg-animation-on-css-transforms/
Vielen Dank nochmals für diese detaillierte Untersuchung von SVG-Transforms.
Ja, ich erinnere mich sehr gut an diesen Artikel, erstens, weil er ungefähr zu der Zeit erschien, als ich anfing, diesen zu schreiben (habe es jetzt bei GitHub überprüft, sagt mir 30. Oktober, also weniger als 2 Wochen auseinander… ja, ich bin *so* schlecht im Schreiben :P) und zweitens, weil es das erste Mal war, dass ich
getBBox()sah, was ich seitdem als immens nützlich für die Positionierung von Text empfinde.Ich bin eher ein Technik-Kopf, daher habe ich noch nie ein SVG in einem Grafikeditor erstellt und glaube, ich habe ein fertiges vielleicht zweimal verwendet.
Ich generiere sie meistens über JavaScript, weil ich vielleicht etwas Komplexeres möchte, was das manuelle Schreiben zu viel Wahnsinn wäre, als ich fähig bin. Dies bedeutet normalerweise, dass alles nach einer Regel generiert wird (auch wenn ich dort Zufälligkeit einführe, wird es nie etwas wie ein Pferd sein – Entschuldigung, Chris, das Pferd in deinem SVG-Talk ist in meinem Gehirn hängen geblieben), oft sind viele sehr ähnliche Komponenten involviert, die ich nur einmal in
<defs>erstelle, in der Mitte des SVG positioniere, dann mehrmalsuseund an der richtigen Stelle mit Hilfe von Transforms positioniere. Für etwas wie geometrische Formen kenne ich also schon alle ihre Abmessungen und die Transformationen, die ich darauf angewendet habe, noch bevor ich das entsprechendeuse-Element zum übergeordneten SVG hinzufüge, aber manchmal möchte ich auch Text für etwas und in diesem Fall brauche ich wirklichgetBBox(), weil ich die Abmessungen dieses Textes nicht im Voraus kenne und die Positionen gemäß diesen Abmessungen berechnen muss, damit er dort platziert wird, wo ich ihn haben möchte.Das mit dem Text war früher mein schlimmster SVG-Albtraum, bevor ich die Idee hatte, diese Funktion zu verwenden, also danke auch dir! :)
Großartige Arbeit, Ana.
(Wenn ein Bild 1.000 Worte wert ist, was ist der Wert einer animierten Erklärung?)
Großartiger Artikel!
Großartiger Artikel, tolle Erklärung, danke.
Hervorragender Artikel Ana, ich verwende GSAP, um die Transform-Origin-Probleme bei SVG-Animationen zu lösen, unter anderem schon seit langer Zeit.
Es ist tatsächlich gut, die inneren Abläufe zu verstehen und zu erkennen, warum dieses Problem besteht und wie wir diese Dinge selbst lösen könnten, ohne uns auf ein Plugin verlassen zu müssen.
GSAP ist großartig, aber wahrscheinlich ein bisschen schwer, wenn wir es nur verwenden, um das Transform-Origin eines SVGs zu beheben, das eine sehr einfache Transform-Animation hat.
Das ist die Website für meine Hochzeit http://yennyetygustavo.com/, ich benutze SVG-Bilder und GSAP-Animationen. Firefox hat mein Transform-Origin durcheinandergebracht, mit einer kleinen JS-Funktion konnte ich es lösen.
Schön, Gustavo. Ich wollte nur darauf hinweisen, dass du eine super alte Version von GSAP verwendest, die vor all den SVG-Workarounds liegt, die wir implementiert haben. Wenn du auf die neueste Version aktualisierst, kümmert sie sich automatisch um alle Transform-Origin-Probleme, auch unter Firefox. Wir erklären es in diesem Artikel: https://css-tricks.de/svg-animation-on-css-transforms/
Demos könnten viel anschaulicher sein, wenn sie die Transformation von rechts nach links anwenden, wie es in SVG geschieht.