Muster mit SVG Filtern erstellen

Avatar of Bence Szabó
Bence Szabó am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Jahrelang war mein Problem, dass ich in CSS kein annähernd natürlich aussehendes Muster erstellen konnte. Ich meine, manchmal brauche ich einfach eine Holztextur. Die einzige produktionsfreundliche Lösung, die ich kannte, war die Verwendung eines externen Bildes, aber externe Bilder sind eine zusätzliche Abhängigkeit und führen neue Komplexität ein.

Ich weiß jetzt, dass ein Großteil dieser Probleme mit ein paar Zeilen SVG gelöst werden könnte.

Kurzfassung: Bring mich zur Galerie!

Es gibt eine Filter-Primitive in SVG namens <feTurbulence>. Sie ist insofern besonders, als dass sie kein Eingabebild benötigt – die Filter-Primitive selbst *generiert* ein Bild. Sie erzeugt sogenannten Perlin Noise, eine Art von Rauschgradient. Perlin Noise wird stark in computergenerierten Grafiken zur Erstellung aller Arten von Texturen verwendet. <feTurbulence> bietet Optionen zur Erstellung mehrerer Arten von Rauschtexturen und Millionen von Variationen pro Typ.

All dies wird mit <feTurbulence> generiert.

„Na und?“, könnten Sie fragen. Diese Texturen sind zwar verrauscht, enthalten aber auch versteckte Muster, die wir durch die Kombination mit anderen Filtern aufdecken können! Darauf werden wir uns nun stürzen.

SVG-Filter erstellen

Ein benutzerdefinierter Filter besteht typischerweise aus mehreren verknüpften Filter-Primitiven, um ein gewünschtes Ergebnis zu erzielen. In SVG können wir dies deklarativ mit dem <filter>-Element und einer Reihe von <fe{PrimitiveName}>-Elementen beschreiben. Ein deklarierter Filter kann dann auf ein renderbares Element – wie <rect>, <circle>, <path>, <text> usw. – angewendet werden, indem die id des Filters referenziert wird. Der folgende Schnipsel zeigt einen leeren Filter mit der ID coolEffect, der auf ein <rect> über die gesamte Breite und Höhe angewendet wird.

<svg xmlns="http://www.w3.org/2000/svg">
  <filter id="coolEffect">
    <!-- Filter primitives will be written here -->
  </filter>
  <rect width="100%" height="100%" filter="url(#coolEffect)"/>
</svg>

SVG bietet mehr als ein Dutzend verschiedener Filter-Primitiven, aber beginnen wir mit einer relativ einfachen: <feFlood>. Sie tut genau das, was ihr Name sagt: Sie flutet einen Zielbereich. (Auch diese benötigt kein Eingabebild.) Der Zielbereich ist technisch gesehen die Unterregion der Filter-Primitive innerhalb der Region des <filter>-Elements.

Sowohl die Filterregion als auch die Unterregion der Filter-Primitive können angepasst werden. Wir verwenden in diesem Artikel jedoch die Standardwerte, die praktisch den gesamten Bereich unseres Rechtecks abdecken. Der folgende Schnipsel macht unser Rechteck rot und halbtransparent, indem die Attribute flood-color (red) und flood-opacity (0.5) gesetzt werden.

<svg xmlns="http://www.w3.org/2000/svg">
  <filter id="coolEffect">
    <feFlood flood-color="red" flood-opacity="0.5"/>
  </filter>
  <rect width="100%" height="100%" filter="url(#coolEffect)"/>
</svg>
A solid light red rectangle that is wider what it is tall.
Ein halbtransparentes rotes Rechteck erscheint auf einem weißen Hintergrund hellrot und auf einem schwarzen Hintergrund dunkelrot , da die Deckkraft auf 0,5 gesetzt ist.

Betrachten wir nun die <feBlend>-Primitive. Sie wird zum Mischen mehrerer Eingaben verwendet. Eine unserer Eingaben kann SourceGraphic sein, ein Schlüsselwort, das die ursprüngliche Grafik repräsentiert, auf die der Filter angewendet wird.

Unsere ursprüngliche Grafik ist ein schwarzes Rechteck – das liegt daran, dass wir für das <rect> keinen fill angegeben haben und die Standardfüllfarbe schwarz ist. Unsere andere Eingabe ist das Ergebnis der <feFlood>-Primitive. Wie Sie unten sehen können, haben wir das Attribut result zu <feFlood> hinzugefügt, um seine Ausgabe zu benennen. Wir referenzieren diese Ausgabe in <feBlend> mit dem Attribut in und SourceGraphic mit dem Attribut in2.

Der Standard-Blendmodus ist normal und die Reihenfolge der Eingaben spielt eine Rolle. Ich würde unsere Blendoperation so beschreiben, dass das halbtransparente rote Rechteck über das schwarze Rechteck gelegt wird.

<svg xmlns="http://www.w3.org/2000/svg">
  <filter id="coolEffect">
    <feFlood flood-color="red" flood-opacity="0.5" result="flood"/>
    <feBlend in="flood" in2="SourceGraphic"/>
  </filter>
  <rect width="100%" height="100%" filter="url(#coolEffect)"/>
</svg>
A solid dark red rectangle that is wider what it is tall.
Nun ist unser Rechteck dunkelrot, unabhängig von der Farbe des Hintergrunds . Das liegt daran, dass wir unser halbtransparentes rotes <feFlood> auf das schwarze <rect> gestapelt und die beiden mit <feBlend> gemischt haben. Wir haben das Ergebnis von <feFlood> an <feBlend> *verknüpft*.

Das Verknüpfen von Filter-Primitiven ist eine ziemlich häufige Operation und glücklicherweise gibt es nützliche Standardeinstellungen, die standardisiert wurden. In unserem obigen Beispiel hätten wir das Attribut result in <feFlood> sowie das Attribut in in <feBlend> weglassen können, da jeder nachfolgende Filter das Ergebnis des vorherigen Filters als seine Eingabe verwendet. Wir werden diese Abkürzung in diesem Artikel recht häufig verwenden.

Zufällige Muster mit feTurbulence generieren

<feTurbulence> hat einige Attribute, die das erzeugte Rauschmuster bestimmen. Gehen wir diese einzeln durch.

baseFrequency

Dies ist das wichtigste Attribut, da es zur Erstellung eines Musters erforderlich ist. Es akzeptiert einen oder zwei numerische Werte. Die Angabe von zwei Zahlen definiert die Frequenz entlang der x- und y-Achse. Wenn nur eine Zahl angegeben wird, definiert sie die Frequenz auf beiden Achsen. Ein sinnvoller Wertebereich liegt zwischen 0.001 und 1, wobei ein niedriger Wert zu großen "Merkmalen" und ein hoher Wert zu kleineren "Merkmalen" führt. Je größer der Unterschied zwischen den x- und y-Frequenzen ist, desto mehr wird das Muster "gestreckt".

<svg xmlns="http://www.w3.org/2000/svg">
  <filter id="coolEffect">
    <feTurbulence baseFrequency="0.001 1"/>
  </filter>
  <rect width="100%" height="100%" filter="url(#coolEffect)"/>
</svg>
Die baseFrequency-Werte in der oberen Reihe , von links nach rechts: 0.010.11.  Untere Reihe: 0.01 0.1, 0.1 0.010.001 1.

type

Das Attribut type akzeptiert einen von zwei Werten: turbulence (Standard) oder fractalNoise, was ich typischerweise verwende. fractalNoise erzeugt die gleiche Art von Muster in den Rot-, Grün-, Blau- und Alpha-Kanälen (RGBA), während turbulence im Alpha-Kanal anders ist als in RGB. Ich finde es schwer, den Unterschied zu beschreiben, aber es ist viel einfacher, ihn beim Vergleichen der visuellen Ergebnisse zu sehen.

<svg xmlns="http://www.w3.org/2000/svg">
  <filter id="coolEffect">
    <feTurbulence baseFrequency="0.1" type="fractalNoise"/>
  </filter>
  <rect width="100%" height="100%" filter="url(#coolEffect)"/>
</svg>
Two squares side-by-side filled with pastel patterns. The left square is sharper than the right square, which is blurry.
Der turbulence-Typ (links) im Vergleich zum fractalNoise-Typ (rechts)

numOctaves

Das Konzept der Oktaven ist Ihnen vielleicht aus der Musik oder Physik bekannt. Eine hohe Oktave verdoppelt die Frequenz. Und bei <feTurbulence> in SVG definiert das Attribut numOctaves die Anzahl der Oktaven, die über die baseFrequency gerendert werden.

Der Standardwert für numOctaves ist 1, was bedeutet, dass das Rauschen mit der Basisfrequenz gerendert wird. Jede zusätzliche Oktave verdoppelt die Frequenz und halbiert die Amplitude. Je höher diese Zahl geht, desto weniger sichtbar wird ihre Wirkung sein. Außerdem bedeuten mehr Oktaven mehr Berechnungen, was die Leistung beeinträchtigen kann. Ich verwende typischerweise Werte zwischen 1 und 5 und setze sie nur zur Verfeinerung eines Musters ein.

<svg xmlns="http://www.w3.org/2000/svg">
  <filter id="coolEffect">
    <feTurbulence baseFrequency="0.1" type="fractalNoise" numOctaves="2"/>
  </filter>
  <rect width="100%" height="100%" filter="url(#coolEffect)"/>
</svg>
Vergleich der numOctaves-Werte: 1 (links), 2 (Mitte) und 5 (rechts)

seed

Das Attribut seed erzeugt verschiedene Instanzen von Rauschen und dient als Startzahl für den Rauschgenerator, der im Hintergrund Pseudozufallszahlen erzeugt. Wenn der Seed-Wert definiert ist, erscheint eine andere Rauschinstanz, aber mit denselben Eigenschaften. Sein Standardwert ist 0 und positive ganze Zahlen werden interpretiert (obwohl 0 und 1 als derselbe Seed gelten). Gleitkommazahlen werden abgeschnitten.

Dieses Attribut eignet sich am besten, um einem Muster eine einzigartige Note zu verleihen. Zum Beispiel kann beim Besuch einer Seite ein zufälliger Seed generiert werden, sodass jeder Besucher ein leicht unterschiedliches Muster erhält. Ein praktischer Wertebereich für die Generierung zufälliger Seeds ist von 0 bis 9999999 aufgrund einiger technischer Details und Gleitkommazahlen mit einfacher Genauigkeit. Aber immerhin sind das 10 *Millionen* verschiedene Instanzen, was hoffentlich die meisten Fälle abdeckt.

<svg xmlns="http://www.w3.org/2000/svg">
  <filter id="coolEffect">
    <feTurbulence baseFrequency="0.1" type="fractalNoise" numOctaves="2" seed="7329663"/>
  </filter>
  <rect width="100%" height="100%" filter="url(#coolEffect)"/>
</svg>
Vergleich der seed-Werte: 1 (links), 2 (Mitte) und 7329663 (rechts)

stitchTiles

Wir können ein Muster genauso kacheln, wie wir background-repeat: repeat in CSS verwenden können! Alles, was wir brauchen, ist das Attribut stitchTiles, das einen von zwei Schlüsselwörtern akzeptiert: noStitch und stitch, wobei noStitch der Standardwert ist. stitch wiederholt das Muster nahtlos entlang beider Achsen.

Two long rectangles with blurry color patterns stacked one on top of the other. The top rectangle is split into six smaller rectangles, carrying the same pattern. The bottom rectangle is a single pattern.
Vergleich von noStitch (oben) mit stitch (unten)

Beachten Sie, dass <feTurbulence> auch Rauschen im Alpha-Kanal erzeugt, was bedeutet, dass die Bilder halbtransparent und nicht vollständig opak sind.

Betrachten wir eine Reihe von fantastischen Mustern, die mit SVG-Filtern erstellt wurden, und finden wir heraus, wie sie funktionieren!

Sternenhimmel

Dieses Muster besteht aus zwei verketteten Filtereffekten auf einem Rechteck voller Breite und Höhe. <feTurbulence> ist der erste Filter, der für die Rauschgenerierung verantwortlich ist. <feColorMatrix> ist der zweite Filtereffekt, der das Eingabebild Pixel für Pixel verändert. Wir können genau bestimmen, wie jeder Ausgabekanäle anhand einer Konstante und aller Eingabekanäle innerhalb eines Pixels sein sollte. Die Formel pro Kanal sieht so aus

O =w_RR_i+w_GG_i+w_BB_i+w_AA_i+w_C

  • O ist der Wert des Ausgabekanals
  • R_i, G_i, B_i, A_i sind die Eingangskanalwerte
  • w_R, w_G, w_B, w_A, w_C sind die Gewichte

Wir können also zum Beispiel eine Formel für den Rotkanal schreiben, die nur den Grünkanal berücksichtigt, indem wir w_G auf 1 setzen und die anderen Gewichte auf 0. Wir können ähnliche Formeln für die Grün- und Blaukanäle schreiben, die jeweils nur den Blau- bzw. Rotkanal berücksichtigen. Für den Alpha-Kanal können wir w_C (die Konstante) auf 1 und die anderen Gewichte auf 0 setzen, um ein vollständig opakes Bild zu erzeugen. Diese vier Formeln führen eine Farbtonrotation durch.

Die Formeln können auch als Matrixmultiplikation geschrieben werden, was der Ursprung des Namens <feColorMatrix> ist. Obwohl <feColorMatrix> ohne Verständnis von Matrixoperationen verwendet werden kann, müssen wir bedenken, dass unsere 4x5-Matrix die 4x5-Gewichte der vier Formeln sind.

\begin{bmatrix} w_{RR} & w_{GR} & w_{BR} & w_{AR} & w_{CR}\\ w_{RG} & w_{GG} & w_{BG} & w_{AG} & w_{CG} \\ w_{RB} & w_{GB} & w_{BB} & w_{AB} & w_{CB} \\ w_{RA} & w_{GA} & w_{BA} & w_{AA} & w_{CA} \end{bmatrix}
  • w_{RR} ist das Gewicht des Beitrags des Rotkanals zum Rotkanal.
  • w_{RG} ist das Gewicht des Beitrags des Rotkanals zum Grünkanal.
  • w_{GR} ist das Gewicht des Beitrags des Grünkanals zum Rotkanal.
  • w_{GG} ist das Gewicht des Beitrags des Grünkanals zum Grünkanal.
  • Die Beschreibung der restlichen 16 Gewichte wird der Kürze halber weggelassen.

Die oben erwähnte Farbtonrotation wird so geschrieben:

\begin{bmatrix} 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 \\ 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & \end{bmatrix}

Es ist wichtig zu beachten, dass die RGBA-Werte Gleitkommazahlen im Bereich von 0 bis 1 (einschließlich) sind (anstelle von ganzen Zahlen im Bereich von 0 bis 255, wie Sie es vielleicht erwarten würden). Die Gewichte können beliebige Gleitkommazahlen sein, obwohl am Ende der Berechnungen alles unter 0 auf 0 geklemmt wird und alles über 1 auf 1 geklemmt wird. Das Sternenhimmel-Muster beruht auf diesem Klemmen, da seine Matrix diese ist

\begin{bmatrix} 0 & 0 & 0 & 9 & -4 \\ 0 & 0 & 0 & 9 & -4 \\ 0 & 0 & 0 & 9 & -4 \\ 0 & 0 & 0 & 0 & 1 \end{bmatrix}
Die durch <feColorMatrix> beschriebene Transferfunktion für die R-, G- und B-Kanäle. Die Eingabe ist immer Alpha.

Wir verwenden die gleiche Formel für die RGB-Kanäle, was bedeutet, dass wir ein Graustufenbild erzeugen. Die Formel multipliziert den Wert des Alpha-Kanals mit neun und zieht dann vier davon ab. Denken Sie daran, dass selbst die Alpha-Werte in der Ausgabe von <feTurbulence> variieren. Die meisten Ergebniswerte liegen nicht im Bereich von 0 bis 1; daher werden sie geklemmt. Unser Bild ist also meistens entweder schwarz oder weiß – schwarz ist der Himmel und weiß sind die hellsten Sterne; die verbleibenden wenigen Werte dazwischen sind schwache Sterne. Wir setzen den Alpha-Kanal in der vierten Zeile auf eine Konstante von 1, was bedeutet, dass das Bild vollständig opak ist.

Kiefernholz

Dieser Code unterscheidet sich kaum von dem, was wir gerade im Sternenhimmel gesehen haben. Es handelt sich wirklich nur um eine Rauschgenerierung und eine Farbmatrix-Transformation. Ein typisches Holzmuster hat Merkmale, die in einer Dimension länger sind als in der anderen. Um diesen Effekt nachzuahmen, erzeugen wir mit <feTurbulence> "gestrecktes" Rauschen, indem wir baseFrequency="0.1 0.01" setzen. Außerdem setzen wir type="fractalNoise".

Mit <feColorMatrix> färben wir unser längliches Muster einfach neu ein. Und wieder wird der Alpha-Kanal zur Varianz-Eingabe verwendet. Dieses Mal verschieben wir jedoch die RGB-Kanäle um konstante Gewichte, die größer sind als die für den Alpha-Kanal angewendeten Gewichte. Dies stellt sicher, dass alle Pixel des Bildes innerhalb eines bestimmten Farbbereichs bleiben. Das Finden des besten Farbbereichs erfordert ein wenig Spielen mit den Werten.

\begin{bmatrix} 0 & 0 & 0 & .11 & .69 \\ 0 & 0 & 0 & .09 & .38 \\ 0 & 0 & 0 & .08 & .14 \\ 0 & 0 & 0 & 0 & 1 \end{bmatrix}
Abbildung von Alpha-Werten auf Farben mit <feColorMatrix> Obwohl extrem subtil , ist der zweite Balken ein Gradient.

Es ist wichtig zu verstehen, dass die Matrix standardmäßig im linearisierten RGB-Farbraum arbeitet. Die Farbe Lila (#800080) wird beispielsweise durch die Werte R=0.216, G=0 und B=0.216 dargestellt. Das mag zunächst seltsam erscheinen, aber es gibt einen guten Grund, für einige Transformationen linearisiertes RGB zu verwenden. Dieser Artikel liefert eine gute Antwort auf das *Warum*, und dieser Artikel ist großartig, um sich mit dem *Wie* zu befassen.

Am Ende bedeutet dies, dass wir unsere üblichen #RRGGBB-Werte in den linearisierten RGB-Raum konvertieren müssen. Ich habe dieses Werkzeug für Farbräume verwendet, um dies zu tun. Geben Sie die RGB-Werte in die erste Zeile ein und verwenden Sie dann die Werte aus der dritten Zeile. Im Fall unseres Lila-Beispiels würden wir R=128, G=0, B=128 in die erste Zeile eingeben und auf die Schaltfläche sRGB8 klicken, um die linearisierten Werte in der dritten Zeile zu erhalten.

Wenn wir unsere Werte richtig wählen und die Konvertierungen korrekt durchführen, erhalten wir etwas, das den Farben von Kiefernholz ähnelt.

Dalmatinerflecken

Dieses Beispiel würzt die Sache ein wenig, indem <feComponentTransfer>-Filter eingeführt wird. Dieser Effekt ermöglicht es uns, benutzerdefinierte Transferfunktionen pro Farbkanal (auch Farbkomponente genannt) zu definieren. Wir definieren in dieser Demo nur eine benutzerdefinierte Transferfunktion für den Alpha-Kanal und lassen die anderen Kanäle undefiniert (was bedeutet, dass die Identitätsfunktion angewendet wird). Wir verwenden den discrete-Typ, um eine Schrittfunktion festzulegen. Die Schritte werden durch durch Leerzeichen getrennte numerische Werte im Attribut tableValues beschrieben. tableValues steuert die Anzahl der Schritte und die Höhe jedes Schritts.

Betrachten wir Beispiele, in denen wir mit dem Wert tableValues experimentieren. Unser Ziel ist es, ein "fleckiges" Muster aus dem Rauschen zu erstellen. Hier ist, was wir wissen:

  • tableValues="1" überträgt jeden Wert auf 1.
  • tableValues="0" überträgt jeden Wert auf 0.
  • tableValues="0 1" überträgt Werte unter 0.5 auf 0 und Werte von 0.5 bis 1.
  • tableValues="1 0" überträgt Werte unter 0.5 auf 1 und Werte von 0.5 auf 0.
Drei einfache Schrittfunktionen. Die dritte (rechte) zeigt, was bei Dalmatinerflecken verwendet wird.

Es lohnt sich, mit diesem Attribut zu experimentieren, um seine Fähigkeiten und die Qualität unseres Rauschens besser zu verstehen. Nach einigen Experimenten gelangen wir zu tableValues="0 1 0", was mittleren Werten 1 und anderen 0 zuweist.

Der letzte Filtereffekt in diesem Beispiel ist <feColorMatrix>, der zum erneuten Einfärben des Musters verwendet wird. Genauer gesagt, er macht die transparenten Teile (Alpha = 0) schwarz und die opaken Teile (Alpha = 1) weiß.

Schließlich verfeinern wir das Muster mit <feTurbulence>. Das Setzen von numOctaves="2" hilft, die Flecken etwas "gezackter" zu machen und längliche Flecken zu reduzieren. Die baseFrequency="0.06" legt im Grunde eine Zoomstufe fest, die meiner Meinung nach für dieses Muster am besten geeignet ist.

ERDL-Tarnung

Das ERDL-Muster wurde zur Tarnung von Militärpersonal, Ausrüstung und Installationen entwickelt. In den letzten Jahrzehnten fand es seinen Weg in die Kleidung. Das Muster besteht aus vier Farben: ein dunkleres Grün für den Hintergrund, Braun für die Formen, ein gelblich-grün für Flecken und schwarz als kleine Kleckse eingestreut.

Ähnlich wie im Dalmatinerflecken-Beispiel verketten wir <feComponentTransfer> mit dem Rauschen – allerdings sind diesmal die discrete-Funktionen für die RGB-Kanäle definiert.

Stellen Sie sich vor, die RGBA-Kanäle sind vier Schichten des Bildes. Wir erstellen Kleckse in drei Schichten, indem wir Einzelschrittfunktionen definieren. Der Schritt beginnt für jede Funktion an unterschiedlichen Positionen, wodurch für jede Schicht eine unterschiedliche Anzahl von Klecksen entsteht. Die Schnitte für Rot, Grün und Blau betragen 66,67 %, 60 % bzw. 50 %.

<feFuncR type="discrete" tableValues="0 0 0 0 1 1"/>
<feFuncG type="discrete" tableValues="0 0 0 1 1"/>
<feFuncB type="discrete" tableValues="0 1"/>

An diesem Punkt überlappen sich die Kleckse auf jeder Schicht an einigen Stellen, was zu unerwünschten Farben führt. Diese anderen Farben erschweren die Umwandlung unseres Musters in eine ERDL-Tarnung, also beseitigen wir sie.

  • Für Rot definieren wir die Identitätsfunktion.
  • Für Grün ist unser Ausgangspunkt die Identitätsfunktion, aber wir subtrahieren Rot davon.
  • Für Blau ist auch unser Ausgangspunkt die Identitätsfunktion, aber wir subtrahieren Rot und Grün davon.
\begin{bmatrix} 1 & 0 & 0 & 0 & 0 \\ -1 & 1 & 0 & 0 & 0 \\ -1 & -1 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 \end{bmatrix}

Diese Regeln bedeuten, dass Rot dort bleibt, wo Rot und Grün und/oder Blau sich einst überlappten; Grün bleibt dort, wo sich Grün und Blau überlappten. Das resultierende Bild enthält vier Arten von Pixeln: Rot, Grün, Blau oder Schwarz.

Die zweite verkettete <feColorMatrix> färbt alles neu ein.

  • Die schwarzen Teile werden mit den konstanten Gewichten dunkelgrün gefärbt.
  • Die roten Teile werden durch Negation der konstanten Gewichte schwarz gefärbt.
  • Die grünen Teile werden durch die zusätzlichen Gewichte aus dem Grünkanal gelbgrün gefärbt.
  • Die blauen Teile werden durch die zusätzlichen Gewichte aus dem Blaukanal braun gefärbt.

Inselgruppe

Dieses Beispiel ist im Grunde eine Höhenkarte. Es ist ziemlich einfach, eine realistisch aussehende Höhenkarte mit <feTurbulence> zu erstellen – wir müssen uns nur auf einen Farbkanal konzentrieren, und den haben wir bereits. Konzentrieren wir uns auf den Rotkanal. Mit Hilfe von <feColorMatrix> wandeln wir das farbige Rauschen in eine Graustufen-Höhenkarte um, indem wir die Grün- und Blaukanäle mit dem Wert des Rotkanals überschreiben.

\begin{bmatrix} 1 & 0 & 0 & 0 & 0 \\ 1 & 0 & 0 & 0 & 0 \\ 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 \end{bmatrix}

Jetzt können wir uns für jeden Farbkanal pro Pixel auf denselben Wert verlassen. Dies erleichtert das Einfärben unseres Bildes Pegel für Pegel mit Hilfe von <feComponentTransfer>, obwohl wir dieses Mal einen table-Typ von Funktion verwenden. table ist ähnlich wie discrete, aber jeder Schritt ist ein Rampenanstieg zum nächsten Schritt. Dies ermöglicht einen viel sanfteren Übergang zwischen den Pegeln.

Die RGB-Transferfunktionen, die in <feComponentTransfer> definiert sind

Die Anzahl der tableValues bestimmt, wie viele Rampen in der Transferfunktion vorhanden sind. Wir müssen zwei Dinge berücksichtigen, um die optimale Anzahl von Rampen zu finden. Eine ist die **Intensitätsverteilung** im Bild. Die verschiedenen Intensitäten sind ungleich verteilt, obwohl die Rampenbreiten immer gleich sind. Das andere ist die **Anzahl der Pegel**, die wir sehen möchten. Und natürlich dürfen wir nicht vergessen, dass wir uns im linearisierten RGB-Raum befinden. Wir könnten uns mit der Mathematik dahinter auseinandersetzen, aber es ist viel einfacher, einfach zu spielen und die richtigen Werte zu finden.

Abbildung von Graustufen auf Farben mit <feComponentTransfer>

Wir verwenden tiefblau und aquamarin von den niedrigsten Intensitäten bis etwa zur Mitte, um das Wasser darzustellen. Dann verwenden wir ein paar Gelbtöne für die sandigen Teile. Schließlich erzeugen Grün und Dunkelgrün bei den höchsten Intensitäten den Wald.


Wir haben das seed-Attribut in keinem dieser Beispiele gesehen, aber ich lade Sie ein, es auszuprobieren, indem Sie es hinzufügen. Denken Sie sich eine Zufallszahl zwischen 1 und 10 Millionen aus, und verwenden Sie dann diese Zahl als Wert für das Attribut seed in <feTurbulence>, z. B. <feTurbulence seed="3761593" ... >

Jetzt haben Sie Ihre eigene Variation des Musters!

Produktionseinsatz

Bisher haben wir uns eine Reihe von coolen SVG-Mustern und deren Herstellung angesehen. Vieles von dem, was wir gesehen haben, sind tolle Proof-of-Concepts, aber der wirkliche Vorteil besteht darin, die Muster verantwortungsbewusst in der Produktion einsetzen zu können.

Meiner Meinung nach gibt es drei grundlegende Wege, aus denen man wählen kann.

Methode 1: Verwendung eines Inline-Data-URIs in CSS oder HTML

Meine bevorzugte Methode zur Verwendung von SVGs ist das Inlining, vorausgesetzt, sie sind klein genug. Für mich bedeutet „klein genug“ ein paar Kilobyte oder weniger, aber es hängt wirklich vom spezifischen Anwendungsfall ab. Der Vorteil des Inlinings ist, dass das Bild garantiert in Ihrer CSS- oder HTML-Datei vorhanden ist, was bedeutet, dass nicht darauf gewartet werden muss, bis es heruntergeladen ist.

Der Nachteil ist, dass der Markup kodiert werden muss. Glücklicherweise gibt es großartige Tools, die genau für diesen Zweck entwickelt wurden. Yoksel's URL-Encoder für SVG ist ein solches Tool, das eine UI zum Kopieren und Einfügen bietet, um den Code zu generieren. Wenn Sie nach einem programmatischen Ansatz suchen – z. B. als Teil eines Build-Prozesses – empfehle ich, sich mini-svg-data-uri anzusehen. Ich habe es persönlich nicht verwendet, aber es scheint ziemlich beliebt zu sein.

Unabhängig vom Ansatz wird der kodierte Data-URI direkt in Ihr CSS oder HTML (oder sogar JavaScript) eingefügt. CSS ist aufgrund seiner Wiederverwendbarkeit besser, aber HTML hat eine minimale Lieferzeit. Wenn Sie eine serverseitige Rendering-Technik verwenden, können Sie auch einen randomisierten seed-Wert in <feTurbulence> innerhalb des Data-URIs einfügen, um für jeden Benutzer eine einzigartige Variation anzuzeigen.

Hier ist das Sternenhimmel-Beispiel, das als Hintergrundbild mit seinem Inline-Data-URI in CSS verwendet wird

.your-selector {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='filter'%3E%3CfeTurbulence baseFrequency='0.2'/%3E%3CfeColorMatrix values='0 0 0 9 -4 0 0 0 9 -4 0 0 0 9 -4 0 0 0 0 1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23filter)'/%3E%3C/svg%3E%0A");
}

Und so sieht es aus, wenn es als <img> in HTML verwendet wird

<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='filter'%3E%3CfeTurbulence baseFrequency='0.2'/%3E%3CfeColorMatrix values='0 0 0 9 -4 0 0 0 9 -4 0 0 0 9 -4 0 0 0 0 1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23filter)'/%3E%3C/svg%3E%0A"/>

Methode 2: Verwendung von SVG-Markup in HTML

Wir können den SVG-Code selbst einfach in HTML einfügen. Hier ist das Markup für den Sternenhimmel, das in eine HTML-Datei eingefügt werden kann

<div>
  <svg xmlns="http://www.w3.org/2000/svg">
    <filter id="filter">
      <feTurbulence baseFrequency="0.2"/>
      <feColorMatrix values="0 0 0 9 -4
                             0 0 0 9 -4
                             0 0 0 9 -4
                             0 0 0 0 1"/>
    </filter>
    <rect width="100%" height="100%" filter="url(#filter)"/>
  </svg>
</div>

Es ist ein super einfacher Ansatz, aber er hat einen erheblichen Nachteil – insbesondere bei den Beispielen, die wir gesehen haben.

Dieser Nachteil? ID-Kollision.

Beachten Sie, dass das SVG-Markup eine #filter ID verwendet. Stellen Sie sich vor, Sie fügen die anderen Beispiele in dieselbe HTML-Datei ein. Wenn diese ebenfalls eine #filter ID verwenden, führt dies zu ID-Kollisionen, bei denen die erste Instanz die anderen überschreibt.

Persönlich würde ich diese Technik nur in handgeschriebenen Seiten verwenden, bei denen der Geltungsbereich klein genug ist, um sich aller eingebundenen SVGs und ihrer IDs bewusst zu sein. Es gibt die Option, während eines Builds eindeutige IDs zu generieren, aber das ist eine ganz andere Geschichte.

Methode 3: Verwendung eines eigenständigen SVG

Dies ist der „klassische“ Weg, SVG zu verwenden. Tatsächlich ist es genau wie die Verwendung jeder anderen Bilddatei. Laden Sie die SVG-Datei auf einen Server hoch und verwenden Sie dann die URL in einem HTML-<img>-Tag oder irgendwo in CSS als Hintergrundbild.

Gehen wir also zurück zum Beispiel mit dem Sternenhimmel. Hier ist der Inhalt der SVG-Datei noch einmal, aber diesmal befindet sich die Datei selbst auf dem Server.

<svg xmlns="http://www.w3.org/2000/svg">
  <filter id="filter">
    <feTurbulence baseFrequency="0.2"/>
    <feColorMatrix values="0 0 0 9 -4
                           0 0 0 9 -4
                           0 0 0 9 -4
                           0 0 0 0 1"/>
  </filter>
  <rect width="100%" height="100%" filter="url(#filter)"/>
</svg>

Jetzt können wir es in HTML verwenden, zum Beispiel als Bild

<img src="https://example.com/starry-sky.svg"/>

Und es ist genauso bequem, die URL in CSS als Hintergrundbild zu verwenden

.your-selector {
  background-image: url("https://example.com/starry-sky.svg");
}

Angesichts der heutigen HTTP2-Unterstützung und wie relativ klein SVG-Dateien im Vergleich zu Rasterbildern sind, ist dies keine schlechte Lösung. Alternativ kann die Datei auf einem CDN platziert werden, um eine noch bessere Auslieferung zu gewährleisten. Der Vorteil, SVGs als separate Dateien zu haben, ist, dass sie in mehreren Ebenen zwischengespeichert werden können.

Einschränkungen

Obwohl ich es sehr genieße, diese kleinen Muster zu erstellen, muss ich auch einige ihrer Unvollkommenheiten anerkennen.

Die wichtigste Unvollkommenheit ist, dass sie sehr schnell eine rechenintensive „Monster“-Filterkette erzeugen können. Die einzelnen Filtereffekte ähneln sehr den einmaligen Operationen in Bildbearbeitungssoftware. Wir „photoshoppen“ im Grunde genommen mit Code, und jedes Mal, wenn der Browser diese Art von SVG anzeigt, muss er jeden Vorgang rendern. Wenn Sie also eine lange Filterkette haben, sind Sie möglicherweise besser dran, Ihr Ergebnis als JPEG oder PNG zu erfassen und bereitzustellen, um die CPU-Zeit der Benutzer zu sparen. Taylor Hunts „Improving SVG Runtime Performance“ enthält viele weitere großartige Tipps, um die maximale Leistung aus SVG herauszuholen.

Zweitens müssen wir über die Browserunterstützung sprechen. Im Allgemeinen wird SVG gut unterstützt, insbesondere in modernen Browsern. Beim Arbeiten mit diesen Mustern bin ich jedoch auf ein Problem mit Safari gestoßen. Ich habe versucht, ein sich wiederholendes kreisförmiges Muster mit <radialGradient> mit spreadMethod="repeat" zu erstellen. Es funktionierte gut in Chrome und Firefox, aber Safari mochte es nicht. Safari zeigt den radialen Farbverlauf so an, als ob seine spreadMethod auf pad gesetzt wäre. Sie können es direkt in der MDN-Dokumentation überprüfen.

Es kann vorkommen, dass verschiedene Browser dasselbe SVG unterschiedlich rendern. Angesichts all der Komplexität des Renderns von SVG ist es ziemlich schwierig, eine perfekte Konsistenz zu erreichen. Das gesagt, habe ich nur einen Unterschied zwischen den Browsern gefunden, der erwähnenswert ist, und das ist beim Wechsel in die „Vollbildansicht“. Wenn Firefox in den Vollbildmodus wechselt, rendert er das SVG nicht im erweiterten Teil des Viewports. Chrome und Safari sind jedoch gut. Sie können dies überprüfen, indem Sie diesen Stift in Ihrem bevorzugten Browser öffnen und dann in den Vollbildmodus wechseln. Dies ist ein seltener Randfall, der wahrscheinlich mit etwas JavaScript und der Fullscreen API umgangen werden könnte.

Danke!

Puh, das war's! Wir haben uns nicht nur einige coole Muster angesehen, sondern auch jede Menge über <feTurbulence> in SVG gelernt, einschließlich der verschiedenen Filter, die dafür benötigt werden, und wie man sie auf interessante Weise manipuliert.

Wie die meisten Dinge im Web gibt es auch bei den von uns gemeinsam behandelten Konzepten Nachteile und potenzielle Fallstricke, aber hoffentlich haben Sie jetzt eine Vorstellung davon, was möglich ist und worauf Sie achten sollten. Sie haben die Macht, einige großartige Muster in SVG zu erstellen!