I recently created a brick wall pattern as part of my #PetitePatterns series, a challenge where I create organic-looking patterns or textures in SVG within 560 bytes (or approximately the size of two tweets). To fit this constraint, I have gone through a journey that has taught me some radical ways of optimizing SVG patterns so that they contain as little code as possible without affecting the overall image quality.
I want to walk you through the process and show you how we can take an SVG pattern that starts at 197 bytes all the way down to a mere 44 bytes — a whopping 77.7% reduction!
Das SVG-Muster
Dies ist ein sogenanntes "Läuferverband"-Ziegelmuster. Es ist das gängigste Ziegelmuster und eines, das Sie sicher schon einmal gesehen haben: Jede Ziegelreihe ist um die halbe Ziegellänge versetzt, wodurch ein sich wiederholendes, gestaffeltes Muster entsteht. Die Anordnung ist ziemlich einfach, wodurch sich das <pattern>-Element von SVG perfekt eignet, um es im Code zu reproduzieren.
Das SVG-Element <pattern> verwendet ein vordefiniertes grafisches Objekt, das an festen Intervallen entlang der horizontalen und vertikalen Achsen wiederholt (oder "gekachelt") werden kann. Im Wesentlichen definieren wir eine rechteckige Kachel und diese wird wiederholt, um den Füllbereich zu malen.
Legen wir zunächst die Abmessungen eines Ziegels und den Abstand zwischen den Ziegeln fest. Der Einfachheit halber verwenden wir einfache, runde Zahlen: eine Breite von 100 und eine Höhe von 30 für den Ziegel und 10 für die horizontalen und vertikalen Abstände dazwischen.

Als Nächstes müssen wir unsere "Basis"-Kachel identifizieren. Und mit "Kachel" meine ich Musterkacheln, nicht physische Fliesen, nicht zu verwechseln mit den Ziegeln. Nehmen wir den hervorgehobenen Teil des obigen Bildes als unsere Musterkachel: zwei ganze Ziegel in der ersten Reihe und ein ganzer Ziegel, der in der zweiten Reihe zwischen zwei halben Ziegeln eingeklemmt ist. Beachten Sie, wie und wo die Abstände einbezogen sind, denn diese müssen in die wiederholte Musterkachel einbezogen werden.
Bei der Verwendung von <pattern> müssen wir die width und height des Musters definieren, die der Breite und Höhe der Basis-Kachel entsprechen. Um die Abmessungen zu erhalten, benötigen wir ein wenig Mathematik.
Tile Width = 2(Brick Width) + 2(Gap) = 2(100) + 2(10) = 220
Tile Height = 2(Bright Height) + 2(Gap) = 2(30) + 2(10) = 80
Okay, also ist unsere Musterkachel 220✕80 groß. Wir müssen auch das Attribut patternUnits setzen, wobei der Wert userSpaceOnUse im Wesentlichen Pixel bedeutet. Schließlich ist das Hinzufügen einer id zum Muster notwendig, damit es referenziert werden kann, wenn wir ein anderes Element damit malen.
<pattern id="p" width="220" height="80" patternUnits="userSpaceOnUse">
<!-- pattern content here -->
</pattern>
Nachdem wir nun die Abmessungen der Kachel festgelegt haben, besteht die Herausforderung darin, den Code für die Kachel so zu erstellen, dass die Grafik mit der geringstmöglichen Bytezahl gerendert wird. Das ist das, was wir am Ende erreichen wollen.

Anfängliches Markup (197 Bytes)
Der einfachste und deklarativste Ansatz, um dieses Muster zu reproduzieren, der mir einfällt, ist das Zeichnen von fünf Rechtecken. Standardmäßig ist die fill eines SVG-Elements schwarz und die stroke transparent. Das funktioniert gut für die Optimierung von SVG-Mustern, da wir diese nicht explizit im Code deklarieren müssen.
Jede Zeile im folgenden Code definiert ein Rechteck. Die width und height sind immer gesetzt, und die x- und y-Positionen werden nur gesetzt, wenn ein Rechteck vom 0-Punkt versetzt ist.
<rect width="100" height="30"/>
<rect x="110" width="100" height="30"/>
<rect y="40" width="45" height="30"/>
<rect x="55" y="40" width="100" height="30"/>
<rect x="165" y="40" width="55" height="30"/>
Die obere Reihe der Kachel enthielt zwei Ziegel voller Breite, der zweite Ziegel ist bei x="110" positioniert, was einen Abstand von 10 Pixeln vor dem Ziegel ermöglicht. Ähnlich gibt es 10 Pixel Abstand danach, da der Ziegel bei 210 Pixeln (110 + 100 = 210) auf der horizontalen Achse endet, obwohl die <pattern>-Breite 220 Pixel beträgt. Wir brauchen diesen kleinen zusätzlichen Platz; andernfalls würde der zweite Ziegel mit dem ersten Ziegel in der benachbarten Kachel verschmelzen.
Die Ziegel in der zweiten (unteren) Reihe sind versetzt, sodass die Reihe zwei halbe Ziegel und einen ganzen Ziegel enthält. In diesem Fall möchten wir, dass die halben Ziegel verschmelzen, sodass am Anfang oder Ende kein Abstand vorhanden ist, was ein nahtloses Zusammenfließen mit den Ziegeln in angrenzenden Musterkacheln ermöglicht. Beim Versetzen dieser Ziegel müssen wir auch halbe Abstände berücksichtigen, daher sind die x-Werte 55 bzw. 165.
Element-Wiederverwendung, (-43B, 154B insgesamt)
Es erscheint ineffizient, jeden Ziegel so explizit zu definieren. Gibt es keine Möglichkeit, SVG-Muster durch Wiederverwendung der Formen zu optimieren?
Ich glaube nicht, dass es weithin bekannt ist, dass SVG ein <use>-Element hat. Sie können damit ein anderes Element referenzieren und dieses referenzierte Element überall rendern, wo <use> verwendet wird. Das spart einige Bytes, da wir die Breiten und Höhen jedes Ziegels weglassen können, außer dem ersten.
Das gesagt, <use> kommt mit einem kleinen Preis. Das heißt, wir müssen eine id für das wiederzuverwendende Element hinzufügen.
<rect id="b" width="100" height="30"/>
<use href="#b" x="110"/>
<use href="#b" x="-55" y="40"/>
<use href="#b" x="55" y="40"/>
<use href="#b" x="165" y="40"/>
Die kürzeste mögliche id ist ein Zeichen, daher habe ich "b" für Ziegel gewählt. Das <use>-Element kann ähnlich wie <rect> positioniert werden, mit den Attributen x und y als Offsets. Da jeder Ziegel volle Breite hat, da wir zu <use> gewechselt haben (denken Sie daran, dass wir die Ziegel in der zweiten Reihe des Musterkachels explizit halbiert haben), müssen wir in der zweiten Reihe einen negativen x-Wert verwenden und dann sicherstellen, dass der letzte Ziegel aus der Kachel überläuft, um eine nahtlose Verbindung zwischen den Ziegeln zu gewährleisten. Das ist aber in Ordnung, da alles, was außerhalb der Musterkachel liegt, automatisch abgeschnitten wird.
Können Sie wiederholende Zeichenketten erkennen, die effizienter geschrieben werden können? Lassen Sie uns das als Nächstes angehen.
Umschreiben zu Pfad (-54B, 100B insgesamt)
<path> ist wahrscheinlich das mächtigste Element in SVG. Sie können fast jede Form mit "Befehlen" in seinem d-Attribut zeichnen. Es gibt 20 verfügbare Befehle, aber wir brauchen nur die einfachsten für Rechtecke.
Hier ist, wo ich damit gelandet bin.
<path d="M0 0h100v30h-100z
M110 0h100v30h-100
M0 40h45v30h-45z
M55 40h100v30h-100z
M165 40h55v30h-55z"/>
Ich weiß, super seltsame Zahlen und Buchstaben! Sie haben alle eine Bedeutung, natürlich. Hier ist, was in diesem speziellen Fall passiert.
M{x} {y}: Bewegt sich zu einem Punkt basierend auf Koordinaten.z: Schließt das aktuelle Segment.h{x}: Zeichnet eine horizontale Linie vom aktuellen Punkt aus, mit der Länge vonxin der Richtung, die durch das Vorzeichen vonxdefiniert wird. Kleinbuchstabexbedeutet relative Koordinate.v{y}: Zeichnet eine vertikale Linie vom aktuellen Punkt aus, mit der Länge vonyin der Richtung, die durch das Vorzeichen vonydefiniert wird. Kleinbuchstabeybedeutet relative Koordinate.
Dieses Markup ist viel kürzer als das vorherige (Zeilenumbrüche und Einrückungen dienen nur der Lesbarkeit). Und hey, wir haben die Hälfte der ursprünglichen Größe eingespart und sind bei 100 Bytes angekommen. Dennoch habe ich das Gefühl, dass das noch kleiner sein könnte...
Kachel-Revision (-38B, 62B insgesamt)
Hat unsere Musterkachel nicht wiederholende Teile? Es ist klar, dass in der ersten Reihe ein ganzer Ziegel wiederholt wird, aber was ist mit der zweiten Reihe? Es ist etwas schwieriger zu erkennen, aber wenn wir den mittleren Ziegel halbieren, wird es offensichtlich.

Nun, der mittlere Ziegel ist nicht exakt halbiert. Es gibt einen leichten Versatz, da wir auch den Abstand berücksichtigen müssen. Jedenfalls haben wir eine einfachere Basis-Musterkachel gefunden, was weniger Bytes bedeutet! Das bedeutet auch, dass wir die width unseres <pattern>-Elements von 220 auf 110 halbieren müssen.
<pattern id="p" width="110" height="80" patternUnits="userSpaceOnUse">
<!-- pattern content here -->
</pattern>
Lassen Sie uns nun sehen, wie die vereinfachte Kachel mit <path> gezeichnet wird.
<path d="M0 0h100v30h-100z
M0 40h45v30h-45z
M55 40h55v30h-55z"/>
Die Größe ist auf 62 Bytes reduziert, was bereits weniger als ein Drittel der ursprünglichen Größe ist! Aber warum aufhören, wenn es noch mehr gibt, was wir tun können!
Verkürzung von Pfadbefehlen (-9B, 53B insgesamt)
Es lohnt sich, sich etwas tiefer mit dem <path>-Element zu beschäftigen, da es weitere Hinweise zur Optimierung von SVG-Mustern liefert. Ein Missverständnis, das ich bei der Arbeit mit <path> hatte, betrifft die Funktionsweise des fill-Attributs. Nachdem ich als Kind viel mit MS Paint gespielt hatte, lernte ich, dass jede Form, die ich mit einer Volltonfarbe füllen wollte, geschlossen sein musste, d.h. keine offenen Punkte haben durfte. Andernfalls würde die Farbe aus der Form austreten und sich über alles verteilen.
In SVG ist dies jedoch nicht der Fall. Lassen Sie mich die Spezifikation selbst zitieren.
Die Fülloperation füllt offene Unterpfade, indem sie die Fülloperation so ausführt, als wäre ein zusätzlicher "closepath"-Befehl zum Pfad hinzugefügt worden, um den letzten Punkt des Unterpfads mit dem ersten Punkt des Unterpfads zu verbinden.
Das bedeutet, dass wir die Close-Path-Befehle (z) weglassen können, da die Unterpfade beim Füllen automatisch als geschlossen betrachtet werden.
Eine weitere nützliche Sache, die man über Pfadbefehle wissen sollte, ist, dass sie in Groß- und Kleinschreibungsvarianten vorkommen. Kleinbuchstaben bedeuten, dass relative Koordinaten verwendet werden; Großbuchstaben bedeuten, dass stattdessen absolute Koordinaten verwendet werden.
Bei den Befehlen H und V ist es etwas kniffliger, da sie nur eine Koordinate enthalten. Hier sind, wie ich diese beiden Befehle beschreiben würde:
H{x}: Zeichnet eine horizontale Linie vom aktuellen Punkt zur Koordinatex.V{y}: Zeichnet eine vertikale Linie vom aktuellen Punkt zur Koordinatey.
Wenn wir den ersten Ziegel in der Musterkachel zeichnen, beginnen wir bei den Koordinaten (0,0). Dann zeichnen wir eine horizontale Linie zu (100,0) und eine vertikale Linie zu (100,30), und schließlich eine horizontale Linie zu (0,30). Wir haben den Befehl h-100 in der letzten Zeile verwendet, aber das ist äquivalent zu H0, was zwei Bytes statt fünf sind. Wir können zwei ähnliche Vorkommen ersetzen und den Code unseres <path> auf Folgendes reduzieren:
<path d="M0 0h100v30H0
M0 40h45v30H0
M55 40h55v30H55"/>
Weitere 9 Bytes eingespart – wie viel kleiner können wir werden?
Brückenbildung (-5B, 48B insgesamt)
Die längsten Befehle, die uns auf dem Weg zu einem vollständig optimierten SVG-Muster im Weg stehen, sind die "move to"-Befehle, die jeweils 4, 5 und 6 Bytes beanspruchen. Eine Einschränkung, die wir haben, ist, dass
Ein Pfaddatensegment (falls vorhanden) muss mit einem "moveto"-Befehl beginnen.
Aber das ist in Ordnung. Der erste ist sowieso der kürzeste. Wenn wir die Reihen tauschen, können wir eine Pfaddefinition erstellen, bei der wir uns nur horizontal oder vertikal zwischen den Ziegeln bewegen müssen. Was wäre, wenn wir dort die Befehle h und v anstelle von M verwenden könnten?

Das obige Diagramm zeigt, wie die drei Formen mit einem einzigen Pfad gezeichnet werden können. Beachten Sie, dass wir die Tatsache nutzen, dass die fill-Operation den offenen Teil zwischen (110,0) und (0,0) automatisch schließt. Mit dieser Umordnung haben wir auch den Abstand links vom Ziegel voller Breite in der zweiten Reihe platziert. So sieht der Code aus, immer noch in eine Kachel pro Zeile aufgeteilt.
<path d="M0 0v30h50V0
h10v30h50
v10H10v30h100V0"/>
Sicherlich haben wir jetzt die absolut kleinste Lösung gefunden, da wir bei 48 Bytes angelangt sind, richtig?! Nun…
Zifferntrimming (-4B, 44B insgesamt)
Wenn Sie bei den Abmessungen etwas flexibel sein können, gibt es noch eine weitere kleine Möglichkeit, SVG-Muster zu optimieren. Wir haben mit einer Ziegelbreite von 100 Pixeln gearbeitet, das sind aber drei Bytes. Wenn wir sie auf 90 ändern, spart das ein Byte jedes Mal, wenn wir sie schreiben müssen. Ebenso haben wir einen Abstand von 10 Pixeln verwendet – aber wenn wir ihn stattdessen auf 8 ändern, sparen wir bei jeder dieser Vorkommnisse ein Byte.
<path d="M0 0v30h45V0
h8v30h45
v8H8v30h90V0"/>
Natürlich bedeutet das auch, dass wir die Musterabmessungen entsprechend anpassen müssen. Hier ist der endgültige optimierte SVG-Muster-Code.
<pattern id="p" width="98" height="76" patternUnits="userSpaceOnUse">
<path d="M0 0v30h45V0h8v30h45v8H8v30h90V0"/>
</pattern>
Die zweite Zeile im obigen Snippet – ohne die Einrückungen – ist **44 Bytes** lang. Wir sind in sechs Iterationen von 197 Bytes hierher gekommen. Das ist eine satte **77,7% Größenreduzierung**!
Ich frage mich jedoch… ist das wirklich die kleinste mögliche Größe? Haben wir alle möglichen Wege zur Optimierung von SVG-Mustern betrachtet?
Ich lade Sie ein, diesen Code weiter zu minimieren oder sogar alternative Methoden zur Optimierung von SVG-Mustern auszuprobieren. Ich würde gerne sehen, ob wir mit der Weisheit der Menge das wahre globale Minimum finden könnten!
Mehr über die Erstellung und Optimierung von SVG-Mustern
Wenn Sie mehr über die Erstellung und Optimierung von SVG-Mustern erfahren möchten, lesen Sie meinen Artikel über die Erstellung von Mustern mit SVG-Filtern. Oder wenn Sie eine Galerie mit über 60 Mustern sehen möchten, können Sie die PetitePatterns CodePen Collection ansehen. Schließlich sind Sie herzlich eingeladen, sich meine Tutorials auf YouTube anzusehen, die Ihnen helfen, tiefer in SVG-Muster einzudringen.
Vielleicht… Da vor Ihrem Zifferntrimming alle Werte durch 10 teilbar sind. Warum nicht diese Zahlen durch 10 teilen und dann das Muster um den Faktor 10 skalieren?
Das Hochskalieren könnte mehrere zusätzliche Bytes kosten, z. B.
transform="scale(2)", daher denke ich, man braucht viele Zahlen, damit es sich lohnt.Die Frage ist – ist das praktisch? Minifizierung bringt eigene Kosten mit sich, z. B. sind Änderungen daran schwierig und fehleranfällig. Tatsächlich ist der Minifizierungsprozess selbst fehleranfällig, es sei denn, er wird von einem ausgereiften automatisierten Werkzeug durchgeführt.
Ich würde nicht sagen, dass es *praktisch* ist. Für mich ist es eine Lernmöglichkeit und eine Notwendigkeit für mein Projekt. Vielleicht ist es nützlich für diejenigen, die SVG-Pfade von Hand codieren, was ich auch tue, wenn ich zum Beispiel Icons für meine Website erstelle.
Definitiv nichts, was in großem Maßstab vernünftigerweise durchgeführt werden könnte.
Mein Lieblingswerkzeug, das unterbewertet wird, SVGOMG, reduziert das Original um ~50%, unkomprimiert.
<svg><rect width="100" height="30"/><rect x="110" width="100" height="30"/>
<rect y="40" width="45" height="30"/>
<rect x="55" y="40" width="100" height="30"/>
<rect x="165" y="40" width="55" height="30"/></svg>
zu
<svg><path d="M0 0h100v30H0zM110 0h100v30H110zM0 40h45v30H0zM55 40h100v30H55zM165 40h55v30h-55z"/></svg>Ja, auf jeden Fall! Ich verwende SVGOMG für den letzten Schritt bei all meinen Mustern. Und natürlich ist es das Werkzeug der Wahl für SVG-Minifizierung im großen Maßstab.
Manchmal brauche ich aber noch etwas mehr, dann fange ich an, mit diesen verrückten Ideen zu spielen.
Nun, wenn jedes Byte zählt, könnten Sie wahrscheinlich einfache Anführungszeichen ' anstelle von doppelten " verwenden. Könnte gegen die Konvention verstoßen, sollte aber trotzdem austauschbar sein.
Doppelte Anführungszeichen sind ein einzelnes Zeichen und belegen nur ein einziges Byte.
98,76,30,45,8,30,45,8,8,30,90
Es erfordert etwas Zahlenrechnen, aber es muss einen (Gleitkomma-) Divisionswert geben, der zu 1 Byte weniger führt.
Beachten Sie auch, dass die Byte-Anzahl nicht so wichtig ist wie die GZip- oder Brotli-Komprimierung, die jeder gute Server durchführt.
Längerer Byte-Code kann für wiederholende Muster besser komprimiert werden.
Insbesondere GROSSBUCHSTABEN komprimieren schlechter, da sie in einer Datei weniger verwendet werden.
Daher könnte die Verwendung von https://yqnn.github.io/svg-path-editor/ zur Konvertierung des d-Pfads in relative Notation Bytes sparen.
Sie können die GZip-Größe mit der von mir geschriebenen Web Component überprüfen: https://dev.to/dannyengelman/file-size-web-component-because-size-does-matter-3d3k
Inkscape kann Pfade zusammenführen und vereinfachen, und Tools wie svgo und svgcleaner können verlustbehaftete Komprimierung durchführen. Beide Optionen können jedoch die *komprimierte* Größe von SVG tatsächlich erhöhen. Überprüfen Sie immer die komprimierten Größen, wenn Sie minimieren.
Eine Sache, die es wert ist, auszuprobieren, ist der Export in verschiedene SVG-Profile. Zum Beispiel exportiere ich gerne nach SVG Tiny 1.2 und bearbeite dann das SVG, um es vollständig SVG 1.1 oder SVG 2 kompatibel zu machen.
Wenn Sie den Abstand zwischen den Ziegeln etwas verringern, können Sie jeden Wert durch 5 dividieren.