Einen realistischen Glaseffekt mit SVG erstellen

Avatar of David Fitzgibbon
David Fitzgibbon am

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

Ich liebe SVG. Sicher, der Code kann anfangs dicht und schwierig aussehen, aber du wirst die Schönheit der Ergebnisse erkennen, wenn du ihn besser kennenlernst. Der Bonus ist, dass diese Ergebnisse im Code vorliegen, sodass sie mit einem CMS verknüpft werden können. Deine Designer können beruhigt sein und wissen, dass sie keinen Effekt für jeden Artikel oder jedes Produkt auf deiner Website reproduzieren müssen.

Heute möchte ich dir zeigen, wie ich zu diesem Glas-Text-Effekt gekommen bin.

The word Kyoto that is translucent and stacked on top an image of the city.

Schritt 0: Geduld und Raum

SVG kann viel sein, besonders wenn man gerade erst anfängt, es zu lernen (und wenn das der Fall ist, ist Chris' Buch ein guter Anfang). Es ist praktisch eine ganz neue Sprache und, besonders für Leute, denen es an Designfähigkeiten mangelt, gibt es viele neue Techniken und Überlegungen zu beachten. Wie bei HTML, wirst du aber feststellen, dass es eine Handvoll Werkzeuge gibt, die wir nutzen können, um SVG viel leichter zu verstehen. Sei also geduldig und versuche es weiter!

Außerdem gib dir selbst Raum. Buchstäblich. SVG-Code ist dicht, daher benutze ich gerne zwei oder drei neue Zeilen, um Dinge aufzulockern. Das macht den Code leichter lesbar und hilft mir, die Trennung verschiedener Teile mit weniger visueller Ablenkung zu erkennen. Oh, und benutze Kommentare, um zu markieren, wo du dich im Dokument befindest. Das kann helfen, deine Gedanken zu ordnen und deine Erkenntnisse zu dokumentieren.

Ich habe für jeden Schritt, den wir im Prozess des Erlernens dieses Glaseffekts behandeln werden, Demos erstellt, um die Dinge, die wir dabei behandeln, zu festigen.

Okay, jetzt, da wir mental vorbereitet sind, legen wir los!

Schritt 1: Das grundlegende Bild platzieren

Zuerst einmal: Wir brauchen ein Bild als Hintergrund für unseren Glaseffekt. Hier haben wir ein <svg>-Element und ein <image> darin. Das ist ähnlich wie das Hinzufügen eines <img> in HTML. Du wirst feststellen, dass die Abmessungen des viewBox-Attributs und des <image>-Elements im SVG-Element gleich sind. Dies stellt sicher, dass das <image> genau die gleiche Größe hat wie das eigentliche Bild, auf das wir verlinken.

Das ist ein wichtiger Unterschied, den man beachten muss: Wir verlinken auf ein Bild. Die SVG-Datei selbst zeichnet kein Rasterbild, aber wir können auf eines im SVG-Code verweisen und sicherstellen, dass diese Ressource an dem von uns angegebenen Ort vorhanden ist. Wenn du schon einmal mit Adobe InDesign gearbeitet hast, ist es so ähnlich wie das Verlinken einer Bildressource in einem Layout – das Bild ist im InDesign-Layout, aber die Ressource selbst lebt tatsächlich woanders.

Sieh den Stift
SVG Glaseffekt – grundlegendes Bild platziert
von David Fitzgibbon (@loficodes)
auf CodePen.

Schritt 2: Das Bild verzerren

Bisher unkompliziert, aber hier wird es kompliziert, denn wir werden dem gerade eingefügten Bild einen Filter hinzufügen. Dieser Filter wird das Bild verzerren. Wenn du dir den Unterschied zwischen der Demo im letzten Schritt und der in diesem Schritt genau ansiehst, wirst du feststellen, dass die Kanten von Objekten im Bild etwas rau und wellig sind. Das ist der Filter in Aktion!

Zuerst erstellen wir ein weiteres <svg>, um den Filter zu beherbergen. Das bedeutet, wenn wir unseren Filter jemals wiederverwenden möchten – zum Beispiel für mehrere Elemente auf der Seite –, dann können wir das auf jeden Fall tun!

Unser erster Filter (#displacement) wird unser Bild verzerren. Wir werden feTurbulence und feDisplacementMap verwenden, die jeweils von Sara Soueidan viel besser erklärt werden, als ich es in diesem Beitrag kann. Beau Jackson hat auch einen schönen Artikel geschrieben, der zeigt, wie sie zur Erstellung eines Wolkeneffekts verwendet werden können. Genug zu sagen, diese beiden Filter gehen oft zusammen und ich denke gerne an sie, wenn etwas „wabbelig“ erscheinen soll.

Mit unserem Filtercontainer an Ort und Stelle müssen wir diesen Filter nur noch mit einem filter-Attribut auf dem <image> auf unser Bild anwenden, Magie!

<svg>

  <!-- more stuff -->
  
  <!-- DISTORTION IMAGE: clipped -->
  <image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 clip-path="url(#clip)" filter= "url(#distortion)"></image>
  
  <!-- FILTER: wobbly effect -->
  <filter id="distortion">
    <feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="2" result="turbulence"/>
    <feDisplacementMap in2="turbulence" in="SourceGraphic" scale="20" xChannelSelector="R" yChannelSelector="G"/>
  </filter>

  <!-- more stuff -->

</svg>

Sieh den Stift
SVG Glaseffekt – Bild verzerrt
von David Fitzgibbon (@loficodes)
auf CodePen.

Schritt 3: Den Text ausschneiden

Wir wollen jedoch nicht, dass das gesamte Bild verzerrt wird. Wir werden die Form unseres verzerrten <image> auf die Form eines Textes zuschneiden. Dies wird im Wesentlichen der Teil des Bildes sein, der „durch“ das Glas gesehen wird.

Um dies zu erreichen, müssen wir ein <text>-Element in einem <clip-path> hinzufügen und ihm eine id geben. Wenn wir diese id im clip-path unseres <image> aufrufen, beschränkt dies seine Form auf die unseres <text>. Wunderbar!

Sieh den Stift
SVG Glaseffekt – Text ausgeschnitten
von David Fitzgibbon (@loficodes)
auf CodePen.

Schritt 4: Das vollständige Bild enthüllen

OK, es ist gut, dass wir das verzerrte <image> auf den <text> zugeschnitten haben, aber jetzt ist der Rest des Bildes weg. Nicht gut.

Wir können dem entgegenwirken, indem wir eine Kopie des gleichen <image> hinzufügen, aber ohne die clip-path- oder filter-Attribute, vor unserem bestehenden <image>. Hier füge ich gerne einige schöne Kommentare hinzu, um alles ordentlich zu halten. Die Idee ist, wie das Auflegen einer transparenten Ebene über das, was wir bisher haben.

Illustration showing one image overlaid on the other.

Ich weiß, ich weiß, das ist nicht sehr elegant und wir wiederholen uns. Idealerweise würden wir unseren Filter direkt auf das <text>-Element setzen und die in="BackgroundImage-Eigenschaft für feDisplacementMap verwenden, um das, was hinter dem Text liegt, zu verzerren, ohne zusätzliche Elemente zu benötigen. Leider hat dies eine schlechte Browserunterstützung, also werden wir uns mit mehreren Bildern behelfen.

<svg>

  <!-- more stuff -->

  <!-- BACKGROUND IMAGE - visible -->
  <image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 ></image>
          
  <!-- DISTORTION IMAGE - clipped -->
  <image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 clip-path="url(#clip)" filter= "url(#distortion)"></image>

  <!-- more stuff -->

</svg>

Sieh den Stift
SVG Glaseffekt – Verzerrung abgeschlossen
von David Fitzgibbon (@loficodes)
auf CodePen.

Schritt 5: Den Text wieder platzieren

Als nächstes duplizieren wir unseren Text, genau wie wir es im letzten Schritt mit dem Bild gemacht haben. Da der Text jedoch in einem clip-path liegt, ist er nun nicht mehr zum Rendern verfügbar. Das ist das letzte Mal, dass wir Inhalte auf diese Weise duplizieren, das verspreche ich!

Jetzt sollten wir etwas haben, das wie ein normales Bild mit schwarzem Text darüber aussieht. Wenn der Verzerrungsfilter auf dem <image>, das wir bereits erstellt haben, das ist, was wir „durch“ das Glas sehen können, dann wird unser neuer <text> das Glas selbst sein.

<svg>

  <!-- more stuff -->

  <!-- TEXT - clipped -->
  <clipPath id="clip">
    <text x="50%" y ="50%" dominant-baseline="middle" text-anchor="middle">KYOTO</text>
  </clipPath>
        
  <!-- TEXT - visible -->
  <text x="50%" y ="50%" dominant-baseline="middle" text-anchor="middle">KYOTO</text>

<!-- more stuff -->

</svg>

Sieh den Stift
SVG Glaseffekt – Text wieder platziert
von David Fitzgibbon (@loficodes)
auf CodePen.

Schritt 6: Den dunklen Rand des Textes erstellen

Hier wird es spannend, zumindest für mich! 🤓

Wir wollen einen dunklen Rand um das Textelement erstellen, der, zusammen mit einem hellen Rand (den wir als nächstes betrachten werden), Tiefe für das Aussehen des Textes vor dem Bild hinzufügt.

Wir brauchen einen neuen filter für unseren <text>, also erstellen wir einen in unserem Filter-SVG-Element und geben ihm eine id="textFilter und verknüpfen ihn mit dem filter-Attribut des <text>-Elements.

SVG arbeitet von hinten nach vorne, also werden wir als erstes den Schatten, den das Glas haben würde, in unseren Filter legen, da dieser am weitesten hinten liegt. Ich sage dir ehrlich, dieser ist ziemlich komplex, aber wir gehen ihn Schritt für Schritt durch.

Für diesen Effekt verwenden wir vier Filterprimitive: feMorphology, feOffset, feFlood und feComposite.

feMorphology kommt zuerst. Wir verwenden es, um den Text dicker zu machen. Im folgenden Demo-Beispiel kommentiere die nächsten drei Primitiven aus ( feOffset, feFlood, feComposite ) und spiele damit herum. Ich habe den Wert radius="4", um den Glaseffekt zu erzielen, aber schau, was passiert, wenn du ihn auf 1... oder 100 setzt!

feOffset wird verwendet, um alle „Pixel“ im vorherigen Primitiven ( feMorphology ) auf der X- oder Y-Achse zu verschieben. Die Werte dx="5" und dy="5" verschieben die „Pixel“ nach rechts auf der X-Achse bzw. nach oben auf der Y-Achse. Je höher die Zahl, desto weiter bewegen sie sich. Verwende negative Zahlen für dx und die „Pixel“ bewegen sich nach links. Negatives dy und sie bewegen sich nach oben! Auch hier ist das die Art von Dingen, die man lernt, wenn man damit herumspielt.

Der Grund, warum ich Anführungszeichen um „Pixel“ setze, ist, dass es sich nicht um Bildschirm-Pixel handelt, wie man es vielleicht von CSS kennt. Vielmehr beziehen sie sich auf die Abmessungen, die wir auf dem übergeordneten <svg> festgelegt haben. Ich denke dabei an Prozente. Wir haben diese Einstellungen viewBox="0 0 1890 1260" in unserem Beispiel verwendet. Das bedeutet, unser <svg> ist 1890 „Pixel“ breit. Wenn wir dx="189" setzen, bedeutet das, dass wir unser Element 10% der SVG-Breite verschieben (1890 geteilt durch 189).

feFlood ist großartig. Wenn du den Bildschirm mit Farbe füllen willst, ist das die Primitive, die du brauchst! Du fragst dich vielleicht, warum wir unseren Text jetzt nicht lesen können, wenn wir ihn anwenden. Das liegt daran, dass du nur das Ergebnis der zuletzt erstellten Filterprimitive sehen kannst. Das Ergebnis jeder der vorherigen Primitiven hatte Bezug zu unserem <text>-Element. Das Ergebnis von feFlood ist wie sein Name: eine Flut von Farbe. Es weiß nicht, was du vorher getan hast und es ist ihm egal – es wird lediglich einen Bereich mit Farbe füllen.

Hier werden manche Leute frustriert mit SVG. Es ist schwer, an etwas zu arbeiten, wenn man es nicht sehen kann! Vertrau mir, wenn du mehr mit SVG arbeitest, wirst du dich daran gewöhnen. Tatsächlich werden die nächsten Schritte erfordern, dass wir uns darauf verlassen und darauf vertrauen, dass alles noch an seinem Platz ist.

feComposite wird dieses Problem für uns lösen. Was macht es? MDN beschreibt es so

DieDie SVG-Filterprimitive führt die Kombination zweier Eingabebilder Pixel für Pixel im Bildraum unter Verwendung einer der Porter-Duff-Compositing-Operationen durch: over, in, atop, out, xor und lighter.

Das ist für mich Kauderwelsch. Ich stelle es mir so vor, dass es die Alpha-Ebene von in mit der Farbe/Alpha von in2 beeinflusst.

Damit können wir unseren Text wieder erkennen und, da die verwendete Farbe leicht transparent ist, können wir sogar den verzerrten „Glas“-Effekt durchscheinen sehen. Großartig!

<svg>

  <!-- more stuff -->
    
  <!-- dark edge -->
  <feMorphology operator="dilate" radius="4" in="SourceAlpha" result="dark_edge_01" />
    <feConvolveMatrix order="3,3" kernelMatrix=
      "1 0 0 
      0 1 0
      0 0 1" in="dark_edge_01" result="dark_edge_02" />
    <feOffset dx="5" dy="5" in="dark_edge_02" result="dark_edge_03"/>
    <feFlood flood-color="rgba(0,0,0,.5)" result="dark_edge_04" />
    <feComposite in="dark_edge_04" in2="dark_edge_03" operator="in" result="dark_edge" />
    
  </filter>

  <!-- more stuff -->

</svg>

Sieh den Stift
SVG Glaseffekt – dunkler Rand
von David Fitzgibbon (@loficodes)
auf CodePen.

Schritt 7: Machen wir den hellen Rand

Das ist im Wesentlichen dasselbe wie das, was wir gerade getan haben, aber wir werden die Form mit negativen dx/dy-Werten nach oben und links verschieben. Diesmal verwenden wir auch eine leicht weiße Farbe. Wir streben einen schönen Tiefeneffekt an.

Wir sind wieder an einem Punkt, an dem das, was wir sehen, das aktuellste Ergebnis eines Filterprimitivs ist, aber wir können unseren dunklen Rand nicht sehen! feComposite ist nicht das, was wir wollen, um sie zu kombinieren, denn wir wollen nicht, dass die Alpha des dunklen Rands von dem hellen Rand gefärbt wird… wir wollen beides sehen! Was uns zu…

<svg>
  <filter id="textFilter">

    <!-- more stuff -->

      <feMorphology operator="dilate" radius="4" in="SourceAlpha" result="light_edge_01" />
      <feConvolveMatrix order="3,3" kernelMatrix=
      "1 0 0 
        0 1 0
        0 0 1" in="light_edge_01" result="light_edge_02" />
      <feOffset dx="-2" dy="-2" in="light_edge_02" result="light_edge_03"/>
      <feFlood flood-color="rgba(255,255,255,.5)" result="light_edge_04" />
      <feComposite in="light_edge_04" in2="light_edge_03" operator="in" result="light_edge" />

    <!-- more stuff -->
  
  </filter>
</svg>

Sieh den Stift
SVG Glaseffekt – heller Rand
von David Fitzgibbon (@loficodes)
auf CodePen.

Schritt 8: Die Ränder kombinieren

feMerge! Das ist ein Held. Es erlaubt uns, beliebig viele Primitive-Ergebnisse zu nehmen und sie zusammenzuführen, wodurch ein neues Bild entsteht. Woohoo, wir können jetzt sowohl dunkle als auch helle Ränder zusammen sehen!

Allerdings wollen wir, dass es Ränder sind und nicht, dass beide den gesamten Text ausfüllen. Daher müssen wir den Raum entfernen, den der ursprüngliche <text> einnimmt. Was wir als nächstes brauchen, ist ein weiteres feComposite, um die ursprüngliche SourceGraphic auszustanzen. Da wir feMorphology verwendet haben, um die Buchstaben für unsere Ränder dicker zu machen, können wir jetzt die ursprünglichen Buchstabenformen aus dem Ergebnis unseres feMerge heraustrennen.

<svg>
  <filter id="textFilter">

    <!-- more stuff -->

    <feMerge result="edges">
      <feMergeNode in="dark_edge" />
      <feMergeNode in="light_edge" />
    </feMerge>
    <feComposite in="edges" in2="SourceGraphic" operator="out" result="edges_complete" />

  </filter>
</svg>

Sieh den Stift
SVG Glaseffekt – Ränder kombiniert
von David Fitzgibbon (@loficodes)
auf CodePen.

Jetzt fangen wir an, wie Glas auszusehen, nur ein Teil fehlt noch.

Schritt 9: Ja, eine Fase

Wir haben einen ziemlich guten 3D-ähnlichen Glaseffekt. Die Buchstaben sehen jedoch flach aus. Fügen wir noch einen Effekt hinzu und lassen sie runder aussehen.

Um das zu erreichen, werden wir einen abgeschrägten Effekt erzeugen.

Zuerst werden wir feGaussianBlur verwenden. Dies wird unsere bestehenden Filter leicht verwischen. Wir werden dieses verwischte Ergebnis als Grundlage verwenden, um etwas feSpecularLighting hinzuzufügen. Wie üblich, kannst du gerne mit den Zahlen hier spielen und sehen, welche Effekte du erzielen kannst! Das wichtigste, das du vielleicht ändern möchtest, ist das lighting-color-Attribut. Das Bild, das wir hier verwenden, ist leicht dunkel, daher verwenden wir eine helle lighting-color. Wenn dein Bild sehr hell wäre, würde dies die Buchstaben schwer lesbar machen, daher könntest du in diesem Fall eine dunklere lighting-color verwenden.

<svg>
  <filter id="textFilter">
  
    <!-- more stuff -->

    <feGaussianBlur stdDeviation="5" result="bevel_blur" />
    <feSpecularLighting result="bevel_lighting" in="bevel_blur" specularConstant="2.4" specularExponent="13" lighting-color="rgba(60,60,60,.4)">
      <feDistantLight azimuth="25" elevation="40" />
    </feSpecularLighting>
    <feComposite in="bevel_lighting" in2="SourceGraphic" operator="in" result="bevel_complete" />

  </filter>
</svg>

Sieh den Stift
SVG Glaseffekt – Fase
von David Fitzgibbon (@loficodes)
auf CodePen.

Schritt 10: Alles zusammen!

Schließlich, da alle Teile fertig sind, machen wir ein letztes feMerge, um alles für den fertigen Effekt zusammenzufügen!

<svg>
  <filter id="textFilter">

    <!-- more stuff -->

    <feMerge result="complete">
      <feMergeNode in="edges_complete" />
      <feMergeNode in="bevel_complete" />
    </feMerge>
  </filter>
</svg>

Hier ist alles zusammen, gut aufgeteilt und kommentiert.

<!-- VISIBLE SVG -->
<svg viewBox="0 0 1890 1260">
        
  <!-- BACKGROUND IMAGE - visible -->
  <image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 ></image>
    
  <!-- DISTORTION IMAGE - clipped -->
  <image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 clip-path="url(#clip)" filter= "url(#distortion)"></image>
    
  <!-- TEXT - clipped -->
  <clipPath id="clip">
    <text x="50%" y ="50%" dominant-baseline="middle" text-anchor="middle">KYOTO</text>
  </clipPath>
    
  <!-- TEXT - visible -->
  <text x="50%" y ="50%" dominant-baseline="middle" text-anchor="middle" filter="url(#textFilter)">KYOTO</text>
    
</svg>

<!-- FILTERS -->
<svg>
  <filter id="distortion">
    <feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="2" result="turbulence"/>
    <feDisplacementMap in2="turbulence" in="SourceGraphic" scale="20" xChannelSelector="R" yChannelSelector="G"/>
  </filter>
    
  <filter id="textFilter">
            
    <!-- dark edge -->
    <feMorphology operator="dilate" radius="4" in="SourceAlpha" result="dark_edge_01" />
    <feOffset dx="5" dy="5" in="dark_edge_01" result="dark_edge_03"/>
    <feFlood flood-color="rgba(0,0,0,.5)" result="dark_edge_04" />
    <feComposite in="dark_edge_04" in2="dark_edge_03" operator="in" result="dark_edge" />     
            
    <!-- light edge -->
    <feMorphology operator="dilate" radius="4" in="SourceAlpha" result="light_edge_01" />
    <feOffset dx="-2" dy="-2" in="light_edge_01" result="light_edge_03"/>
    <feFlood flood-color="rgba(255,255,255,.5)" result="light_edge_04" />
    <feComposite in="light_edge_04" in2="light_edge_03" operator="in" result="light_edge" />
          
    <!-- edges together -->
    <feMerge result="edges">
      <feMergeNode in="dark_edge" />
      <feMergeNode in="light_edge" />
    </feMerge>
    <feComposite in="edges" in2="SourceGraphic" operator="out" result="edges_complete" />
          
    <!-- bevel -->
    <feGaussianBlur stdDeviation="5" result="bevel_blur" />
    <feSpecularLighting result="bevel_lighting" in="bevel_blur" specularConstant="2.4" specularExponent="13" lighting-color="rgba(60,60,60,.4)">
      <feDistantLight azimuth="25" elevation="40" />
    </feSpecularLighting>
    <feComposite in="bevel_lighting" in2="SourceGraphic" operator="in" result="bevel_complete" />

    <!-- everything in place -->
    <feMerge result="complete">
              <feMergeNode in="edges_complete" />
              <feMergeNode in="bevel_complete" />
    </feMerge>

  </filter>
</svg>

Sieh den Stift
SVG Glaseffekt
von David Fitzgibbon (@loficodes)
auf CodePen.