SVG/Tutorials/Shape-Morphing
Beim Shape-Morphing wird eine geometrische Form in einem oder mehreren fließenden Zwischenschritten in eine andere verwandelt.
Mit „normalen “ CSS-Eigenschaften ist so etwas nur eingeschränkt möglich: Eine Änderung der Breite oder Höhe verändert nur die Dimensionen der Form; mit border-radius (oder rx bei Ellipsen und rect-Elementen können die Ecken abgerundet werden.
Anders sieht es bei Polygon und path-Elementen aus. Im points- , bzw. d-Attribut können durch eine Animation der Werte die Form völlig verändert werden.
In diesem Artikel lernst du, wie man Shape-Morphing-Animationen erstellt. Dabei sind Grundkenntnisse in SVG und in CSS erforderlich!
Inhaltsverzeichnis
Information: Shape-Morphing mit Rastergrafiken?
Deshalb ist SVG hier das Format der Wahl!
In CSS kann man zwar Präsentationsattribute wie Farbe oder auch Abmessungen ändern. Das d-Attribut gehört leider noch nicht dazu, sodass diese Möglichkeit vorerst ausfällt. Eine Alternative ist der Einsatz von clip-path oder der experimentellen d:path-Eigenschaft.
Animation mit SMIL
SMIL (Aussprache wie engl. smile), die Synchronized Multimedia Integration Language ist eine eigenständige XML-Sprache, die sogar noch vor SVG selbst festgelegt wurde.
Das XML-Attribut d beinhaltet die Pfad-Kommandos und die dazugehörigen Koordinaten. Bei der Animation mit SMIL erhält das eigentlich inhaltsleere path-Element nun ein Kind-Element animate.
Die unterschiedlichen Koordinaten können entweder mit from to oder bei mehreren Animationsschritten die einzelnen Schlüsselzustände in einer mit Semikolon getrennten values-Liste notiert werden.
<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 Werte 50 und 150.
Achtung!
animate
-Elemente nicht gerendert werden.Kopiere das Beispiel in Deinen Code-Editor!
Du kannst die Punkte jedoch auch in x- und y-Richtung gleichzeitig verschieben:
<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>
<path id="hexagon" d="M420,200 l-40,-80 l40,-80 h100 l40,80 l-40,80Z">
<animate attributeName="d"
values="M420,200 l-40,-80 l40,-80 h100 l40,80 l-40,80Z;
M400,200 l0,-80 l0,-80 h140 l0,80 l0,80Z;
M420,200 l-40,-80 l40,-80 h100 l40,80 l-40,80Z;"
begin="hexagon.mouseover"
end="hexagon.click"
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.
Die Kanten des Sechsecks richten sich während der Animation so aus, dass ein Rechteck entsteht. Du kannst die Animation starten, wenn du mit der Maus über die Figur fährst und Sie beenden, wenn du darauf klickst.
komplexere Formen mit Kurven
Du kannst auch komplexere Formen wie Kurven morphen. Hier empfiehlt es sich das Pfadkommando für kubische Bézier-Kurven zu verwenden, mit dem du auch gerade Linien ziehen kannst, wenn du die Kontrollpunkte auf (0,0) setzt.
<path d="M50,50 c0,0 0,0 100,0 0,0 0,0 0,100 0,0 0,0 -100,0 0,0 0,0 0,-100z;">
<animate
attributeName="d"
values="M50,50 c0,0 0,0 100,0 0,0 0,0 0,100 0,0 0,0 -100,0 0,0 0,0 0,-100z;
M50,50 c0,0 0,0 100,0 0,0 0,0 0,100 0,0 0,0 -100,0 0,0 0,0 0,-100z;
M50,50 c46,-46 92,-9 100,0 0,0 46.5,46 0,100 0,0 -45,49 -100,0 0,0 -46,-46 0,-100z;
M50,50 c46,-46 92,-9 100,0 0,0 46.5,46 0,100 0,0 -45,49 -100,0 0,0 -46,-46 0,-100z;
M50,50 c0,0 0,0 50,0 0,0 0,0 50,0 0,0 0,0 -50,120 0,0 0,0 -50,-120z;
M50,50 c0,0 0,0 50,0 0,0 0,0 50,0 0,0 0,0 -50,120 0,0 0,0 -50,-120z;
M50,50 c46,-46 92,-9 100,0 0,0 46.5,46 0,100 0,0 -45,49 -100,0 0,0 -46,-46 0,-100z;
M50,50 c46,-46 92,-9 100,0 0,0 46.5,46 0,100 0,0 -45,49 -100,0 0,0 -46,-46 0,-100z;
M50,50 c0,0 0,0 100,0 0,0 0,0 0,100 0,0 0,0 -100,0 0,0 0,0 0,-100z;"
dur="5s"
repeatCount="indefinite" />
</path>
Damit die Formen nicht gleich wieder beginnen die nächste Form anzunehmen, werden die Werte doppelt eingetragen.
So wird das Rechteck zum Kreis und verharrt dann, bis es zum Dreieck und dann nach einer Pause wieder zum Kreis wird.
Herzschlag
Ein Herz kann aus zwei Linien und zwei Bögen gezeichnet werden. Die folgende Animation versucht das Schlagen im Puls nachzuahmen:
<path id="example"
d="M0,300
l-100,-100
a50,50 90 0,1 100,-75
a50,50 90 0,1 100,75
z"/>
<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="2s"
repeatCount="indefinite" >
</path>
Wie du im Pfad mit der id example
sehen können, 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.
Mondphasen
Eine Animation, die den Mond in seinen verschiedenen Phasen zeigt, ist schwieriger. Eigentlich könnte man den Mond in zwei elliptischen Bögen zeichnen. Bei einer Animation müsste der konkave Innenbogen mittels einer Änderung der large-arc-flag and sweep-flag-Werte zu einem konvexen Bogen werden, was aber nur mit einem Sprung funktioniert:
<path id="moon" d="M250,50 a60,60 1 1,0 0,100 a60,60 1 0,1 0,-100">
<animate attributeName="d"
values="M250,50 a60,60 1 1,0 0,100 a60,60 1 1,0 0,-100;
M250,50 a60,60 1 1,0 0,100 a50,50 1 0,1 0,-100;
M250,50 a60,60 1 1,0 0,100 a60,60 1 0,1 0,-100"
dur="2s"
repeatCount="indefinite" >
</path>
Dieses Beispiel ist komplexer als gedacht!
- Die Bögen müssen durch kubische Bézier-Kurven dargestellt werden. Allerdings lassen sich so keine perfekten Halbkreise darstellen, sondern nur Näherungen, was in unserem Fall aber wohl ausreicht.[1]
- die Ausgangspunkte der Bögen wandern entlang des Mondrands. Die Koordinaten der Ausgangspunkte müssten mit JavaScript berechnet werden, worauf in dieser Animation jedoch verzichtet wurde.
</style>
<svg viewbox="0 0 500 360">
<path id="moon" d="M250,50 a60,60 1 1,0 0,100 a60,60 1 0,1 0,-100">
<animate attributeName="d"
values="M250,50 a60,60 1 1,0 0,100 a60,60 1 1,0 0,-100;
M250,50 a60,60 1 1,0 0,100 a50,50 1 0,1 0,-100;
M250,50 a60,60 1 1,0 0,100 a60,60 1 0,1 0,-100"
dur="2s"
repeatCount="indefinite" >
</path>
So, jetzt funktioniert es:
Das Beispiel enthält noch einige Sterne und Wolken, um den Nachthimmel realistischer darzustellen.
Mehr über Sonne, Mond und Sterne finden Sie im Artikel SVG/Tutorials/Infografiken
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.[2]
<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.
- Versuchen Sie 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.[3]
Ebenfalls sehenswert ist die Präsentation verschiedener Batman-Logos in Tavmjong Bah's Blog.[4]
Zwischenfazit
Ich hatte dieses Tutorial im August 2015 veröffentlicht. Damals war so etwas revolutionär - andererseits 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 auch kein :hover
, sondern das aus JavaScript bekannte mouseover
und click
.
Hauptartikel: 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 mittlerwiele bietet:
Alternativen mit CSS
In der SVG 2-Spezifikation ist das d
-Attribut eine Geometrie-Eigenschaft, die als Präsentationsattribut auch mit CSS formatiert und animiert werden sollte[5]. Allerdings haben die Browserhersteller dies so nicht implementiert.
Deshalb müssen wir uns nach Alternativen umsehen, die das gleiche Ziel erreichen.
Beschneidung mit clip-path
Es gibt aber einen Umweg über die clip-path-Eigenschaft, mit der du ein Element beschneiden kannst. Dies bedeutet, dass nicht die gesamte Box des Elements dargestellt wird. Die dafür nötigen Beschneidungspfade werden als Grundformen oder SVG-Grafik definiert. Sie können auch mit CSS-Animation so animiert werden, dass sie ihre Form ändern (engl. morphen).
.intro {
clip-path: circle(15% at 0% 0%);
}
.intro:hover,
.intro:focus {
clip-path: circle(75%);
}
@media (prefers-reduced-motion: no-preference) {
.circle {
animation: circle 3s infinite;
clip-path: circle(75%);
}
.star {
animation: star 3s infinite;
clip-path: polygon(50% 0%, 65.5% 32.9%, 100% 38.2%, 75% 63.8%, 80.9% 100%, 50% 82.9%, 19.1% 100%, 25% 63.8%, 0% 38.2%, 34.5% 32.9%);
}
.burst {
animation: 1.5s burst infinite;
}
}
Im Beispiel finden sich ein div und drei Buttons. Das div verändert bei :hover und :focus (mit der Tastatur) seine Beschneidungsform und wird so sichtbar.
Die Buttons verändern durch eine CSS-Animation ihre (Beschneidungs)-form und ihre Farbe. Dies sorgt einerseits für Aufmerksamkeit, bei vielen aber auch für Irritation aufgrund des Klickibunti-Effekts. Das Bewusstsein, dass manche Nutzer dies nicht wünschen und ihr Willen respektiert werden sollte, hat sich immer mehr durchgesetzt. Deshalb gibt es mit prefers-reduced-motion eine Abfrage der Benutzereinstellungen. Ohne Festlegung wird die Animation dargestellt.
Falls der Nutzer im Betriebssystem oder Browser dies mit reduce
eingeschränkt hat, wird die Animation nicht ausgeführt und die Buttons erscheinen in ihrer „normalen“ Farbe und ohne Beschneidung.
Die clip-path-Eigenschaft wird mittlerweile von allen Browsern umgesetzt. Ursprünglich konnten nur Grundfromen und die polygon()-Funktion für die Beschneidung genutzt werden. Mittlerweile können aber auch komplexere Pfade mit der path()-Funktion verwendet werden.
Ein interessanter Anwendungsfall ist ein Play-Pause-Button, in dem die zwei Balken des Pause-Zeichens sich in das dreieckige Play-Zeichen morphen.
Animation mit d:path()
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 werdne und erhält als Wert eine path()-Funktion, die wiederum die aus SVG bekannten Pfadkommandos enthält. Mittlerweile wird diese Syntax in allen Browser 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 Art und Weise 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 kontrolieren, welche Punkte mit welchen verschmelzen, um glattere und ansehnlichere Animationen zu erreichen.[7]
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. - ↑ 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
- ↑ W3C: d-property (SVG 2)
- ↑ 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
- Buchstaben morphen (CodePen)