Shape-Morphing
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.
Inhaltsverzeichnis
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:
<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:
<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.
Information: Shape-Morphing mit Rastergrafiken?
Auch Rastergrafiken lassen sich scheinbar ineinander überblenden – etwa als animiertes GIF wie hier: Wikimedia Commons oder Video. Dabei werden jedoch lediglich einzelne Bilder nacheinander angezeigt. Die eigentliche Form wird nicht beschrieben und kann deshalb auch nicht gezielt verändert werden. SVG verfolgt einen anderen Ansatz: Hier wird die Geometrie einer Form durch Koordinaten beschrieben. Werden diese Koordinaten animiert, entsteht ein echtes Shape-Morphing. Das Ergebnis ist meist kompakter, beliebig skalierbar und lässt sich durch Anpassen weniger Werte einfach verändern.Deshalb ist SVG hier das Format der Wahl!
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.
<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:
<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.
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.
.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.
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:
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.
<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“.
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); }
}
Information: Schatten und SVG
Der aus der HTML-Welt bekannte box-shadow funktioniert nicht bei SVG-Elementen. Hier gibt es eine Möglichkeit mit SVG-Filter (SVG/Element/feGaussianBlur), was jedoch umfangreiches SVG-Markup erfordern würde und dann mit SMIL animiert werden könnte.Die vorhandenen Algorithmen wurden als
drop-shadow()-Funktion in die CSS-Filter-Eigenschaft integriert.So ist der Einsatz viel leichter geworden.
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://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]
<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.
- 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.
<path id="heart" />
#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');
}
}
Siehe auch:
- css-tricks: Animate SVG Path Changes in CSS vom 07.02.2020
- https://codepen.io/jorgeatgu/pen/bEEemM (codepen, der sowohl die schrittweise als auch die gemorphte Animation enthält.)
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
- ↑ 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. - ↑ W3C: d-property (SVG 2)
- ↑ wikipedia: Entwicklung der besonderen Ampelmännchen in der DDR
- ↑ css-tricks.com: How SVG Shape Morphing Works
- ↑ tavmjong.free.fr: Tavmjong Bah's Blog SVG Path-Morphing am Beispiel des Batman-Zeichens
- ↑ css-tricks: GreenSock MorphSVGPlugin
- ↑ codepen: Flame brennende Kerze von Sarah Drasner
- ↑ codepen: MorphSVGPlugin Examples
CSS-Transitions
- Morphing Device
Vom Desktop zum Tablet zum Handy und wieder zurück!
SVG und canvas
- Grundformen morphen mit animierter Randlinie (CodePen)
- Buchstaben morphen (CodePen)
- SVG Counter (CSS Only) (codePen)