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 man SVGs selbst erstellt, ist es nur logisch anstelle von komplexen Pfaden ein Rechteck zu zeichnen und dies dann passend zu rotieren oder scheren. In der Neufassung 2022 werden, ausgehend von der bereits aus den vorherigen Kapiteln bekannten transform-Eigenschaft in CSS eher die Besonderheiten von SVG erklärt.
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.
Du kannst mehrere Transformationen in einer Befehlskette kombinieren, indem man sie durch Whitespace-Zeichen oder Kommata voneinander trennt.
- 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] Du mussst von Fall zu Fall entscheiden, wenn du die Unterschiede und Besonderheiten kennst.
Verschieben mit translate()
Mit der translate(x,y)-Funktion kann man 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 */
#css-rect {
transform: translate(100px, 50px);
fill: ...
}
*/ SVG */
<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!
Ersetze im [Live ausprobieren] oder Seiteninspektor
- die
translate()
-Funktion durch einetranslateX()
-Funktion mit nur einem Parameter. - Pixelwerte im CSS durch em / Prozentangaben.
Was kannst du beobachten?
Strecken mit scale()
Mit der scale(x,y)-Funktion kann man 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 */
.css-rect {
animation: 4s infinite alternate scaling;
}
@keyframes scaling {
from { transform: scale(1);}
to { transform: scale(2);}
}
/* SVG */
<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.
→ 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 kann man 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 oberen 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 SVG-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])"
-
Drehwinkel
: (Pflichtangabe) Winkelmaß in Grad, negative Werte drehen das Objekt entgegen dem Uhrzeigersinn -
x
: (optionaler) X-Drehpunkt in dimensionslosen Größen (engl. unitless values) -
y
: (optionaler) 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.
.css-rect {
animation: 4s infinite turning;
transform-origin: 50px 0;
}
@keyframes turning {
from {
transform: rotate(0);
}
to {
transform: rotate(1turn);
}
}
<rect class="svg-rect"
x="50" y="50"
transform="rotate(20, 50, 50)"
/>
Als Drehpunkt werden die x- und y-Werte verwendet. Während dies bei Kreisen und Ellipsen der Mittelpunkt ist, ist es bei Rechtecken die linke, obere Ecke. Deshalb müsste dieser aber erst berechnet werden:
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:
.foo {
transform-origin: calc(var(--x) + 0.5*var(--width)),
calc(var(--y) + 0.5*var(--height));
}
Wenn man die x- , y-, width- und height-Attribute als custom property anlegen würde, könnte man diese Angaben sowohl für den Drehpunkt, als auch für die Koordinaten des Rechtecks verwenden:
.rect-zentriert {
x: calc(-0.5 * var(--width));
y: calc(-0.5 * var(--height));
width: var(--width);
height: var(--height);
translate: calc(var(--x) + (0.5 * var(--width))) calc(var(--y) + (0.5 * var(--height)));
}
.original {
--x: 0; /* gewünschte Position, spätere Verschiebung */
--y: 0;
--width: 50px;
--height: 30px;
}
.blau {
--x: 200px; /* gewünschte Position, spätere Verschiebung */
--y: 50px;
--width: 50px;
--height: 50px;
}
.oval-zentriert {
cx: 0;
cy: 0;
rx: calc(0.5 * var(--width));
ry: calc(0.5 * var(--height));
translate: var(--x) var(--y);
}
Alle Rechtecke und Ellipsen sind um den Ursprung zentriert.
Sie erhalten mit custom properties einheitliche Maße und Koordinaten, die erst im Regelsatz in die abweichenden Geometrie-Attribute eingesetzt werden.
So ist eine Rotation oder Stauchung problemlos möglich; ein Drehpunkt muss nicht berechnet werden!
Scherung mit skewX() oder skewY()
Mit den skewX()-, bzw. skewY()-Funktionen kann man 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>
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)
- Transform/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
- An Animated Tale of SVG Transforms Quinton Jason (dockyard.com)