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:

1. Maskenschablone mit externer Grafik ansehen …
.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.

Allerdings kann man dies auch ohne Zugriff auf die SVGs erzielen:

2. Icons färben ansehen …
.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.

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:

3. Maskenschablonen mit CSS-Verläufen ansehen …
.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 4.a: Maskenschablone mit CSS-Verlauf ansehen …
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: white;
    mask-image: linear-gradient(transparent, black 50%);
}

Das Hero-Image wird im html-Element als Hintergrundbild eingebunden. Allerdings ist es nicht ganz sichtbar - über das untere Drittel wird der footer eingeschoben, der den Seiteninhalt mit mask-image von oben nach unten ausblendet.

Bsp 4.b: Maskenschablone mit CSS-Verlauf ansehen …
.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.

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,
  • outset ist nur möglich, wenn Sie auch width angeben. Wenn das für Ihre Zwecke nicht passt, müssen Sie die Einzeleigenschaften verwenden.[4]

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

mask-border vs. -webkit-mask-box-image ansehen …
.stamptiles {
	-webkit-mask-box-image: url('stampTiles.svg') 25% / 5% space;
	mask-border: url('stampTiles.svg') 25% / 5% fill;
}

Das Foto erhält für die Chromium-Browser mit -webkit-mask-box-image 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. Hier muss, da ansonsten die Bildmitte ausgeschnitten wird, abweichend vom oberen das Schlüsselwort fill gesetzt werden.

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


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[5][6]. 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!
5. Referenzieren einer SVG-Maske als Schablone ansehen …
<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:


6. Text als Maskenschablone ansehen …
    <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.[7]

7. teiltransparente Bereiche mit SVG-Masking ansehen …
<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

8. Weiche Kanten mit SVG-Masking ansehen …
  <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:

9. Weiche Kanten mit SVG-Filter ansehen …
  <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. mask-border (CSS Almanach) (css-tricks.com)
  5. W3C: MaskElement
  6. Maskierung, mask und mask (wikibooks.de.org)
  7. https://tutorials.jenkov.com/svg/mask.html#mask-example