Transform/SVG-Transformationen
Bereits vor der Erfindung von CSS gab es Transformationen in SVG. Mit CSS3 wurden die Transform-Funktionen auch für HTML-Elemente mit der (im ersten Kapitel besprochenen) transform-Eigenschaft verfügbar gemacht.
Im CSS Transforms Module Level 2 wurden Transformationen in SVG und CSS zu einer gemeinsamen Spezifikation zusammengefasst. So können SVG-Elemente nun mit dem transform-Attribut sowie mit den entsprechenden CSS-Eigenschaften transformiert werden.
In der 2015er Urversion dieses Artikels wurde die Verwendung von transform als XML-Attribut erklärt: Wenn Sie SVGs selbst erstellen, ist es nur logisch anstelle von komplexen Pfaden ein Rechteck zu zeichnen und dies dann passend zu rotieren oder scheren. Jetzt 2022 werden, ausgehend von der bereits aus den vorherigen Kapiteln bekannten transform-Eigenschaft in CSS eher die Besonderheiten von SVG erklärt werden.
Herauszuheben ist, dass heute (Stand: November 2022) alle modernen Browser die hier vorgestellten Techniken unterstützen. Nur 3d-Transformationen in SVG sind, da SVG rein zweidimensional ist, noch nicht möglich.
Inhaltsverzeichnis
transform: CSS vs. XML-Attribut
Die klassische Eigenschaft für Transformationen ist das transform-Attribut. Es transformiert ein SVG-Element, indem eine oder mehrere Transform-Funktionen angewandt werden.[1]
Seit SVG2 ist es ein Präsentationsattribut, das nun auch in CSS festgelegt werden kann.
Vorteile für den Einsatz im CSS sind, dass …
- HTML und SVG mit den selben Techniken transformiert werden können
- Formatierungen für gleiche SVG-Objekte zusammengefasst werden können und das SVG-Markup aufgeräumter wird.
Andererseits werden gerade Verschiebungen jeweils für ein SVG-Objekt durchgeführt, sodass sie hier als XML-Attribut eines SVG-Elements richtig aufgehoben sind.
CSS:
element {
transform: … function2(…) function1(…);
}
SVG:
<element1 transform="… function2(…) function1(…)" >
<element2 transform="… function2(…) function1(…)" >
Diese Transform-Funktionen gibt es:
In der CSS-Eigenschaft transform können alle, im XML-Attribut transform nur die mit Stern * gekennzeichneten Funktionen verwendet werden.
Sie können mehrere Transformationen in einer Befehlskette kombinieren, indem Sie sie durch Whitespace-Zeichen oder Kommata voneinander trennen.
- Mehrere Transformationen werden von rechts nach links durchgeführt.
- Eine unterschiedliche Reihenfolge der Transformationen bringt fast immer andere Ergebnisse hervor.
Was soll ich nun verwenden - CSS-transform-Eigenschaft oder SVG-transform-Attribut?
Auffälligster Unterschied bei den Transform-Funktionen ist die Verwendung von Prozentwerten und Maßeinheiten in CSS, während innerhalb des transform-Attributs nur Zahlenwerte gültig sind. Das klingt angesichts der dimensionslosen Einheiten im SVG-Koordinatensystem sauberer.[2] Sie müsssen von Fall zu Fall entscheiden, wenn Sie die Unterschiede und Besonderheiten kennt.
Verschieben mit translate()
Mit der translate(x,y)-Funktion können Sie eine Form oder Gruppierung verschieben.
Folgende Angaben sind nötig:
x
: X-Werty
: (optionaler) Y-Wert
Wenn nur ein Wert angegeben wird, wird nur die x-Achse verschoben.
#css-rect {
transform: translate(100px, 50px);
fill: ...
}
<use href="#rechteck"
id="svg-rect"
transform="translate(100,150)"
/>
Im Definitionsabschnitt wird ein Muster-Rechteck definiert, das dann mit use mehrmals aufgerufen wird. Dabei werden das zweite und dritte Rechteck verschoben.
-
css-rect
wird mit der transform-Eigenschaft verschoben -
svg-rect
mit dem transform-Attribut
Beide Techniken erzielen das gleiche Ergebnis!
Ersetzen Sie im Frickl oder Seiteninspektor
- die
translate()
-Funktion durch einetranslateX()
-Funktion mit nur einem Parameter. - Pixelwerte im CSS durch em / Prozentangaben.
Was können Sie beobachten?
Strecken mit scale()
Mit der scale(x,y)-Funktion können Sie eine Form oder Gruppierung strecken oder spiegeln.
Parameter sind:
-
x
: Angabe einer Gleitkommazahl, Werte unter 1 verkleinern, Werte über 1 vergrößern die Form. Negative Werte spiegeln die Form. -
y
: Angabe eines optionalen Y-Werts.
Wird ein zweiter Wert festgelegt, bezieht sich der erste Wert nur auf die X-Koordinaten.
.css-rect {
animation: 4s infinite alternate scaling;
}
@keyframes scaling {
from { transform: scale(1);}
to { transform: scale(2);}
}
<rect class="svg-rect"
x="40" y="40" width="90" height="60"
transform="scale(1.5,1.5)" />
<rect class="original"
x="40" y="40" width="90" height="60" />
Um die Transformationen in den weiteren Beispielen sichtbar zu machen wurde die viewBox so verändert, dass auch die negativen Koordinaten zu sehen sind. Mit dem pattern-Element wird ein Raster als Muster definiert und aufgerufen.
Hauptartikel: Datenvisualisierung/Liniendiagramm#Koordinatensystem mit Raster
Wie oben gezeigt, werden bei einer Skalierung alle Koordinaten neu berechnet und so die Position des gezeigten Objekts verschoben. Durch die Kombination mit einer Verschiebung kann dies wieder ausgeglichen werden:
<ellipse id="oval" cx="150" cy="75" rx="50" ry="20" />
<use href="#oval" transform="scale(2)" opacity=".5"/>
<use href="#oval" transform="translate(-150,-75) scale(2)" opacity=".5"/>
Das skalierte Oval wird, da auch die Werte für cx und cy skaliert werden, gegenüber dem Original verschoben. Indem das zweite Oval mit
transform="translate(-150,-75) scale(2)"
wieder verschoben wird, kommt es auf dem Original zu liegen – die opacity von 0.5 lässt das Original durchscheinen.
Die nötigen Werte für die Verschiebung könnte man aus den Werten für cx, cy sowie dem Skalierungsfaktor berechnen; einfacher ist es die Form um den Ursprung zu zentrieren:
Drehen mit rotate()
Mit der rotate()-Funktion können Sie eine Form oder Gruppierung um den Punkt (0,0) drehen.
.css-rect {
animation: 4s infinite turning;
...
}
@keyframes turning {
from { transform: rotate(0);}
to { transform: rotate(1turn); }
}
<rect class="svg-rect"
x="40" y="40" width="90" height="60"
transform="rotate(20)" />
<rect class="original"
x="40" y="40" width="90" height="60" />
Das gelbe Rechteck (rechtes Code-Snippet) erhält ein transform-Attribut, das als Wert eine rotate()
-Funktion erhält. Parameter ist ein Winkelmaß von 20° als Zahlenwert.
Im linken Code-Snippet wird das gelbe Oval über die CSS-Animation turning
animiert. Die transform-Eigenschaft benötigt eine Angabe des Winkelmaßes mit einer Maßeinheit (hier: 1turn
).
Bei der Drehung der beiden Objekte wird deutlich, dass sie sich um den Mittelpunkt (0,0) des SVG-Koordinatensystems drehen. Bei HTML-Elementen ist der Drehpunkt dagegen immer der Mittelpunkt des Elements selbst.
Da sich bei einer Rotation alle Koordinaten des gedrehten Objekts ändern, würde sich das Rechteck ab einer Drehung von 70° in den negativen Bereich schieben. Dies ist gut bei der CSS-Animation des Ovals sichtbar, das aus dem „normalen“ Bereich der Leinwand (positive x- und y-Werte) in den negativen Bereich wandert.
Um eine Drehung um sich selbst zu ermöglichen, gibt es zwei Ansätze:
Lösung 1: Drehpunkt ändern
Die rotate()-Funktion hat in SVG 2 weitere, optionale Parameter:
transform="rotate(Drehwinkel [x-Drehpunkt y-Drehpunkt])"
Folgende Angaben sind nötig:
-
Drehwinkel
: Winkelmaß in Grad, negative Werte drehen das Objekt entgegen dem Uhrzeigersinn
Daneben sind folgende Angaben möglich;
-
x
: X-Drehpunkt in dimensionslosen Größen (engl. unitless values) -
y
: Y-Drehpunkt in dimensionslosen Größen (engl. unitless values)
Diese Koordinaten bestimmen den Mittelpunkt der Drehung. Standardwert ist (0,0) des SVG-Elements; die Koordinaten beziehen sich darauf.
Problem dieser Lösung ist, dass der Drehpunkt ja der Mittelpunkt der SVG-Form sein soll, dieser aber erst berechnet werden muss.
ToDo (weitere ToDos)
- Muss hier ein Live-Bsp. hin???
- CSS transform-origin einbauen
.foo { transform-origin: calc(var(--x) + 0.5*var(--width)), calc(var(--y) + 0.5*var(--height)); }
Lösung 2: „zentrierte“ Objekte
Einfacher wäre es, SVG-Objekte so zu zeichnen, dass Mittelpunkt und Ursprung zusammenfallen. Ein solches Objekt könnte gedreht oder skaliert und dann an den passenden Zielort verschoben werden.
Während es bei circle und ellipse noch trivial ist, die cx und cy auf Null zu setzen, muss man bei Rechtecken einen Trick anwenden:
rect { --width: 40; /* dimensionslose Einheiten */ --height: 30; --translateX: 100; /* gewünschte Position, spätere Verschiebung */ --translateY: 50; transform: rotate(30deg); } <rect x="calc(-0.5 * (var(--width)" y="calc(-0.5 * (var(--height)" width="var(--width)" height="var(--height)" transform="var(--translateX), var(--translateY)" />
Da wäre transform-origin gar nicht notwendig.
Da muss ich aber erst gucken, ob das überhaupt geht!
Scherung mit skewX() oder skewY()
Mit den skewX()-, bzw. skewY()-Funktionen können Sie eine Form oder Gruppierung durch eine Scherung verzerren.
Folgende Angabe ist nötig:
-
grad
: Winkelmaß in Grad. Positive Werte neigen das Objekt im Uhrzeigersinn, negative gegen ihn. 90 ist keine gültige Angabe für beide skew-Funktionen, da hier der Tangens berechnet wird undtan(90°)
nicht definiert ist.
#textfeld {
animation: 4s infinite alternate zerren;
}
@keyframes zerren {
from { transform: skewX(0); }
to { transform: skewX(45deg); }
}
<g id="textfeld2" transform="skewY(40)">
<rect x="30" y="30" width="50" height="30" />
<text x="35" y="50">SVG</text>
</g>
3d
Irgendwie ist man ja gewohnt, dass mit SVG alles geht. Als ich versuchte, den CSS-Würfel in SVG nachzuzeichnen, fiel mir erst auf den zweiten Blick auf, dass die in CSS vorhandenen 3d-Funktionen zwar „funktionieren“ - SVG aber eben nur eine zweidimensionale Zeichenfläche ist.
Ein Workaround ist, die sechs Flächen des Würfels als SVG zu realisieren und diese im HTML-Dokument dreidimensional zu rotieren.[3][4]
Anwendungsbeispiele
Tangram
Tangram ist ein chinesisches Legespiel, in dem ein Quadrat in 7 geometrische Formen zerschnitten wird, die dann beliebig zu neuen Bildern gedreht, gespiegelt und verschoben werden.
<svg>
<defs>
<title>Vorlagen für Tangram-Figuren</title>
<symbol id="dreieck_gr">
<desc>das große Dreieck</desc>
<path d="m0,0 132,132 132-132z"/>
</symbol>
<symbol id="dreieck_m">
<desc>das mittlere Dreieck</desc>
<path d="m0,0 h132 v132 z"/>
</symbol>
<symbol id="dreieck_kl">
<desc>das kleine Dreieck</desc>
<path d="m0,66 l66,66v-132z"/>
</symbol>
<symbol id="quad">
<desc>das quadratische Rechteck</desc>
<path d="m0,0 v94 h94 v-94z"/>
</symbol>
<symbol id="para">
<desc>das Paralellogramm</desc>
<path d="m66,0 l-67,67 v132 l67-67 z"/>
</symbol>
<symbol id="viereck">
<text x="10" y="280">Quadrat</text>
<use href="#dreieck_gr" class="eins"/>
<use href="#dreieck_gr" class="zwei" transform="scale(-1 1) rotate(90)"/>
<use href="#dreieck_m" class="drei" transform="translate(264,132) rotate(90)"/>
<use href="#dreieck_kl" class="vier" x="132" y="66"/>
<use href="#quad" class="sechs" transform="translate(132,132) rotate(45)"/>
<use href="#para" class="sieben" x="198" y="0"/>
<use href="#dreieck_kl" class="fuenf" transform="translate(132,197) rotate(90)"/>
</symbol>
<symbol id="schiff">
<text x="40" y="250">Schiff</text>
<use href="#dreieck_gr" class="eins" transform="translate(140,0) rotate(90)"/>
<use href="#dreieck_gr" class="zwei" transform="translate(290,0) rotate(90)"/>
<use href="#dreieck_m" class="drei" transform="translate(400,80) scale(-1 1) rotate(45)"/>
<use href="#dreieck_kl" class="vier" transform="translate(281,218) rotate(45)"/>
<use href="#para" class="sieben" transform="translate(374,218) rotate(45) "/>
<use href="#dreieck_kl" class="fuenf" transform="translate(187,311) rotate(135)"/>
<use href="#quad" class="sechs" transform="translate(140,264)"/>
</symbol>
</defs>
<use href="#viereck" x="10" y="10"/>
<use href="#schiff" x="400" y="10"/>
</svg>
Die einzelnen Formen werden als symbol deklariert und dann mit use aufgerufen.
Da es beim Rotieren, Spiegeln und Verschieben zu Rundungsfehlern kommen kann, wurde den Figuren ein 1px breiter weißer Rand gegeben, der das Gesamtbild besonders im ersten Beispiel gleichmäßiger aussehen lässt.
Siehe auch
- SVG/Tutorials/SVG mit CSS animieren: Transformationen (SVG-Elemente mit CSS-transforms gestalten)
- SVG/Tutorials/Rube-Goldberg-Maschinen
- CSS-Würfel
Weblinks
- ↑ Spezifikation (CSSWG): 6. The SVG transform Attribute CSS Transforms Module Level 1, Editor’s Draft, 16 February 2022
- ↑ css-tricks.com Transforms on SVG Elements
sehr interessanter, umfassender Artikel von Ana Tudor, ursprünglich 2015 geschrieben, 2019 aktualisiert - ↑ SO: 3d transforms on SVG element
- ↑ CodePen: ebass Logo - sieht aus wie 3d