Realistische Wolken mit SVG und CSS zeichnen

Avatar of Beau Jackson
Beau Jackson am

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

Die griechische Mythologie erzählt die Geschichte von Zeus, der die Wolkennymphe Nephele erschafft. Wie andere griechische Mythen wird diese Geschichte ziemlich bizarr und jugendfrei. Hier ist eine sehr gekürzte, höfliche Version.

Nephele, so erzählt man uns, wurde von Zeus nach dem Bild seiner eigenen schönen Frau erschaffen. Ein Sterblicher trifft Nephele, verliebt sich in sie und gemeinsam legen sie ein Nickerchen für Erwachsene™. Schließlich, in einer bizarren Wendung, bringt die Wolke halb menschliche, halb pferdeartige Zentaur-Babys zur Welt.

Seltsam, oder? Persönlich kann ich mir keinen Reim darauf machen. Glücklicherweise ist der Prozess der Erstellung von Wolken im Browser *viel* unkomplizierter und weitaus weniger anrüchig.

Detailansicht von Yuans Chuan's Wolken. (Demo)

Kürzlich entdeckte ich, dass der Entwickler Yuan Chuan code-generierte, fotorealistische Wolken realisiert hat. Für mich war diese Vorstellung im Browser lange Zeit Stoff für Mythen.

Mit einem Blick auf den Code in diesem Pen können wir uns vorstellen, dass überzeugende einzelne Wolken durch die Verwendung von CSS box-shadow mit einem <filter>-Element, das zwei SVG-Filter als Ergänzung enthält, erreichbar sind.

Der gewünschte Fotorealismus wird mit einer feinen Mischung aus feTurbulence und feDisplacementMap erreicht. Diese SVG-Filter sind leistungsstark, komplex und bieten sehr aufregende Funktionen (einschließlich eines Oscar-prämierten Algorithmus)! Unter der Haube kann ihre Komplexität jedoch etwas einschüchternd sein.

Während die Physik von SVG-Filtern den Rahmen dieses Artikels sprengt, gibt es reichlich Dokumentation auf MDN und w3.org. Eine sehr informative Seite zu feTurbulence und feDisplacement ist frei verfügbar (und wird als Kapitel von diesem erstaunlichen Buch angeboten).

Für diesen Artikel konzentrieren wir uns darauf, wie man diese SVG-Filter verwendet, um spektakuläre Ergebnisse zu erzielen. Wir müssen nicht zu tief in das eindringen, was algorithmisch hinter den Kulissen geschieht, so wie ein Künstler nicht die molekulare Struktur von Farbe kennen muss, um eine atemberaubende Landschaft zu malen.

Konzentrieren wir uns stattdessen auf eine kleine Handvoll SVG-Attribute, die für das Zeichnen überzeugender Wolken im Browser unerlässlich sind. Ihre Verwendung wird es uns ermöglichen, diese leistungsstarken Filter unserem Willen zu beugen und zu lernen, wie wir sie in unseren eigenen Projekten mit Präzision anpassen können.

Lassen Sie uns mit einigen Grundlagen beginnen

Die CSS-Eigenschaft box-shadow hat fünf Werte, die eine genaue Beachtung verdienen.

box-shadow: <offsetX> <offsetY> <blurRadius> <spreadRadius> <color>;

Erhöhen wir diese Werte (wahrscheinlich höher, als es jeder vernünftige Entwickler tun würde), damit dieser Schatten selbst zu einem Akteur auf der Bühne wird.

(Demo)
#cloud-square {
  background: turquoise;
  box-shadow: 200px 200px 50px 0px #000;
  width: 180px;
  height: 180px;
}

#cloud-circle {
  background: coral;
  border-radius: 50%;
  box-shadow: 200px 200px 50px 0px #000;
  width: 180px;
  height: 180px;
}

Sie haben entweder Schattenspiele gemacht oder gesehen, richtig?

Quelle: Double-M

So wie sich eine Hand verändert, um den Schatten zu verändern, kann eine "Quellform" in unserem HTML sich bewegen und verformen, um die Form eines Schattens zu bewegen und zu verändern, der im Browser gerendert wird. box-shadow dupliziert die "Morphing"-Funktionen auf der ursprünglichen Größe und border-radius. SVG-Filter werden sowohl auf das Element *als auch* auf seinen Schatten angewendet.

<svg width="0" height="0"> 
  <filter id="filter">
    <feTurbulence type="fractalNoise" baseFrequency=".01" numOctaves="10" />
    <feDisplacementMap in="SourceGraphic" scale="10" />
  </filter>
</svg>

Dies ist der Markup für unser SVG bisher. Es wird nicht gerendert, da wir noch nichts Visuelles definiert haben (ganz zu schweigen von der Nulllänge und -breite). Sein einziger Zweck ist es, einen Filter zu halten, den wir unserem SourceGraphic (auch bekannt als unser <div>) zuführen. Unser Quell-<div> *und* sein Schatten werden beide unabhängig voneinander durch den Filter verzerrt.

Wir werden die wesentliche CSS-Regel hinzufügen, die das HTML-Element (`#cloud-circle`) mit dem SVG-Filter über seine ID verbindet.

#cloud-circle {
  filter: url(#filter);
  box-shadow: 200px 200px 50px 0px #fff;
}

Et Voilà!

Okay, zugegeben, das Hinzufügen des SVG-Filters ist ziemlich *unter*wältigend.

(Demo)

Keine Sorge! Wir haben gerade erst an der Oberfläche gekratzt und es gibt noch viel mehr Gutes zu betrachten.

Experimentieren mit dem feDisplacementMap-Skalierungsattribut

Einige *unwissenschaftliche* Experimente mit diesem einen Attribut können dramatische Ergebnisse liefern. Vorerst lassen wir alle Werte in feTurbulence konstant und passen einfach das scale-Attribut von DisplacementMap an.

Während scale steigt (in Schritten von 30) wird unser Quell-<div> verzerrt und wirft einen Schatten, der die stochastische Form widerspiegelt, in der Wolken am Himmel erscheinen.

<feDisplacementMap in="SourceGraphic" scale="180"/>
Das Skalierungsattribut wurde mit Werten von 30 inkrementiert. (Demo)

Okay, wir kommen voran! Ändern wir die Farben ein wenig, um eine überzeugendere Wolke zu erzeugen und den Effekt "zu verkaufen".

body {
  background: linear-gradient(165deg, #527785 0%, #7FB4C7 100%);
}

#cloud-circle {
    width: 180px;
    height: 180px;
    background: #000;
    border-radius: 50%;
    filter: url(#filter);
    box-shadow: 200px 200px 50px 0px #fff;
}

Jetzt kommen wir einem realistischen Wolkeneffekt näher!

Anpassen des box-shadow-Blur-Werts

Die folgende Bilderserie zeigt den Einfluss, den der Blur-Wert auf box-shadow hat. Hier wird der Blur inkrementell um 10 Pixel erhöht.

Die Wolke wird "weicher", wenn der Blur-Wert steigt.

Um unserer Wolke einen cumulus-artigen Effekt zu verleihen, können wir unser Quell-<div> etwas verbreitern.

#cloud-circle {
  width: 500px;
  height: 275px;
  background: #000;
  border-radius: 50%;
  filter: url(#filter);
  box-shadow: 200px 200px 60px 0px #fff;
}
Großartig, jetzt stört das Quell-Element. 😫

Moment! Wir haben das Quell-Element verbreitert und jetzt stört es unseren weißen Schatten, den wir als Wolke bezeichnen. Lassen Sie uns den Schatten weiter entfernt "neu werfen", damit unsere Wolke nicht mehr vom Quellbild verdeckt wird. (Stellen Sie sich das so vor, als würden Sie Ihre Hand weiter von der Wand entfernen, damit sie Ihre Schattenspielfigur nicht verdeckt.)

Dies wird gut mit etwas CSS-Positionierung erreicht. Der <body> ist das Elternelement für unsere Wolke, das standardmäßig statisch positioniert ist. Lassen Sie uns unser Quell-<div> mit absoluter Positionierung "verstecken". Anfangs wird dies auch unseren Schatten neu positionieren, daher müssen wir auch den Abstand des Schattens zum Element erhöhen und das Element etwas weiter verschieben.

#cloud-circle {
  width: 500px;
  height: 275px;
  background: #000;
  border-radius: 50%;
  filter: url(#filter);
  box-shadow: 400px 400px 60px 0px #fff; /* Increase shadow offset */
  position: absolute; /* Take the parent out of the document flow */
  top: -320px; /* Move a little down */
  left: -320px; /* Move a little right */
}

Ja! Wir sind bei einer ziemlich überzeugenden Wolke angekommen.

Siehe den Pen
von Beau Haus (@beauhaus)
auf CodePen.

Was hier in den Browser gemalt wird, ist eine ziemlich gute Darstellung einer Wolke – aber ich bin mir nicht sicher… macht *diese Wolke* der Wolkennymphe Nephele wirklich Ehre? Ich bin sicher, wir können es besser machen!

Tiefe durch Ebenen vermitteln

Das wollen wir

A photo of clouds against a blue sky. The clouds have shades of gray that provide depth.
Quelle: pcdazero

Angesichts der Tiefe, Textur und des Reichtums der Wolken auf diesem Foto ist eines klar: Zeus ging zur Kunstschule. Zumindest muss er The Universal Principles of Design gelesen haben, das ein mächtiges – und doch täuschend gewöhnliches – Konzept illustriert.

[…] Lichtbias spielt eine bedeutende Rolle bei der Interpretation von Tiefe und Natürlichkeit und kann von Designern auf vielfältige Weise manipuliert werden… Nutzen Sie den Kontrast zwischen hellen und dunklen Bereichen, um die Erscheinung von Tiefe zu variieren.

Dieser Abschnitt gibt uns einen Hinweis darauf, wie wir unsere eigenen code-generierten Wolken erheblich verbessern können. Wir können unsere Wolke mit hoher Detailtreue zu den Wolken in unserem Referenzbild rendern, indem wir Ebenen unterschiedlicher Form, Größe und Farbe übereinander stapeln. Dazu reicht es aus, unseren filter so oft aufzurufen, wie wir Ebenen wünschen.

<svg width="0" height="0">
    <!-- Back Layer -->
    <filter id="filter-back">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="4" />
      <feDisplacementMap  in="SourceGraphic" scale="170" />
    </filter>
    <!-- Middle Layer -->
    <filter id="filter-mid">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" />
      <feDisplacementMap  in="SourceGraphic" scale="150" />
    </filter>
    <!-- Front Layer -->
    <filter id="filter-front">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" />
      <feDisplacementMap  in="SourceGraphic" scale="100" />
    </filter>
</svg>

Das Anwenden unserer Ebenen bietet uns die Gelegenheit, feTurbulence zu erkunden und seine Vielseitigkeit zu erkennen. Wir wählen die glattere verfügbare Art: fractalNoise mit hochgedrehtem numOctaves auf 6.

<feTurbulence type="fractalNoise" baseFrequency="n" numOctaves="6"/>

Was bedeutet das alles? Konzentrieren wir uns vorerst speziell auf das Attribut baseFrequency. Hier ist, was wir bekommen, wenn wir den Wert von n erhöhen.

Je niedriger der Wert, desto runder und unschärfer wird es. Je höher der Wert, desto runder und starrer wird es.

Wörter wie **Turbulenz**, **Rauschen**, **Frequenz** und **Oktave** mögen seltsam und sogar verwirrend erscheinen. Aber keine Angst! Es ist tatsächlich perfekt zutreffend, die Effekte dieses Filters mit Schallwellen zu vergleichen. Wir können eine niedrige Frequenz (baseFrequency=0.001) mit einem tiefen, dumpfen Geräusch und eine steigende Frequenz (baseFrequency=0.1) mit einer höheren, klareren Tonhöhe gleichsetzen.

Wir sehen, dass unser idealer Punkt für einen cumulus-artigen Effekt im Bereich von etwa 0,005 bis 0,01 für die baseFrequency liegen könnte.

Detail hinzufügen mit numOctaves

Das Erhöhen von numOctaves ermöglicht es uns, unser Bild in extrem feinem Detail zu rendern. Dies erfordert eine Menge Berechnung, seien Sie also gewarnt: Hohe Werte sind ein erheblicher Performance-Hit. Versuchen Sie, der Versuchung zu widerstehen, diesen Wert zu erhöhen, es sei denn, Ihr Browser trägt einen Helm und Knieschützer.

Je höher der Wert, den wir in numOctaves eingeben, desto mehr feine Details verleihen wir unserer Wolke.

Die gute Nachricht ist, dass wir diesen Wert nicht zu hoch einstellen müssen, um Detail und Feinheit zu erzeugen. Wie die Bilderserie oben zeigt, können wir uns mit einem numOctaves-Wert von 4 oder 5 zufrieden geben.

Hier ist das Ergebnis

Siehe den Pen
von Beau Haus (@beauhaus)
auf CodePen.

Unendliche Vielfalt mit dem seed-Attribut

Es gibt viel über das seed-Attribut zu sagen, da es einen Hinweis auf die Magie gibt, die hinter den Kulissen geschieht. Aber für unsere Zwecke kann die Nützlichkeit von seed auf vier Wörter reduziert werden: "anderer Wert, andere Form".

Die Perlin Noise-Funktion (die zuvor erwähnt wurde) verwendet diesen Wert als Ausgangspunkt für ihren Zufallszahlengenerator. Wenn Sie dieses Attribut *nicht* einschließen, wird seed standardmäßig auf Null gesetzt. Wenn es jedoch enthalten ist, müssen wir uns bei welchem Wert auch immer wir seed geben, keine Sorgen um Leistungseinbußen machen.

Animation showing thr shape of a cloud changing as the seed value changes.
Unterschiedliche Seed-Werte erzeugen unterschiedliche Formen.

Die obige GIF-Datei repräsentiert einen Teil dessen, was seed zu bieten hat. Beachten Sie, dass jede dieser Wolken eine geschichtete, zusammengesetzte Wolke ist. (Obwohl ich Attribute für jede Schicht angepasst habe, habe ich ihre jeweiligen seed-Werte einheitlich gehalten.)

Quelle: Brockenhexe

Hier habe ich, mit einem genauen Blick auf das Referenzbild, 3 Wolken-<div>s (unterschiedlicher Opazität) auf ein einziges Basis-Div geschichtet. Durch Ausprobieren und Eingeben beliebiger seed-Werte bin ich schließlich zu einer Form gelangt, die der Form der Wolke auf dem Foto ähnelt.

Siehe den Pen
Nephele Reference Image study
von BEAU.HAUS (@beauhaus)
auf CodePen.

Der Himmel ist die Grenze

Natürlich wäre es Hybris zu glauben, dass die <div>s, die wir in den Browser malen, überzeugender sein könnten als die von Zeus, Nephele.

Je mehr Geheimnisse wir jedoch aus CSS- und SVG-Filtern herauskitzeln können, desto mehr werden wir befähigt, etwas visuell Beeindruckendes mit einem hohen Grad an Treue zur ursprünglichen Schöpfung des Donnergottes zu schaffen. Dann können wir weiter experimentieren!

Reflektierender Nebel

Animierter reflektierender Nebel

Altocumulus-Wolken

Altocumulus-Wolken

In diesem Artikel haben wir nur mit der Zehe in ein Meer von Macht und Komplexität getaucht. SVG-Filter können oft überwältigend und unzugänglich erscheinen.

Jedoch, ähnlich wie bei den Beispielen im Projekt A Single Div oder Diana Smiths Maltechniken, wird ein spielerischer und experimenteller Ansatz immer mit spektakulären Ergebnissen belohnt!

Erfolg freigeschaltet! Nephele Wolken-Generator

Ich bin sicher, viele von Ihnen sind begeistert von all den technischen Details, die zur Erzeugung von Wolken erforderlich sind, bevorzugen aber vielleicht etwas, das es viel einfacher macht, Wolken in einem Projekt zu verwenden. Ich habe ein kleines Werkzeug entwickelt, das hilft, Wolken zu generieren und mit Formen und Variationen zu experimentieren.

Wolken erstellen!

Haben Sie Fragen, Vorschläge oder Ratschläge? Ping mich im Twitter-Universum oder hinterlassen Sie einen Kommentar hier im Beitrag.


Vielen Dank an Amelia Bellamy-Royds für ihren freundlichen Rat zu diesem Artikel.