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 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.

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.


Beachten Sie:
  • Mehrere Transformationen werden von rechts nach links durchgeführt.
  • Eine unterschiedliche Reihenfolge der Transformationen bringt fast immer andere Ergebnisse hervor.
Empfehlung: Verzichten Sie auf eine Positionierung mit x und y und legen Sie 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] 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-Wert
  • y: (optionaler) Y-Wert
    Wenn nur ein Wert angegeben wird, wird nur die x-Achse verschoben.
Beachten Sie: Das XML-Attribut benötigt eine Angabe in dimensionslosen Größen (engl. unitless values), die CSS-Eigenschaft eine Maßeinheit in px, etc.
Verleich CSS vs SVG ansehen …
#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

Checkmark Beide Techniken erzielen das gleiche Ergebnis!

Ersetzen Sie im Frickl oder Seiteninspektor

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

Was können Sie beobachten?

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

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

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 können Sie 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 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).

Datei:Unterschied-CSS-und-SVG.svg

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;

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)

.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 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>

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.

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
  3. SO: 3d transforms on SVG element
  4. CodePen: ebass Logo - sieht aus wie 3d