Shape-Morphing

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Beim Shape-Morphing wird eine geometrische Form in einem oder mehreren fließenden Zwischenschritten in eine andere verwandelt.

Mit den üblichen CSS-Eigenschaften ist das nur eingeschränkt möglich. Änderungen an width oder height verändern lediglich die Größe einer Form, und mit border-radius (bzw. rx und ry bei SVG-Elementen wie rect oder ellipse) lassen sich lediglich die Ecken oder Radien beeinflussen.

Deutlich flexibler sind SVG-Elemente wie polygon und path. Hier wird die Geometrie direkt durch die Werte der Attribute points bzw. d beschrieben. Werden diese Werte animiert, kann sich die Form fließend in eine völlig andere verwandeln.

In diesem Artikel lernst du Schritt für Schritt, wie solche Shape-Morphing-Animationen entstehen – von einfachen Polygonen bis zu geschwungenen Bézierkurven. Grundkenntnisse in SVG und CSS werden vorausgesetzt.


Animation mit SMIL

SMIL (Aussprache wie engl. smile), die Synchronized Multimedia Integration Language, ermöglicht es, SVG-Elemente und ihre Attribute direkt im SVG zu animieren.

Dazu erhält das eigentlich inhaltsleere path-Element nun ein animate als Kind. Im folgenden Beispiel wird das d-Attribut animiert, das die Form des Pfades festlegt:


Shape-Morphing 1: Fünfeck oder Stern? ansehen …
<path id="pentagon" d="M100,200 l-15,-50 l-15,-50 l40,-30 l40,-30 l40,30 l40,30 l-15,50 l-15,50 l-51,0 l-51,0">
  <animate attributeName="d" 
    values="M100,200 l-15,-50 l-15,-50 l40,-30 l40,-30 l40,30 l40,30 l-15,50 l-15,50 l-51,0 l-51,0;
            M100,200 l18,-60 l-48,-40 l60,0 l20,-60 l20,60 l60,0 l-48,38 l17,60 l-49,-38 l-49,38;
            M100,200 l-15,-50 l-15,-50 l40,-30 l40,-30 l40,30 l40,30 l-15,50 l-15,50 l-51,0 l-51,0;"
    dur="2s" 
    repeatCount="indefinite" 
  />
</path>

Die Kanten des Fünfecks bestehen aus jeweils 2 Linien, die während der Animation nach innen rücken und so einen Stern bilden.

Im folgenden Beispiel untersuchen wir, was genau passiert:

Shape-Morphing 2: Koordinaten verschieben ansehen …
<path id="form" >
  <animate 
    attributeName="d" 
    dur="5s" 
    values="M 100,10  0,100   70,50   130,150  200,100  z;     
            M 100,10  0,100   70,150  130, 50  200,100  z;     
            M 100,10  0,100   70,50   130,150  200,100  z" 
    repeatCount="indefinite"/>
</path>

Im Beispiel siehst du ein unregelmäßiges Fünfeck, dessen zweite und dritte Punkte die gleichen x-Koordinaten behalten. Die y-Werte pendeln aber während der Animation zwischen den Werten 50 und 150.

  • dur (duration) legt die Dauer der Animation fest
  • repeatCount legt fest, wie oft die Animation abläuft
    indefinite lässt sie unendlich fortsetzen.
Beachte: Um eine flüssige Animation zu erreichen, muss die Anzahl der Koordinaten immer identisch sein; ansonsten springt die Animation zwischen den Zuständen.


komplexere Formen mit Kurven

Shape-Morphing wird besonders interessant, wenn nicht nur Eckpunkte, sondern auch geschwungene Formen verändert werden. Dafür eignen sich die Pfadkommandos für kubische Bézier-Kurven.


3. Nachthimmel mit Mondphasen ansehen …
<path id="moon" d="M200,99 c-133,0 -133,200 0,200 c-133,0 -133,-200 0,-200" >
	<animate attributeName="d" 
     values="M200,99 c-133,0 -133,200 0,200 c-133,0 -133,-200 0,-200;
             M200,99 c-133,0 -133,200 0,200  c133,0  133,-200 0,-200;
             M200,99 c133,0   133,200 0,200  c133,0  133,-200 0,-200;
             M200,99 c-133,0 -133,200 0,200  c133,0  133,-200 0,-200;
             M200,99 c-133,0 -133,200 0,200 c-133,0 -133,-200 0,-200" 
     dur="12s"
	 repeatCount="indefinite">
</path>

Diese Animation zeigt den Mond in seinen verschiedenen Phasen:

  • Der Mond besteht aus zwei kubischen Bézier-Kurven
    Das sind zwar keine perfekten Halbkreise, sondern nur Näherungen, was in unserem Fall aber wohl ausreicht.[1]
  • Diese Kurven erhalten nun unterschiedliche Werte, wodurch sich der Mond von einem Kreis zur schmalen Sichel und wieder zurück verwandelt.

Das Beispiel enthält noch einige Sterne und Wolken, um den Nachthimmel realistischer darzustellen.

Herzschlag

Ein Herz kann aus nur einer Linie und zwei Bögen gezeichnet werden. Die folgende Animation versucht einen Pulsschlag nachzuahmen:

4: Herzschlag ansehen …
<path id="heart" d="M300,300 l-100,-100 a50,50 90 0,1 100,-75 a50,50 90 0,1 100,75 z">
  <animate attributeName="d" 
	 values="M300,300 l-100,-100 a50,50 90 0,1 100,-75 a50,50 90 0,1 100,75 z;
             M300,300 l-100,-100 a50,50 90 0,1 95,-75  a60,60 90 0,1 120,75 z;
             M300,300 l-100,-100 a50,50 90 0,1 100,-75 a50,50 90 0,1 100,75 z;"
	 dur="1s" 
     repeatCount="indefinite" >
  <animate attributeName="fill"
     values="#c82f04;hotpink;#c82f04"
     dur="1s"
     repeatCount="indefinite"/>
</path>

Wie du im Pfad sehen kannst, muss nur die erste Linie und die beiden Bögen festgelegt werden; die zweite Linie wird mit dem z-Befehl zum Ausgangspunkt des Pfades zurückgeführt.

Für das d-Attribut werden drei values-Paare angegeben, die den Herzschlag durch eine Vergrößerung des Bogens simulieren.

Danach findet sich ein weiteres animate-Element, mit dem das fill-Attribut animiert wird. Hier zeigt sich eine Eigenheit von SMIL: Für jedes weitere Attribut ist ein eigenes animate-Element erforderlich. Schon bei einfachen Animationen wächst der XML-Code dadurch schnell an und wird unübersichtlicher.

Zwischenfazit

Ich hatte dieses Tutorial im August 2015 veröffentlicht. Damals war so etwas revolutionär - einerseits unterstützte erst der 2011 erschienene IE9 SVG - auf vielen Windows XP-Rechnern lief aber noch IE6-8, bei denen man einen eigenen SVG-Viewer benötigte.

Heute scheinen die Einbindung der SMIL-Elemente in das SVG und die vielen XML-Attribute übermäßig komplex. Animationen können nicht einmal definiert und dann mehrfach eingebunden werden. Allerdings war das 1998, als der Standard entwickelt wurde, selbst bei HTML oft noch so - CSS war gerade 3 Monate alt und lief noch nicht in allen Browsern!

Deshalb gibt es in SMIL auch kein :hover, sondern das aus JavaScript bekannte mouseover und click.

SMIL/Animationen steuern

Andererseits kann man diese Animationen nicht zentral abstellen, falls der Nutzer keine Animationen wünscht oder verträgt. Deshalb wollen wir nun schauen, welche Möglichkeiten CSS mittlerweile bietet:

Animation mit CSS

In CSS lassen sich bereits viele Präsentationsattribute wie Farben, Größen oder Transformationen animieren. In der SVG-2-Spezifikation ist auch das d-Attribut als Geometrie-Eigenschaft definiert, die sich als Präsentationsattribut mit CSS formatieren und animieren lassen sollte.[2] Die Browserhersteller haben diese Möglichkeit jedoch bislang nicht implementiert.

Bei CSS-Animationen werden alle Änderungen einer Animation in gemeinsamen @keyframes zusammengefasst. Da sich das d-Attribut derzeit nicht per CSS animieren lässt, müssen wir für Shape-Morphing auf andere Techniken ausweichen.

Beschneiden mit clip-path

Eine solche Alternative ist die CSS-Eigenschaft clip-path. Sie beschneidet ein Element auf eine beliebige Form, sodass nur der innerhalb des Beschneidungspfads liegende Bereich sichtbar bleibt.

Der Beschneidungspfad kann aus einer Grundform, einem Polygon oder einem SVG-Pfad bestehen. Da sich diese Formen mit CSS animieren lassen, eignet sich clip-path hervorragend für viele Shape-Morphing-Effekte.

Shape-Morphing mit clip-path ansehen …
.icon {
    width: 1.3em;
    aspect-ratio: 1;
    background: gold;
    transform-origin:center;
    transition: all 1s;
	clip-path:polygon(
		50% 0%,
		61% 35%,
		98% 35%,
		68% 57%,
		79% 91%,
		50% 70%,
		21% 91%,
		32% 57%,
		 2% 35%,	
		39% 35% );
}

.done .icon{
  background: lime;
  clip-path: polygon(
    55%    31.4%,
    73.7%   0%,
    96.1%  15.7%,
    70.9%  56.8%,
    59.3%  75.5%,
    44.4% 100%,
    23.1%  84.6%,
    0%     67.5%,
    11.8%  46.8%,
    35.6%  63%
  );
    transform:scale(1.15) rotate(-8deg);
}

Das Symbol wird nicht als SVG-Grafik gezeichnet, sondern entsteht durch die CSS-Eigenschaft clip-path. Sie beschneidet das quadratische Element auf die Form eines zehnzackigen Polygons, dessen Eckpunkte mit Prozentwerten angegeben werden. Die Prozentangaben beziehen sich auf die Breite und Höhe des Elements und sorgen dafür, dass sich die Form automatisch an unterschiedliche Größen anpasst.

Mit der Klasse .done erhält das Symbol einen neuen Beschneidungspfad. Da beide Polygone aus derselben Anzahl von Eckpunkten bestehen, kann der Browser die entsprechenden Punkte miteinander interpolieren und die Form fließend von einem Stern in ein Häkchen überführen.

Zusätzlich ändern sich Hintergrundfarbe, Größe und Ausrichtung des Symbols. Die Eigenschaft transform: scale(1.15) rotate(-8deg) vergrößert das Häkchen leicht und dreht es ein wenig nach links. Dadurch wirkt die Bestätigung lebendiger als bei einer reinen Formänderung.

Alle Änderungen werden gemeinsam über die transition-Eigenschaft animiert. Im Gegensatz zu SMIL ist dafür kein weiteres Animationselement erforderlich – die Animation entsteht allein durch das Hinzufügen der Klasse .done.

Die clip-path-Eigenschaft wird mittlerweile von allen Browsern umgesetzt. Ursprünglich konnten nur Grundformen und die polygon()-Funktion für die Beschneidung genutzt werden. Mittlerweile können aber auch komplexere Pfade mit der path()-Funktion verwendet werden.

Beachte: Bei clip-path: path() werden die Koordinaten nicht wie bei polygon() als Prozentwerte relativ zur Elementgröße interpretiert. Der Pfad verwendet ein eigenes, in CSS-Pixeln angegebenes Koordinatensystem. Das macht path() für kleine, responsive Symbole etwas unhandlicher.

Masken mit CSS animieren

Weiter oben gab es bereits eine Grafik, die die Mondphasen illustrierte:

Spektakulärer sind die nur selten auftretenden Sonnenfinsternisse.

Diese Infografik aus Wikimedia Commons zeigt die Funktionsweise:

Schema einer Sonnenfinsternis

Solch ein Schema zeigt eine auf das Wesentliche reduzierte, grafische, übersichtliche Darstellung. Vorteil der SVG-Grafik ist die einfache Anpassung an andere Sprachen. Aus dem russischen Original sind mittlerweile über 20 Varianten entstanden, in denen nur der Text geändert wurde.


Sonnenfinsternis ansehen …
<mask id="moon">
  <rect x="-200" y="0" width="600" height="200" style="fill:white;" />
  <circle cx="-100" cy="100" r="80" style="fill:black;" />
</mask>
<g id="sun-g" mask="url(#moon)">
  <circle id="sun" cx="100" cy="100" r="80" />
</g>

Im Beispiel wird mit dem mask-Element eine Schablone angefertigt. Weiße Flächen (das rect-Element) sind undurchsichtig, schwarz ist vollständig transparent (der Kreis).

Diese Maske wird nun über die Sonne (der Kreis mit der id sun) gelegt und mit der Animation moonrun verschoben. Sobald der (Mond)-Kreis über der Sonne liegt, wird er ausgeschnitten. Um die totale Sonnenfinsternis genauer zu sehen, wird die Animation hier kurz „gebremst“.

Sonnenfinsternis ansehen …
svg {
  background: DeepSkyBlue;
  animation: sky 5s ease 1s infinite;
}  

#sun {
  fill: #DBBB34;
  stroke: yellow;
  stroke-width: 0;
  filter: drop-shadow( 0 0 10px transparent);
  animation: corona 5s ease 1s infinite;
}

@keyframes sky {
  0% { background: DeepSkyBlue; }
  45%, 55% { background: MidnightBlue; }
  100% { background: DeepSkyBlue; }
}

@keyframes corona {
  40% { filter: drop-shadow( 0 0 10px transparent); }
  45%, 55% { filter: drop-shadow( 0 0 10px lightYellow); }
  60% { filter: drop-shadow( 0 0 10px transparent); }
}

Ist dir aufgefallen, dass sich der Himmel verdunkelt? Dies wird über die CSS-Animation sky erreicht, die die Hintergrundfarbe entsprechend ändert.

filter: drop-shadow( 0 0 10px yellow); gibt der Sonne einen Schlagschatten, der über die Animation corona passend eingeblendet wird.

https://codepen.io/editor/miguelgchavez/pen/019fb835-ff6d-78da-b6a7-fdf8580128f6?panel=false&show=preview

https://medium.com/@jaskaransingh1989/solar-eclipse-fun-project-with-html-and-css-3e17e8097095

paint-order: https://tylersticka.com/journal/improved-css-text-stroke/


Ampelmännchen

Nach der Wiedervereinigung 1990 wurden die Ost-Ampelmännchen im Gebiet der ehemaligen DDR zunächst sukzessive gegen das westdeutsche Ampelmännchen ausgetauscht. Innerhalb der Bevölkerung kam es daraufhin zu Protesten, und das Ost-Ampelmännchen wurde etwas später in den Richtlinien für Lichtsignalanlagen als zulässiges Sinnbild aufgenommen.[3]

Shape-Morphing 5: Ampelmännchen ansehen …
<path fill="url(#verlaufGruen)"
d="m146 226c-4-12 14-27-1-35-14-11-27-23-40-35C83 174 66 199 47 218 35 207 22 195 12 182c4-12 21 9 27-5 8-11 17-22 25-33 6-7 1-16-8-11C44 135 38 122 27 119 16 111 9 100 0 90-5 77 18 70 21 84 31 92 43 100 56 101 65 95 71 84 76 75 71 67 56 63 61 49 64 42 51 37 62 34 70 29 70 18 60 16 52 10 61 5 67 10 78 16 83-1 93 7c10 6 21 10 31 16 5 6 0 18 12 19 7 0 14 12 3 9-11-4-23 2-26 14 2 73 81 116 64 131-4 5-24 31-31 31z" >
    <animate attributeName="d" 
             values="m146 226c-4-12 14-27-1-35-14-11-27-23-40-35C83 174 66 199 47 218 35 207 22 195 12 182c4-12 21 9 27-5 8-11 17-22 25-33 
                     ...
		     m146 226c-4-12 14-27-1-35-14-11-27-23-40-35C83 174 66 199 47 218 35 207 22 195 12 182c4-12 21 9 27-5 8-11 17-22 25-33 
                     6-7 1-16-8-11C44 135 38 122 27 119 16 111 9 100 0 90-5 77 18 70 21 84 31 92 43 100 56 101 65 95 71 84 76 75 71 67 56 
                     63 61 49 64 42 51 37 62 34 70 29 70 18 60 16 52 10 61 5 67 10 78 16 83-1 93 7c10 6 21 10 31 16 5 6 0 18 12 19 7 0 14 
                     12 3 9-11-4-23 2-26 14 2 73 81 116 64 131-4 5-24 31-31 31z;"
             dur="5s" 
             repeatCount="indefinite" />
</path>

Als Vorlage benutzte ich die in der Wikipedia vorhandene Grafik. Obwohl im SVG-Format, musste sie optimiert werden:

  • Im Code-Editor:
    • Gruppierungen und nicht verwendete Pfade löschen
  • In Inkscape:
    • Grafik skalieren
    • Pfade vereinfachen
  • Im SVG-Optimizer die Dezimalstellen runden
  • Metadaten löschen

Das West-Ampelmännchen wurde dann in Inkscape als Schablone unter den optimierten Pfad gelegt und die Endpunkte der Pfade verschoben.

Beachte:
  • Versuche die Punkte möglichst wenig zu verschieben, damit das Ineinanderfließen natürlicher aussieht.
  • Symmetrische Figuren können nur einseitig definiert und dann mit transform="scale" noch einmal spiegelverkehrt dargestellt werden.

Ein weiteres - sehr gut erklärtes - Anwendungsbeispiel bietet css-tricks, wo Chris Coyier einen fünfzackigen Stern zu einem Check-mark beim Online-Einkauf morphen lässt. Er erklärt (auf Englisch, aber mit vielen Grafiken) wie er die Koordinaten verschiebt.[4]

Ebenfalls sehenswert ist die Präsentation verschiedener Batman-Logos in Tavmjong Bah's Blog.[5]

SVG-Pfade mit d:path() animieren

Google Chrome’s Blink layout engine implementierte eine alternative Möglichkeit, die Werte innerhalb des d-Attributs zu animieren.

Das d kann so als Präsentationsattribut in CSS verwendet werden und erhält als Wert eine path()-Funktion, die wiederum die aus SVG bekannten Pfadkommandos enthält. Mittlerweile wird diese Syntax in allen Browsern außer Safari unterstützt.

Shape-morphing mit d:path() ansehen …
<path id="heart" />
Gegenüber der oben erwähnten Herzschlag-Animation mit SMIL ist das SVG-Markup viel aufgeräumter. Alle Präsentationsattribute und auch die Geometrieattribute sind im CSS notiert:
#heart {
	fill: #c82f04;
	stroke: #333;
	stroke-width: 3;
	d: path('M300,230 l-100,-100 a50,50 90 0,1 100,-75 a50,50 90 0,1 100,75 z'); 
}
@media (prefers-reduced-motion: no-preference) {
	#heart {
		animation: heartbeat 1s infinite;
	}
}

@keyframes heartbeat {

  0% {
     d: path('M300,230 l-100,-100 a50,50 90 0,1 100,-75 a50,50 90 0,1 100,75 z');
  }

  33% {
     d: path('M300,230 l-100,-100 a50,50 90 0,1 95,-75 a60,60 90 0,1 120,75 z');
  }
  
  66% {
     d: path('M300,230 l-100,-100 a50,50 90 0,1 100,-75 a50,50 90 0,1 100,75 z'); 
  }
  
  100% {
     d: path('M300,230 l-100,-100 a50,50 90 0,1 100,-75 a50,50 90 0,1 100,75 z'); 
  }
}
Beachte: Auch hier müssen die Anzahl der Punkte innerhalb des d-Attributs übereinstimmen!

Siehe auch:

Fazit

Falls Safari mit der Unterstützung für d: path() nachzieht, wäre dies die einfachste Möglichkeit, komplexere Shape-Morphing zu realisieren.

Bis dahin muss man überlegen, ob man eine SMIL-Animation direkt im SVG-Objekt oder die Alternative mit clip-path verwendet.

Alternative mit JavaScript

GreenSock MorphSVGPlugin

Das GreenSock MorphSVGPlugin ermöglicht es, Formen mithilfe von JavaScript zu morphen.[6] Was es gegenüber anderen Plugins auszeichnet, ist, dass die Anzahl der Koordinaten der zu morphenden Formen nicht identisch sein muss. Mit einem shapeindex kann man kontrollieren, welche Punkte mit welchen verschmelzen, um glattere und ansehnlichere Animationen zu erreichen.[7]

GSAP und die Plugins sind inzwischen frei verfügbar.

Weblinks

  1. Stack Overflow: How to create (a) circle with Bézier curves?
    Die Browser zeichnen die Kreise z. B. im canvas-Element mit Bézier-Kurven; verwenden dafür aber 4-8 Kurven, um so die Abweichungen zum perfekten Kreis zu minimieren.
  2. W3C: d-property (SVG 2)
  3. wikipedia: Entwicklung der besonderen Ampelmännchen in der DDR
  4. css-tricks.com: How SVG Shape Morphing Works
  5. tavmjong.free.fr: Tavmjong Bah's Blog SVG Path-Morphing am Beispiel des Batman-Zeichens
  6. css-tricks: GreenSock MorphSVGPlugin
  7. codepen: Flame brennende Kerze von Sarah Drasner
  8. codepen: MorphSVGPlugin Examples

CSS-Transitions

SVG und canvas


Abgerufen am 8.08.2026
von "http://wiki.selfhtml.org/wiki/Shape-Morphing"