Verwendung von CSS-Masken zur Erstellung von gezackten Rändern

Avatar of Stuart Langridge
Stuart Langridge am

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

Ich arbeitete an einem Projekt, das diesen hübschen gezackten Rand am unteren Rand eines Bannerbildes hatte.

Sieht scharf aus ... in mehrfacher Hinsicht.

Es hat mich einen Moment zum Nachdenken gebracht und ich habe dabei etwas gelernt! Ich dachte, ich schreibe auf, wie ich es angegangen bin, damit Sie es in Ihren eigenen Projekten verwenden können.

Ich begann mit einem altmodischen HTML-Bild in einem umgebenden Element

<div class="jagged-wrapper">
  <img src="path-to-image.jpg" />
</div>

Dann verwendete ich sein `::after`-Pseudo-Element, um ein sich wiederholendes Hintergrundbild darauf zu legen

.jagged-wrapper::after {
  content: "";
  background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1" preserveAspectRatio="none"><polygon style="fill:white;" points="1,0 1,1 0,1 "/></svg>');
  background-size: 30px 30px;
  width: 100%;
  height: 30px;
  position: absolute;
  bottom: 0px;
  right: 0;
  z-index: 2;
}

Dieses Hintergrundbild? Es ist SVG-Code, der in eine Data-URI konvertiert wurde. Hier ist der originale SVG-Code. Chris hat ein schönes Video, in dem er durch diese Konvertierung führt.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1" preserveAspectRatio="none">
  <polygon style="fill: white;" points="1,0 1,1 0,1 "/>
</svg>

„Da haben wir’s!“, dachte ich.

Das funktioniert zwar, aber oh je, das ist viel Aufwand. SVG-Markup ist in CSS so schwer zu lesen. Außerdem ist es ärgerlich, sich daran erinnern zu müssen, es zu quotieren (z. B. `url('data:image/svg+xml'...)`). Sicher, wir können das SVG base64-codieren, um das zu vermeiden, aber das ist noch ärgerlicher. Außerdem muss das SVG mit der gleichen Hintergrundfarbe wie das Bild (oder wo immer es verwendet wird) gefüllt sein, sonst funktioniert es nicht.

Warten Sie, ist das nicht genau das, wofür Maskierung da ist? Ja! Ja, dafür ist Maskierung da.

Das brachte mich zu einem neuen Ansatz: Verwenden Sie ein Bild wie das obige als CSS-Maske, sodass die „fehlenden“ Teile des Bannerbilds tatsächlich *fehlen*. Anstatt Dreiecke der Hintergrundfarbe über das Banner zu zeichnen, sollten wir diese Dreiecke stattdessen komplett vom Banner wegmaskieren und den echten Hintergrund durchscheinen lassen. So funktioniert es auf jedem Hintergrund!

Maskierung wird so gut wie überall unterstützt – zumindest auf die einfache Art, wie ich hier spreche. Wir sprechen auch von etwas, das mit progressiver Verbesserung implementiert werden kann; wenn Masken in einem bestimmten Browser nicht unterstützt werden, erhalten Sie einfach nicht den Sägezahneffekt. Definitiv nicht das Ende der Welt.

Diese Browser-Supportdaten stammen von Caniuse, das mehr Details bietet. Eine Zahl bedeutet, dass der Browser die Funktion ab dieser Version unterstützt.

Desktop

ChromeFirefoxIEEdgeSafari
12053Nein12015.4

Mobil / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
12712712715.4

Eine Möglichkeit, wie eine CSS-Maske funktioniert, besteht darin, ein Bild mit einem Alphakanal als `mask-image` bereitzustellen. Das zugrunde liegende Element – das maskierte Element – wird zu dem Grad (halb-)transparent, den der Alphakanal des `mask-image` vorgibt. Wenn Ihr Maskenbild also eine weiße Teekanne auf transparentem Hintergrund ist, wird das maskierte Element auf die Form der Teekanne zugeschnitten und alles außerhalb davon wird verborgen.

Maskierung kann ein kniffliges Konzept sein, das man durchdringen muss. Sarah Drasner hat einen Artikel , der tief in die Maskierung eintaucht, einschließlich des Unterschieds zur Beschneidung. Masken können viel mehr als das, was wir hier behandeln. Schauen Sie sich die Spezifikationen, caniuse und MDN für noch mehr Informationen an.

Was wir brauchen, ist ein einzelnes „Sägezahn“-ähnliches Bild, ähnlich wie das obige SVG, bei dem die obere linke Hälfte weiß gefüllt ist und die untere linke Hälfte halbtransparent bleibt. Und idealerweise wäre dieses Bild kein tatsächliches SVG, da wir sonst wieder in dem hässlichen Data-URI-Chaos landen würden, in dem wir uns zuvor befanden.

An diesem Punkt denken Sie vielleicht: „Hey, binden Sie das SVG einfach direkt in CSS ein, definieren Sie eine Maske darin und verweisen Sie dann über die CSS-ID in der SVG-Datei auf die Maske!“

Gute Idee! Und es ist sicherlich machbar, *wenn* Sie das HTML bearbeiten können. Für mein spezifisches Projekt arbeitete ich jedoch mit WordPress und wollte meine Änderungen wirklich auf reines CSS beschränken, anstatt zusätzliche Teile in das HTML einzufügen. Das wäre viel mehr Arbeit gewesen. Ich glaube nicht, dass das ungewöhnlich ist; für eine solche präsentationsbezogene Änderung ist es nützlich, das HTML nicht bearbeiten zu müssen. Wir sind uns weitgehend einig, dass semantisch wertlose Wrapper-Elemente nur zur Bereitstellung von Styling-Hooks vermieden werden sollten, aber ich denke, das gilt auch für das Hinzufügen von vollständigem SVG-Markup zum Dokument… oder sogar einer WordPress-Vorlage.

Wir können einen CSS-Linearverlauf verwenden, um stattdessen eine Dreiecksform zu erstellen

.el {
  linear-gradient(
    to bottom right,
    white,
    white 50%,
    transparent 50%,
    transparent
  );
}

Hier ist es auf einem radialen Hintergrund, damit Sie sehen können, dass es wirklich transparent ist

Großartig! Wir können das einfach als `mask-image` auf unserem Banner verwenden, oder? Wir müssen `mask-size` festlegen, was wie `background-size` ist, und `mask-repeat`, wie `background-repeat`, und wir sind fertig?

Leider nein. Nicht so gut.

Der erste Grund ist, dass Sie, wenn Sie nicht Firefox verwenden, höchstwahrscheinlich keine Maskierung in dem obigen Beispiel sehen werden. Das liegt daran, dass Blink und WebKit zum Zeitpunkt des Schreibens immer noch nur Maskierung mit einem Vendor-Präfix unterstützen. Das bedeutet, wir brauchen auch für alles `-webkit-` Präfixe.

Abgesehen vom Vendor-Prefixing ist das, was wir tun, auch *konzeptionell* falsch. Wenn wir die Maske mit `mask-size` nur auf den unteren Streifen des Bildes beschränken, hat der Rest des Bildes überhaupt kein `mask-image`, was es vollständig ausmaskiert. Infolgedessen können wir den Sägezahn allein nicht als Maske verwenden. Was wir brauchen, ist ein `mask-image`, das ein Rechteck von der Größe des Bildes ist, mit nur einem Sägezahn unten. 

Etwas wie das

Das machen wir mit *zwei* Gradientenbildern. Das erste Bild ist der gleiche Sägezahndreieck wie oben, das auf `repeat-x` gesetzt und unten positioniert ist, sodass es sich nur am unteren Rand des Bildes wiederholt. Das zweite Bild ist ein weiterer Verlauf, der für die unteren 30px transparent ist (um den Sägezahn nicht zu beeinträchtigen), darüber opak ist (was im Demo von Schwarz nach Weiß geht) und die gesamte Größe des Elements einnimmt. 

Somit haben wir nun dieses keilförmige Stück mit einem einzelnen Dreieckssägezahn unten, und es nimmt die gesamte Höhe unseres Bannerbildes in zwei separaten Stücken ein. Schließlich können wir diese Stücke mit `mask-image` verwenden, indem wir sie horizontal über unser Bild wiederholen, und das sollte den gewünschten Effekt erzielen.

Und da haben wir es!