Transform/SVG-Transformationen

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

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.

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.


Beachten Sie:
  • Mehrere Transformationen werden von rechts nach links durchgeführt.
  • Eine unterschiedliche Reihenfolge der Transformationen bringt fast immer andere Ergebnisse hervor.
Empfehlung: Verzichte auf eine Positionierung mit x und y und lege die endgültige Position des transformierten Elements mit translate() fest.


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-Wert
  • y: (optionaler) Y-Wert
    Wenn nur ein Wert angegeben wird, wird nur die x-Achse verschoben.
Beachte: Das XML-Attribut benötigt eine Angabe in dimensionslosen Größen (engl. unitless values), die CSS-Eigenschaft eine Maßeinheit in px, etc.
Vergleich CSS vs SVG ansehen …
/* 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

Checkmark Beide Techniken erzielen das gleiche Ergebnis!

Ersetze im [Live ausprobieren] oder Seiteninspektor

  • die translate()-Funktion durch eine translateX()-Funktion mit nur einem Parameter.
  • Pixelwerte im CSS durch em / Prozentangaben.

Was kannst du beobachten?

Beachten Sie, dass sich Prozentangaben in SVG auf das SVG-Koordinatensystem (und nicht wie bei HTML auf die Breite des verschobenen Elements) beziehen.
Beachte: Die Werte der Verschiebung werden mit den vorhandenen x- und y-Werten addiert.

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.
Skalieren ansehen …
 /* 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:

Skalierung am gleichen Ort ansehen …
<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.

Drehung mit rotate() ansehen …
.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).

Datei:Unterschied-CSS-und-SVG.svg

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])"

Diese Koordinaten bestimmen den Mittelpunkt der Drehung. Standardwert ist (0,0) des SVG-Elements; die Koordinaten beziehen sich darauf.

Drehpunkte festlegen ansehen …
.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:

Schritt 1: Drehpunkt mit custom properties
.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:

Vorlage für zentrierte Rechtecke ansehen …
.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!

Beachte: Da die verwendeten CSS-Eigenschaften Pixelwerte benötigen, muss jeder Wert in px notiert 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 und tan(90°) nicht definiert ist.
Textfeld verzerren ansehen …
#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.

Beispiel ansehen …
<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

Weblinks

  1. Spezifikation (CSSWG): 6. The SVG transform Attribute CSS Transforms Module Level 1, Editor’s Draft, 16 February 2022
  2. css-tricks.com Transforms on SVG Elements
    sehr interessanter, umfassender Artikel von Ana Tudor, ursprünglich 2015 geschrieben, 2019 aktualisiert