Masken und Beschneidungen/Masken

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

In guten Bildbearbeitungsprogrammen kann man mit Ebenenmasken Teile des Bilds verdecken. Nur wo die Maske durchsichtig ist, wirkt sich die Bildbearbeitung aus.

Im Unterschied zu einer Beschneidung mit clip-path, bei der außerhalb der Beschneideform stehende Teile einfach abgeschnitten werden, sind Masken Grafiken, die Sie auch für teiltransparente Schablonen mit weichen Übergängen verwenden können. Unter den Masken befindliche Objekte sind nur in den transparenten Bereichen sichtbar. Dies können Sie z.B. durch Verläufe oder Filtereffekte erreichen.

Kurz: Masken sind Grafiken; Beschneidungen aber Pfade.

Masken in CSS

Genau wie Beschneidungen waren Masken eine Domäne von SVG und wurden erst relativ spät in CSS implementiert.

Mit der CSS-Eigenschaft mask können Sie eine Schablone anlegen, die dann nur Teilbereiche einer beliebigen Form oder Grafik teilweise oder ganz sichtbar macht.[1][2] Die Eigenschaft ist eine Zusammenfassung der möglichen Einzelangaben mask-image, mask-origin, mask-clip und mask-border.

Sie ähnelt in vielem der background-Eigenschaft, sodass Ihnen einiges bekannt vorkommen dürfte!

Vorteil der CSS-Lösung gegenüber der „klassischen“ SVG-Vorgehensweise:

Das HTML besteht nur aus einem img-Element - alles andere findet im CSS statt!


Achtung!

CSS-Maskierungen werden von allen modernen Browsern unterstützt, es muss jedoch für ältere Versionen (Stand: Juli 2024) zusätzlich noch der Browser-Präfix -webkit- verwendet werden. --Matthias Scharwies (Diskussion) 08:56, 20. Jul. 2024 (CEST)

Die Eigenschaft mask-image referenziert die Grafik, die als Schablone oder Maske über ein HTML-Element gelegt wird.[3] Wie bei background-image kann dies eine externe Gafik oder ein Verlauf sein:

Maskenschablone mit externer Grafik So sieht's aus
.wolke {
	mask-image: url(clouds.svg);
	mask-size: contain;
	mask-repeat: no-repeat;
}

img:hover {
	mask: none;
}

Das Porträt wird von der Maske überlagert. Transparente Bereiche außerhalb der sichtbaren Wolke bleiben verdeckt; nur die Bereiche innerhalb der Wolken sind sichtbar.

  • mask-image: gibt die Referenz für die Maske an
  • mask-size: skaliert die Maske auf die größere Seite (contain)
  • mask-repeat: verhindert eine hier unerwünschte Kachelung

Exkurs: Icons färben

Wenn man Icon-Fonts verwendet, erhält man oft eine Vielzahl von einfarbigen SVGs, die man problemlos einbinden kann. Um sie zu färben, könnte man jedes SVG-Dokument öffnen und eine Füllfarbe definieren - nimmt man dort als Wert currentColor oder eine custom property kann man die Icons entsprechend färben.

Auf dem SELFHTML-Stammtisch wurde berichtet, dass man dies auch ohne Zugriff auf die SVGs erzielen kann:

1.a Icons färben So sieht's aus
.icon {
  mask-image: url(https://wiki-test.selfhtml.org/images/f/f3/Clouds.svg);	
  mask-size: contain;
 mask-repeat: no-repeat;
	width: 200px;
	height: 200px;
}

.blue {
  background-color: steelBlue;
}

.red{
  background-color: #c82f04;
  mask-size: 50%;
  mask-position: center top;
}

Unsere Icons bestehen aus quadratischen, farbig gefüllten Flächen. Mit mask-image wird das Icon eingebunden. Die schwarzen Flächen sind transparent und lassen den farbigen Hintergrund durchscheinen. Die Maske kann analog zur bekannten background-Eigenschaft mit mask-size und mask-position entsprechend formatiert werden.

ToDo (weitere ToDos)

Was haltet ihr von diesem Beispiel? Ich würde es drin lassen, weil es das System der Maske, die Licht durchscheinen lässt, noch einmal zeigt.

--Matthias Scharwies (Diskussion) 05:36, 25. Jul. 2024 (CEST)

teiltransparente Bereiche

Das obere Beispiel hätte man auch mit clip-path verwirklichen können. Besonders wirkungsvoll sind teiltransparente Bereiche, mit denen attraktive Effekte erzielt werden können:

Maskenschablonen mit CSS-Verläufen So sieht's aus
.linear {
	mask-image: linear-gradient(black, transparent); 
}

.radial { 
	mask: radial-gradient( black 40%, transparent 70%);
}

.radial2 {
  mask-image: radial-gradient(circle at 50% 37%, black 48%, rgb(0 0 0 / 0.6) 48%);
}

img:hover {
	mask: none;
}

Dies ist eine der Stärken von CSS, das mit CSS-Funktionen Grafiken selbst erzeugt werden können:

Diese Verläufe werden über die Bilder gelegt; schwarze Flächen sind durchscheinend, transparente werden weiß. Im dritten Beispiel ist der Kopf in der Bildmitte akzentuiert; die Außenflächen durch rgb(0 0 0 / 0.6) aber halbtransparent.

Beachten Sie: Die von der Maske verdeckten Teile sind nicht oder nur teilweise sichtbar, das maskierte Elemente selbst behälte aber seine vorherige Größe.

Ein Anwendungsfall für den linearen Verlauf des ersten Beispiels stellen Aufklappboxen dar, deren Inhalt unten ausgeblendet und dann auf Klick eingeblendet wird.

Hero-Images

Bsp 3.a: Maskenschablone mit CSS-Verlauf So sieht's aus
html {
    background-image: url(https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Marienburg_%28Malbork_castle%29.jpg/2560px-Marienburg_%28Malbork_castle%29.jpg);
    background-size: cover;
    background-repeat: no-repeat;
    background-position: center center;
}		

footer {
    background-color: DarkGreen;
    color: white;
    mask-image: linear-gradient(transparent, black 50%);
}

Das Hero-Image wird im html-Element als Hintergrundbild eingebunden. Damit die Schrift im Footer immer lesbar ist, erhält er einen dunkelgrünen Hintergrund, der mit mask-image von oben nach unten eingeblendet wird.

ToDo (weitere ToDos)

Soll/kann das Beispiel 3.a so im Tutorial bleiben?

Sollte der footer ein unauffälligeres Schwarz erhalten?

--Matthias Scharwies (Diskussion) 04:59, 25. Jul. 2024 (CEST)
Bsp 3.b: Maskenschablone mit CSS-Verlauf So sieht's aus
.hero {
  mask-image: linear-gradient(to bottom, transparent, #000),
    linear-gradient(to bottom, #000, transparent), 
    linear-gradient(to bottom, transparent, #000),
    linear-gradient(to bottom, #000, transparent),
    linear-gradient(to bottom, transparent, #000);
  mask-size: 18% 70%;
  mask-position: 0 100%, 25% 25%, 50% 50%, 75% 0, 100% 50%;
  mask-repeat: no-repeat;
  width: 100%
}

Das Bild ist nun Teil des Inhalts. Es wird mit mehreren Masken mit Transparenzen versehen, die das Hero-Image in mehrere Segmente teilen.

ToDo (weitere ToDos)

Soll/kann das Beispiel 3.b so im Tutorial bleiben?

@Felix -eine Kombinationen von background-image und mask-image ist mir hier nicht gelungen.

--Matthias Scharwies (Diskussion) 04:59, 25. Jul. 2024 (CEST)

Perforation mit mask-border

Die Eigenschaft mask-border ist eine Zusammenfassung der möglichen Einzelangaben mask-border-source, mask-border-slice, mask-border-width, mask-border-outset, mask-border-repeat und mask-border-mode. Die Angaben für *slice*, *width* und *outset* müssen dabei in genau dieser Reihenfolge stattfinden und durch einen Schrägstrich voneinander getrennt sein. Dabei ist *width* nur verwendbar, wenn Sie auch *slice* angeben, und *outset* ist nur möglich, wenn Sie auch *width* angeben. Wenn das für Ihre Zwecke nicht passt, müssen Sie die Einzeleigenschaften verwenden.

Dieses Beispiel vergleicht die mask-border-Eigenschaft mit der nicht-standardisierten -webkit-mask-box-image-Eigenschaft.

mask-border vs. -webkit-box-image So sieht's aus
.stamptiles {
	-webkit-mask-box-image: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/stampTiles.svg') 25% / 5% space;
	mask-border: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/stampTiles.svg') 25% / 5% space;
}

Das Foto erhält mit mask-border eine Randperforation. Da das Maskenbild so eingeteilt ist, dass die Performation jeweils 25% der Bildgröße hat, wird dies als Wert für *slice* angegeben. Damit die Zähnung nicht zu grob wird, verwenden wir auch die *width*-Angabe, diese muss mit einem Schrägstrich von der *slice*-Angabe getrennt werden. Schließlich wird die Art, wie sich das Bild entlang der Ränder wiederholen soll (*repeat*-Angabe) auf space gesetzt, wodurch der Browser die Größen so anpasst, dass sich entlang der Ränder eine ganzzahlige Wiederholung des Bildes ergibt. Die mask-border-Eigenschaft funktioniert (Stand: Juli 2024) nur im Safari.

Auf Chromium basierende Browser verwenden eine nicht-standardisierte -webkit-mask-box-image-Eigenschaft. Die Mozilla-Dokumentation beschreibt sie zwar, **aber falsch**. Angeblich sei es eine Sammeleigenschaft von -webkit-mask-box-image-source, -webkit-mask-box-image-outset und -webkit-mask-box-image-repeat, das stimmt jedoch nicht. -webkit-mask-box-image sammelt - genau wie mask-border - die Untereigenschaften für source, slice, width, outset und repeat (-webkit-mask-box-image-mode gibt es nicht), und verwendet die gleiche Syntax: slice, width und outset müssen durch Schrägstriche voneinander getrennt werden.

Firefox unterstützt Randmasken noch nicht, deshalb wird das Bild dort mit „normalem“ Rand dargestellt - progressive enhancement!

Exkurs: mask-size

Sie können Masken mithilfe der Eigenschaft mask-size skalieren.

Erlaubt sind dabei die Schlüsselwörter:

  • auto, Voreinstellung übernimmt die Maße der Grafik, keine Skalierung
  • contain, passt unter Beibehaltung des Seitenverhältnisses die größere Seite der Grafik in den Anzeigebereich ein
  • cover, passt unter Beibehaltung des Seitenverhältnisses die kleinere Seite der Grafik in den Anzeigebereich ein, ergibt eine vollständige Füllung des Anzeigebereiches
  • ein Paar Längenangaben

Die erste Angabe spezifiziert die gewünschte Breite, die zweite entsprechend die Höhe der Maske. Ist nur ein Wert gegeben, so wird die Höhe unter Beibehaltung des Seitenverhältnisses skaliert.

Beispiele für maskierte Bilder So sieht's aus
.umriss {
	mask-image: url(https://wiki.selfhtml.org/images/5/56/Deutschland-Umriss.svg#maskelement); 
	 mask-size: 50%;
	 mask-repeat: no-repeat;
}

Das Beispiel erhält eine Grafik mit einer Maske und einem Schieberegler input type="range". Mit ihm können Sie den Wert für mask-size und so die Größe der Schablone verändern.

Masken in SVG

Das Referenzieren von externen Grafiken als Schablone hat Nachteile: neben der Dateigröße wird ein zusätzlicher HTTP-Request nötig. Besser wäre es wie bei den Verläufen im ersten Beispiel unsere Schablone mit SVG direkt im HTML-Dokument zu notieren:

Mit dem mask-Element können Sie eine Schablone anlegen, die dann nur Teilbereiche einer beliebigen Form oder Grafik sichtbar macht[4][5]. Die Maskenform wird im Definitionsabschnitt festgelegt und mit ihrer id dem zu maskierenden Element über das mask-Attribut oder über CSS zugeordnet.

Beachten Sie: Anders als bei CSS ist der Standardwert für SVG-Masken alpha, sodass Sie für durchscheinende Flächen weiß (anstatt schwarz bei CSS) verwenden müssen!
Referenzieren einer SVG-Maske als Schablone So sieht's aus
<style>
.bubbles1 {
  mask-image: url(#mask1);
  mask-size:100%
}
</style>
...
<svg width="0" height="0" viewBox="0 0 450 450">
  <defs>
    <mask id="mask1">
      <rect fill="black" x="0" y="0" width="450" height="450" />
      <circle fill="white" r="50" />
      <circle fill="white" cx="125" cy="125" r="100" />
      <circle fill="white" cx="225" cy="300" r="100" />
      <circle fill="white" cx="350" cy="50" r="90" />
      <circle fill="white" cx="100" cy="400" r="100" />
      <circle fill="white" cx="400" cy="400" r="100" />	  	  
    </mask>
  </defs>
</svg>

Im (unsichtbaren) Definitionsabschnitt wird mit dem SVG-mask-Element eine Schablone angelegt, die hier aus mehreren circle-Elementen besteht. Sie erhalten eine weiße Füllung und werden so durchscheinend. Im rechten Bild werden diese Kreise mit SMIL eingeblendet.

Unterschied CSS vs SVG

Was mich immer wieder irritiert hatte: Im ersten Beispiel waren die schwarzen Flächen der CSS-Verläufe durchsichtig - im letzten Beispiel jedoch die weißen Flächen der SVG-Maske.

Zeit für ein bisschen Theorie:

  • alpha: In der Alphamaske wird nur der Wert des s berücksichtigt. RGB-Werte ohne Werte im Alphakanal erhalten einen Wert von 1.
  • luminance: Eine Luminanzmaske wird in eine nicht sichtbare Ebene mit transparent-schwarzem Hintergrund projiziert. Die Farbwerte werden wie im Filter feColorMatrix mit der Einstellung luminance-to-alpha in Graustufen umgewandelt. Weiße Flächen sind undurchsichtig, schwarz ist vollständig transparent. Diese Farbwerte werden dann mit der Alphamaske kombiniert.

Mit diesen Werte können Sie Ihre Schablone über mask-mode und mask-type entsprechend anpassen.

Text als Maskenschablone

Während CSS-Masking relativ neu ist und erst im letzen Jahr (2023) weitreichende Browserunterstützung erhalten hat, gibt es SVG-Masking bereits seit über 20 Jahren. Heute erscheint das Anlegen eigener Masken in XML-Schreibweise umständlicher als das Einbinden einer CSS-Funktion mit einem Verlauf.

Es lassen sich aber noch immer Effekte erzielen, die in CSS so unmöglich sind:


Text als Maskenschablone So sieht's aus
    <defs>
        <mask id="Maske" maskUnits="userSpaceOnUse"
              x="0" y="0" width="200" height="80">
            <rect x="0" y="0" width="100" height="80" fill="white"/>
        </mask>

        <text id="Text" x="84" y="48" 
              font-size="20" font-weight="bold" text-anchor="middle">
            schwarz &amp; weiß 
        </text>
    </defs>

    <rect x="100" y="10" width="95" height="60"  />
  
    <use href="#Text" fill="white"/>
    <use href="#Text" fill="black" mask="url(#Maske)"/>

Der Hintergrund besteht aus einem schwarzen Rechteck (Ohne Angabe einer Füllfarbe wird der Standardwert schwarz verwendet.).

Der Text wird als symbol definiert und mit use zweimal aufgerufen:

  1. als weiße Schrift und dann noch
  2. als schwarze Schrift mit der Maske:

Die Maske beinhaltet ein weißes Rechteck, das die linke Hälfte des Hintergrunds verdeckt. Sie erhält mit maskUnits absolute oder relative Koordinaten; hier wird mit userSpaceOnUse das aktuelle Koordinatensystem verwendet. (Standardwert ist objectBoundingBox, sodass die Koordinaten sich an das zu beschneidende Element anpassen).

Die Füllfarbe der Maskenform bestimmt den Grad der Transparenz der Maske. Sie können mit Verläufen weiche Übergange der Transparenzen erreichen. Je näher der Farbwert bei Weiß (#ffffff) liegt, desto deckender ist die Maske, je näher bei Schwarz (#000000), desto transparenter.[6]

teiltransparente Bereiche mit SVG-Masking So sieht's aus
<defs>
    <linearGradient id="Verlauf1"
                    x1="0%"   y1="0%"
                    x2="100%" y2="0%">
        <stop offset="0%"   stop-color="white"/>
        <stop offset="100%" stop-color="black"/>
    </linearGradient>

    <mask id="Maske3" x="0" y="0" width="800" height="120" >
        <rect x="0" y="0"  width="800" height="120"
            fill="url(#Verlauf1)"/>
    </mask>
</defs>

<text x="10" y="100" >
    Text im Hintergrund
</text>
<rect x="1" y="1" width="800" height="120"
    style="fill: steelBlue; mask: url(#Maske3)"/>

Über den Text wird eine Maske gelegt, die mit einem Verlauf gefüllt ist. Die weißen Bereiche geben keine Transparenz, so dass die blaue Farbe sichtbar bleibt, nach rechts werden die Schwarz-Anteile größer, so dass die Maske transparenter wird.

Weiche Kanten mit SVG

Weiche Kanten mit SVG-Masking So sieht's aus
  <defs>
    <lineargradient id="Verlauf1"
                    x1="0%"   y1="0%"
                    x2="100%" y2="0%">
        <stop offset="0%"  stop-color="white"/>
        <stop offset="80%" stop-color="black"/>
    </lineargradient>
	
	<radialGradient id="Verlauf2">
        <stop offset="90%"  stop-color="white"/>
        <stop offset="100%" stop-color="black"/>
    </radialGradient>

    <mask id="Maske4">
      <circle cx="200" cy="200" r="200"
            fill="url(#Verlauf1)" />
    </mask>

    <mask id="Maske5">
      <circle cx="200" cy="200" r="200"
            fill="url(#Verlauf2)" />
    </mask>
  </defs>

  <image x="0" y="0" width="400px" height="400px" transform="translate(50,0)"
    href="http://wiki.selfhtml.org/images/a/a6/Lauf-1.jpg"
	mask="url(#Maske4)">
    <title>Lauf an der Pegnitz - normal ohne Filter</title>
  </image>	

  <image x="0" y="0" width="400px" height="400px" transform="translate(500,0)"
    href="http://wiki.selfhtml.org/images/a/a6/Lauf-1.jpg"
	mask="url(#Maske5)">
    <title>Lauf an der Pegnitz - normal ohne Filter</title>
  </image>

In diesen Beispielen werden die Bilder durch Masken verdeckt. Während das linke Bild durch einen linearen Verlauf nach rechts hin ausgeblendet wird; wird das rechte Bild durch eine Maske mit einem radialen Verlauf verdeckt - die Außenbereiche werden verdeckt. Im Unterschied zu einer Beschneidung sorgt der Verlauf für weiche Kanten.


Weiche Kanten durch radiale Verläufe lassen sich nur bei kreisförmigen Elementen erzeugen. Bei anderen Formen kann man einen Filter verwenden:

Weiche Kanten mit SVG-Filter So sieht's aus
  <defs>
    <filter id="Filter1" x="-.33" y="-.33" width="1.5" height="1.5">
      <feGaussianBlur stdDeviation="15"/>
    </filter>

    <mask id="Maske6" x="0" y="0" width="400" height="400">
      <circle cx="200" cy="200" r="175" 
	        fill="white" filter="url(#Filter1)"/>
    </mask>

    <mask id="Maske7" x="0" y="0" width="400" height="400">
      <rect x="25" y="25" width="350" height="350" 
	        fill="white" filter="url(#Filter1)"/>
    </mask>
  </defs>

  <image x="0" y="0" width="400px" height="400px" transform="translate(50,0)"
    href="http://wiki.selfhtml.org/images/a/a6/Lauf-1.jpg"
	mask="url(#Maske6)">
    <title>Lauf an der Pegnitz - normal ohne Filter</title>
  </image>	

  <image x="0" y="0" width="400px" height="400px" transform="translate(500,0)"
    href="http://wiki.selfhtml.org/images/a/a6/Lauf-1.jpg"
	mask="url(#Maske7)">
    <title>Lauf an der Pegnitz - normal ohne Filter</title>
  </image>

Den Masken wurde ein Weichzeichnungsfilter feGaussianBlur zugeordnet. Da die Abmessungen des Filter größer als die der Grafik sind, wurden die Abmessungen der für die Masken benutzten Grundformen (Kreis links und Rechteck rechts) entsprechend verkleinert.

Huete kann man so etwas mit dem drop-shadow()-Filter direkt in CSS erzeugen!

Schlagschatten


Weblinks

  1. W3C: Masking
  2. MDN: Mask
  3. Apply effects to images with CSS's mask-image property von Rachel Andrew (2020)
  4. W3C: MaskElement
  5. Maskierung, mask und mask (wikibooks.de.org)
  6. https://tutorials.jenkov.com/svg/mask.html#mask-example