SVG-Kreiszerlegung in Pfade
Veröffentlicht: 2022-03-10Dieser Artikel beginnt mit einem Geständnis: Ich codiere SVG gerne von Hand. Das ist nicht immer der Fall, aber oft genug kommt es Menschen, die meine Vorliebe nicht teilen, seltsam vor. SVG von Hand schreiben zu können, hat viele Vorteile, z. B. das Optimieren von SVGs auf eine Weise, die ein Tool nicht kann (einen Pfad in einen einfacheren Pfad oder eine einfachere Form umwandeln), oder einfach zu verstehen, wie Bibliotheken wie D3 oder Greensock funktionieren .
Vor diesem Hintergrund möchte ich Kreisformen in SVG und Dinge, die wir damit machen können, genauer betrachten, wenn wir uns über einen einfachen Kreis hinausbewegen. Warum Kreise? Nun, ich liebe Kreise. Sie sind meine Lieblingsform.
Zunächst einmal (hoffentlich haben Sie schon einmal einen einfachen Kreis in SVG gesehen), hier ist ein Stift, der einen zeigt:
Mit einem Kreis kann man viel machen: Er kann animiert und mit verschiedenen Farben versehen werden. Dennoch gibt es zwei sehr nette Dinge, die ein Kreis in SVG 1.1 nicht tun kann: Sie können kein anderes grafisches Element dazu bringen, sich entlang des Kreispfads zu bewegen (mit dem animateMotion
Element), und Sie können keinen Text entlang des Kreispfads formen (dies wird erst nach Veröffentlichung von SVG 2.0 erlaubt).
Unseren Kreis in einen Weg verwandeln
Es gibt ein kleines Online-Tool, mit dem Sie Pfade aus Kreisen erstellen können (Sie können es hier ausprobieren), aber wir werden alles von Grund auf neu erstellen, damit wir herausfinden können, was wirklich hinter den Kulissen vor sich geht.
Um einen kreisförmigen Pfad zu erstellen, werden wir tatsächlich zwei Bögen erstellen, dh Halbkreise, die den Kreis auf einem Pfad vervollständigen. Wie Sie wahrscheinlich im obigen SVG bemerkt haben, definieren die Attribute CX
, CY
und R
jeweils, wo der Kreis entlang der X- und Y-Achse gezeichnet wird, während R
den Radius des Kreises definiert. CX
und CY
bilden den Mittelpunkt des Kreises, sodass der Kreis um diesen Punkt herum gezeichnet wird.
Das Replizieren dieses Kreises könnte so aussehen:
<path d=" M (CX - R), CY a R,R 0 1,0 (R * 2),0 a R,R 0 1,0 -(R * 2),0 " />
Beachten Sie, dass CX
dasselbe ist wie das cx
Attribut des Kreises; dasselbe gilt für CY
und das cy
Attribut des Kreises sowie R
und das r
-Attribut des Kreises. Das kleine a
-Zeichen wird verwendet, um ein Segment eines Ellipsenbogens zu definieren. Sie können ein optionales Z
(oder z
) verwenden, um den Pfad zu schließen.
Der Kleinbuchstabe a
bezeichnet den Beginn eines Ellipsenbogens, der relativ zur aktuellen Position gezeichnet wird – oder in unserem speziellen Fall:
<path d=" M 25, 50 a 25,25 0 1,1 50,0 a 25,25 0 1,1 -50,0 " />
Sie können die Magie in diesem Stift sehen:
Unter dem Pfad ist ein Kreis mit einer roten Füllung verborgen. Wenn Sie mit den Werten des Pfads herumspielen, sehen Sie diesen Kreis, solange der Pfad den Kreis vollständig abdeckt (der Pfad selbst ist ein Kreis derselben Größe), und wir wissen, dass wir die Dinge richtig machen .
Eine Sache, die Sie auch wissen sollten, ist, dass Sie den a
-Befehl nicht für jeden gezeichneten Bogen wiederholen müssen, solange Sie relative Bögen zeichnen. Wenn Ihre ersten 7 Eingaben für Ihren Bogen erledigt sind, werden die zweiten 7 Eingaben für den nächsten Bogen verwendet.
Sie können dies mit dem obigen Stift ausprobieren, indem Sie das zweite a
im Pfad entfernen:
a 25,25 0 1,1 50,0 25,25 0 1,1 -50,0
Das sieht vielleicht gleich aus, aber ich lasse es lieber drin, bis ich bereit bin, eine Zeichnung fertigzustellen, und das hilft mir auch, den Überblick zu behalten, wo ich bin.
Wie dieser Pfad funktioniert
Zuerst bewegen wir uns zu einer absolut positionierten X,Y
-Koordinate im Bild. Es zeichnet dort nichts – es bewegt sich nur dorthin. Denken Sie daran, dass für ein Kreiselement CX
CY
den Mittelpunkt des Kreises bezeichnet; aber wie es beim elliptischen Bogen der Fall ist, werden die wahren CX
und CY
des Bogens aus den anderen Eigenschaften dieses Bogens berechnet.
Mit anderen Worten, wenn wir wollen, dass unser CX
bei 50
liegt und unser Radius 25
ist, dann müssen wir uns auf 50 - 25
bewegen (wenn wir natürlich von links nach rechts zeichnen). Das bedeutet, dass unser erster Bogen aus 25 X, 50 Y
gezogen wird, was dazu führt, dass unser erster Bogen 25,25 0 1,0 50,0
.
Lassen Sie uns aufschlüsseln, was der Wert 25,25 0 1,0 50,0
unseres Bogens tatsächlich bedeutet:
-
25
: Der relative X-Radius des Bogens; -
25
: Der relative Y-Radius des Bogens; -
0 1,0
: Ich werde nicht auf die drei mittleren Werte (Rotation, Large-Arc-Flag und die Sweep-Flag-Eigenschaften) eingehen, da sie im Kontext des aktuellen Beispiels nicht sehr wichtig sind, solange sie sind für beide Bögen gleich; -
50
: Die End-X-Koordinate (relativ) des Bogens; -
0
: Die End-Y-Koordinate (relativ) des Bogens.
Der zweite Bogen ist ein 25,25 0 1,0 -50,0
. Denken Sie daran, dass dieser Bogen dort zu zeichnen beginnt, wo der letzte Bogen aufgehört hat zu zeichnen. Natürlich sind der X- und der Y-Radius gleich ( 25
), aber die X-Endkoordinate ist -50
von der aktuellen.
Offensichtlich hätte dieser Kreis auf viele verschiedene Arten gezeichnet werden können. Dieser Vorgang, bei dem ein Kreis in einen Pfad umgewandelt wird, wird als Zerlegung bezeichnet. In der SVG 2-Spezifikation wird die Dekomposition eines Kreises mit 4 Bögen durchgeführt, die empfohlene Methode kann jedoch noch nicht verwendet werden, da sie derzeit von einem Feature namens segment-completing close path abhängt, das noch nicht spezifiziert wurde.
Um Ihnen zu zeigen, dass wir den Kreis auf viele Arten zeichnen können, habe ich einen kleinen Stift mit verschiedenen Beispielen vorbereitet:
Wenn Sie genauer hinschauen, sehen Sie unseren ursprünglichen Kreis zusammen mit fünf verschiedenen Beispielen, wie Sie Pfade auf diesem Kreis zeichnen können. Jeder Pfad hat ein desc
-Element, das die Verwendung von CX
-, CY
- und R
-Werten zum Erstellen des Kreises beschreibt. Das erste Beispiel ist das, das wir hier besprochen haben, während drei andere Variationen verwenden, die durch Lesen des Codes verständlich sein sollten; Die letzten Beispiele verwenden vier statt zwei Halbkreisbögen, wodurch der in der oben verlinkten SVG 2-Spezifikation beschriebene Prozess in gewisser Weise nachgebildet wird.
Die Kreise werden übereinander geschichtet, indem die natürliche Z-Indizierung von SVG verwendet wird, bei der Elemente, die später im Markup kommen, über denen platziert werden, die früher kommen.
Wenn Sie auf die kreisförmigen Pfade im Stift klicken, wird beim ersten Klick gedruckt, wie der Pfad in der Konsole aufgebaut ist, und dem Element eine Klasse hinzugefügt, sodass Sie die Strichfarbe sehen, wie der Kreis gezeichnet wird (Sie können sehen dass der erste Kreis mit einem Startkeil vom Strich gezeichnet wird). Der zweite Klick entfernt den Kreis, sodass Sie mit dem Kreis unten interagieren können.
Jeder Kreis hat eine andere Füllfarbe; Das eigentliche Kreiselement ist gelb und sagt der Konsole, dass Sie auf den Kreis geklickt haben, wenn darauf geklickt wird. Sie können den Code natürlich auch einfach lesen, da die desc
Elemente recht einfach sind.
Von einem Pfad zu einem Kreis gehen
Ich nehme an, Sie haben bemerkt, dass es zwar viele verschiedene Möglichkeiten gibt, den Kreis zu zeichnen, die verwendeten Pfade jedoch immer noch ziemlich ähnlich aussehen. Oft – insbesondere in SVG-Ausgaben aus einem Zeichenprogramm – werden Kreise durch Pfade dargestellt. Dies liegt wahrscheinlich an der Optimierung des Grafikprogrammcodes; Sobald Sie den Code zum Zeichnen eines Pfads haben, können Sie alles zeichnen, also verwenden Sie ihn einfach. Dies kann zu etwas aufgeblähten SVGs führen, über die man sich nur schwer Gedanken machen kann.
Empfohlene Lektüre : „ Tipps zum Erstellen und Exportieren besserer SVGs für das Web“ von Sara Soueidan
Nehmen wir als Beispiel das folgende SVG aus Wikipedia. Wenn Sie sich den Code für diese Datei ansehen, werden Sie sehen, dass es eine Menge Editor-Cruft gibt, sobald Sie es durch Jake Archibalds SVGOMG! (worüber Sie hier mehr lesen können), erhalten Sie so etwas wie die folgende Datei, die ziemlich optimiert wurde, aber die Kreise im Dokument werden immer noch als Pfade gerendert:
Mal sehen, ob wir angesichts dessen, was wir über die Funktionsweise von Pfaden wissen, herausfinden können, was diese Kreise sein sollten, wenn sie tatsächliche Kreiselemente wären. Der erste Pfad im Dokument ist offensichtlich kein Kreis, während die nächsten beiden es sind (zeigen nur das d
Attribut):
M39 20a19 19 0 1 1-38 0 19 19 0 1 1 38 0z
M25 20a5 5 0 1 1-10 0 5 5 0 1 1 10 0z
Denken wir also daran, dass das zweite a
weggelassen werden kann, und schreiben wir diese so um, dass sie ein wenig mehr Sinn ergeben. (Der erste Weg ist der große Kreis.)
M39 20 a19 19 0 1 1-38 0 a19 19 0 1 1 38 0z
Diese Bögen sind dann offensichtlich die folgenden:
aR R 0 1 1 - (R * 2) 0 aR R 0 1 1 (R * 2) 0
Das bedeutet, dass unser Kreisradius 19
ist, aber was sind unsere CX
und CY
Werte? Ich denke, unser M39
ist eigentlich CX + R
, was bedeutet, dass CX
20
ist und CY
auch 20
ist.
Nehmen wir an, Sie fügen nach allen Pfaden wie folgt einen Kreis hinzu:
<circle fill="none" stroke-width="1.99975" stroke="red" r="19" cx="20" cy="20" />
Sie werden sehen, dass das richtig ist und dass der rot umrandete Kreis genau den großen Kreis abdeckt. Der zweite umformulierte Kreisweg sieht so aus:
M25 20 a5 5 0 1 1-10 0 5 5 0 1 1 10 0z
Offensichtlich ist der Radius 5
, und ich wette, unsere CX
und CY
Werte sind die gleichen wie zuvor: - 20
.
Hinweis : Wenn CX = 20
, dann CX + R = 25
. Der Kreis sitzt innerhalb des größeren in der Mitte, also sollte er offensichtlich die gleichen CX
und CY
Werte haben.
Fügen Sie den folgenden Kreis am Ende der Pfade hinzu:
<circle fill="yellow" r="5" cx="20" cy="20" />
Dass dies stimmt, können Sie nun an folgendem Stift erkennen:
Jetzt, da wir wissen, was die Kreise sein sollen, können wir diese nicht benötigten Pfade entfernen und die Kreise tatsächlich erstellen – wie Sie hier sehen können:
Verwenden unseres kreisförmigen Pfads zum Umbrechen von Text
Jetzt, da wir unsere Kreise in Pfaden haben, können wir Text auf diesen Pfaden umbrechen. Unten sehen Sie einen Stift mit den gleichen Pfaden wie unser vorheriger „Alle Kreise“-Stift, aber mit Text, der um den Pfad gewickelt ist. Immer wenn Sie auf einen Pfad klicken, wird dieser Pfad gelöscht und der Text wird wie folgt auf den nächsten verfügbaren Pfad umbrochen:
Wenn Sie sich die verschiedenen Pfade ansehen, werden Sie winzige Unterschiede zwischen den einzelnen Pfaden feststellen (mehr dazu gleich), aber zuerst ist eine kleine Cross-Browser-Inkompatibilität zu sehen – besonders auffällig im ersten Pfad:
Firefox-Entwickler | |
Chrom | |
Microsoft Edge |
Der Grund, warum das beginnende „S“ von „Smashing“ in der Firefox-Lösung in diesem lustigen Winkel sitzt, ist, dass wir dort tatsächlich angefangen haben, unseren Pfad zu zeichnen (aufgrund des von uns verwendeten vR-Befehls). Dies ist in der Chrome-Version deutlicher, wo Sie den ersten tortenförmigen Keil unseres Kreises, den wir gezeichnet haben, deutlich sehen können:
Chrome folgt nicht allen Keilen, daher ist dies das Ergebnis, wenn Sie den Text in „Smashing Magazine“ ändern. |
Der Grund dafür ist, dass Chrome einen Fehler bezüglich der Vererbung des textLength
Attributs hat, das für das übergeordnete text
deklariert ist. Wenn Sie möchten, dass beide gleich aussehen, setzen Sie das textLength
Attribut sowohl auf das textPath
Element als auch auf den Text. Warum? Denn es stellt sich heraus, dass Firefox Developer den gleichen Fehler hat, wenn das textLength
Attribut nicht auf dem text
angegeben ist (dies ist seit einigen Jahren der Fall).
Microsoft Edge hat einen ganz anderen Fehler; Es kann keine Leerzeichen zwischen dem Text
und dem TextPath
Element verarbeiten. Sobald Sie Leerzeichen entfernt und das textLength
Attribut sowohl auf die text
als auch auf die textPath
Elemente gesetzt haben, sehen sie alle relativ gleich aus (mit kleinen Abweichungen aufgrund von Unterschieden in den Standardschriftarten usw.). Also drei verschiedene Fehler in drei verschiedenen Browsern – deshalb arbeiten die Leute oft lieber mit Bibliotheken!
Der folgende Stift zeigt, wie die Probleme behoben werden können:
Ich habe auch die verschiedenen Füllfarben entfernt, da dies den Textumbruch besser erkennen lässt. Das Entfernen der Füllfarben bedeutet, dass meine kleine Funktion, mit der Sie die Pfade durchlaufen und sehen können, wie sie aussehen, nicht funktioniert, es sei denn, ich füge ein Attribut pointer-events="all"
hinzu, also habe ich diese ebenfalls hinzugefügt.
Hinweis : Sie können mehr über die Gründe dafür in „Managing SVG Interaction With The Pointer Events Property“ von Tiffany B. Brown lesen.
Wir haben bereits das Wrapping des Multiarc-Pfads besprochen, also schauen wir uns jetzt die anderen an. Da wir einen Pfad haben, auf dem wir umbrechen, bewegt sich der Text immer in die gleiche Richtung.
Bild | Weg | Erläuterung |
---|---|---|
M CX, CY a R, R 0 1,0 -(R * 2), 0 a R, R 0 1,0 R * 2, 0 und verwendet die translate , um +R auf der X-Achse zu verschieben. | Die Startposition für unseren textPath (da wir ihn in keiner Weise angegeben haben) wird durch unseren ersten Endbogen -(R * 2) bestimmt, angesichts des Radius, den der Bogen selbst hat. | |
M (CX + R), CY a R,R 0 1,0 -(R * 2),0 a R,R 0 1,0 (R * 2),0 | Es gilt das Gleiche wie beim vorherigen Pfad. | |
M CX CY m -R, 0 a R,R 0 1,0 (R * 2),0 a R,R 0 1,0 -(R * 2),0 | Da wir in unserem ersten Bogen bei (R * 2 ) enden, werden wir offensichtlich an der entgegengesetzten Position beginnen. Mit anderen Worten, dieser beginnt dort, wo unsere beiden vorherigen Wege endeten. | |
M (CX - R), CY a R,R 0 1, 1 (R * 2),0 a R,R 0 1, 1 -(R * 2),0 | Diese beginnt aufgrund von (R * 2) an der gleichen Position wie die letzte, läuft aber im Uhrzeigersinn, weil wir die Eigenschaft Sweep-Flag (gelb markiert) auf 1 gesetzt haben. |
Wir haben gesehen, wie man Text auf einem einzelnen Pfad in einem Kreis umbricht. Sehen wir uns nun an, wie wir diesen Pfad in zwei Pfade aufteilen können und welche Vorteile Sie daraus ziehen können.
Unsere Wege in Teile brechen
Es gibt viele Dinge, die Sie mit dem Text in Ihrem Pfad machen können, zB stilistische Effekte mit tspan
Elementen erzielen, den Abstand des Textes festlegen oder den Text animieren. Grundsätzlich wird alles, was Sie tun, durch den Pfad selbst eingeschränkt. Aber indem wir unsere Multiarc-Pfade in einzelne Arc-Pfade aufteilen, können wir mit der Richtung unseres Textes, der Z-Indizierung verschiedener Teile unseres Textes herumspielen und komplexere Animationen erzielen.
Zuerst wollen wir ein anderes SVG-Bild verwenden, um einige der Effekte zu zeigen. Ich werde die Raute aus dem Artikel über Zeigerereignisse verwenden, die ich zuvor erwähnt habe. Lassen Sie uns zunächst zeigen, wie es aussehen wird, wenn ein kreisförmiger Text mit einem einzelnen Pfad darauf gelegt wird.
Nehmen wir an, unser Kreis ist CX 295, CY 200, R 175
. Nach der Kreispfadmethode sehen wir nun Folgendes:
M (CX - R), CY a R,R 0 1,1 (R * 2),0 a R,R 0 1,1 -(R * 2),0
Ich werde nicht über den Pfad oder die Textgröße, Füll- oder Strichfarbe sprechen. Wir sollten das inzwischen alle verstehen und in der Lage sein, es so zu gestalten, wie wir es wollen. Aber wenn wir uns den Text ansehen, können wir sofort einige Nachteile oder Einschränkungen erkennen:
- Der Text läuft alle in eine Richtung;
- Es könnte schön sein, einen Teil des Textes hinter den Amethyst zu stellen, besonders dort, wo MAGAZIN steht. Damit sich „M“ und „E“ auf dem Kreis ausrichten, muss das „A“ am seitlichen unteren Punkt des Amethysten liegen, was sich auf andere Weise unausgeglichen anfühlt. (Ich habe das Gefühl, dass das 'A' genau positioniert sein und an diesem Punkt nach unten zeigen sollte.)
Wenn wir diese Probleme beheben wollen, müssen wir unseren einzelnen Pfad in zwei Teile aufteilen. Im folgenden Stift habe ich den Pfad in zwei Pfade aufgeteilt (und sie in den defs
Bereich der SVG platziert, damit unsere textPath
s darauf verweisen können):
Angenommen, unser CX
ist 295, CY 200, R 175
, dann haben die beiden Pfade das folgende Format (für den oberen halbkreisförmigen Pfad):
M (CX - R), CY a R,R 0 1,1 (R * 2),0
Und für die Unterseite folgendes:
M (CX + R), CY a R,R 0 1,1 -(R * 2),0
Wir haben jedoch immer noch kreisförmigen Text, der sich alle in die gleiche Richtung bewegt. Um das für alles außer Edge zu beheben, müssen Sie lediglich das Attribut side="right"
zum text
hinzufügen, das den textPath
'MAGAZINE' enthält.
Den Text in eine andere Richtung gehen lassen
Wenn wir so viele Browser wie möglich unterstützen wollen, müssen wir den Pfad ändern und uns nicht auf das side
verlassen, das nicht vollständig unterstützt wird. Was wir tun können , ist, unseren oberen Halbkreispfad zu kopieren, aber den Sweep von 1
auf 0
zu ändern:
Vor:
M 120, 200 a 175,175 0 1,
M 120, 200 a 175,175 0 1,
1
350,0
350,0
Nach dem:
M 120, 200 a 175,175 0 1,
M 120, 200 a 175,175 0 1,
0
350,0
350,0
Aber unser Text wird jetzt auf dem durch das Sweep definierten inneren Kreis gezeichnet und sieht in verschiedenen Browsern nicht so schön aus. Dies bedeutet, dass wir die Position unseres Pfads verschieben müssen, um ihn mit dem „S“ von „Smashing“ auszurichten, das X am Ende des Pfads größer zu machen und einen gewissen Versatz zum Text festzulegen. Wie Sie sehen können, gibt es auch einen kleinen Textunterschied zwischen Firefox und den anderen, den wir verbessern können, indem wir das textLength
Attribut des text
erhöhen und Leerzeichen aus dem textPath
(da Firefox Whitespace offensichtlich für sinnvoll hält).
Die Lösung:
Ändern Sie den Z-Index eines Teils unseres Rundschreibens
Schließlich möchten wir, dass unser Text sowohl vor als auch hinter dem Amethyst steht. Nun, das ist einfach. Denken Sie daran, dass die Z-Indizierung von Elementen in SVG davon abhängt, wo sie sich im Markup befinden? Wenn wir also zwei Elemente haben, wird Element 1
hinter Element 2
gezeichnet. Als nächstes müssen wir nur noch ein text
in unserem SVG-Markup nach oben verschieben, damit es vor dem Amethyst gezeichnet wird.
Sie können das Ergebnis unten sehen, in dem Teile des Wortes „MAGAZIN“ durch die untere Spitze des Amethysten verdeckt sind.
Wenn Sie sich das Markup ansehen, können Sie sehen, dass der untere Halbkreis des Textes vor den Pfad verschoben wurde, der den Amethyst zeichnet.
Animieren der Teile unseres Kreises
Jetzt haben wir also die Möglichkeit, kreisförmigen Text zu erstellen, indem wir die Richtung der Teile unseres Textes vollständig steuern, indem wir den Text in zwei Halbkreise setzen. Dies kann natürlich auch ausgenutzt werden, um Animationen des Textes zu erstellen. Das Erstellen von browserübergreifenden SVG-Animationen ist wirklich das Thema eines anderen Artikels (oder vieler weiterer Artikel). Diese Beispiele funktionieren nur in Chrome und Firefox, da die SMIL-Animationssyntax anstelle von CSS-Keyframes oder Tools wie Greensock verwendet wird. Aber es gibt einen guten Indikator für die Effekte, die Sie durch Animieren des zerlegten Kreises erzielen können.
Nehmen Sie folgenden Stift:
Bitte drücken Sie die „Rerun“-Taste auf dem Codepen, um die Animation in Aktion zu sehen. Die beiden Teile unseres Rundtextes beginnen gleichzeitig mit der Animation, haben aber eine unterschiedliche Dauer, sodass sie zu unterschiedlichen Zeiten enden. Da wir das textLength
Attribut animieren, haben wir zwei animate
-Direktiven unter jeden Text gesetzt – eine für das text
(damit Firefox funktioniert) und eine für das textpath
Element (damit Chrome funktioniert).
Fazit
In diesem Artikel haben wir gesehen, wie man einen Kreis in einen Pfad und wieder zurück verwandelt, um besser zu verstehen, wann wir einen Pfad optimieren müssen und wann nicht. Wir haben gesehen, wie das Umwandeln des Kreises in einen Pfad uns die Freiheit gibt, den Text auf dem kreisförmigen Pfad zu platzieren, aber auch, wie wir den kreisförmigen Pfad weiter in Halbkreise aufteilen und eine vollständigere Kontrolle über die Richtung und Animation der Bestandteile unseres kreisförmigen Textes erhalten .
Weiterführende Literatur zu SmashingMag:
- Responsive SVG überdenken
- Animieren von SVG-Dateien mit SVGator
- SVGs mit CSS gestalten und animieren
- Verwalten der SVG-Interaktion mit der Pointer Events-Eigenschaft