Masken und Beschneidungen

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Alle Elemente einer Webseite werden im Box-Modell zunächst als rechteckige Flächen dargestellt.

Mit einfachen CSS-Eigenschaften wie border-radius oder overflow:hidden kann man diese rechteckigen Boxen bereits optisch verändern oder teilweise verbergen.

Oft möchte man jedoch nicht nur rechteckige oder abgerundete Formen erzeugen, sondern gezielt bestimmte Bereiche eines Elements sichtbar machen. Für komplexere und frei definierbare Formen bietet CSS die Eigenschaft clip-path.

Ursprünglich aus SVG übernommen, kann man damit Elemente direkt per CSS zuschneiden. So lassen sich Polygone, Kreise oder sogar freie Formen definieren, ohne eine Grafik vorher bearbeiten zu müssen.

  • Beschneidungen mit clip-path

    sichtbare Bereiche von Elementen flexibel steuern

  • Masken in CSS + SVG

    teiltransparente Bildmanipulationen

  • Textumfluss mit Shapes
    Shapes ermöglichen interessante Layouts mit fließendem Text um grafische Objekte herum.


Beschneidungen mit clip-path

Die CSS-Eigenschaft clip-path ermöglicht es, festzulegen, dass nicht die gesamte Box des Elementes dargestellt wird.[1] Dabei sind, anders als bei der Vorgänger-Eigenschaft clip, nicht nur Rechtecke, sondern auch viele andere Grundformen möglich. Allerdings sind aber nur scharfe Kanten möglich, weiche Übergänge können mit mask-image erreicht werden.

Als Wert für die clip-path-Eigenschaft kann man Grundformen festlegen. Die Bemaßungen dieser Grundformen beziehen sich auf eine so genannte Referenzbox,[2] bei der es sich standardmäßig um die Außenkante der Elementumrandung (border bzw. stroke) handelt.


Beschneidung mit Grundformen ansehen …
.clip-rect {
  clip-path: inset(80px 110px 190px 90px round 5px);
}
.clip-circle {
  clip-path: circle(100px at 105px 100px);
}
.clip-oval {
  clip-path: ellipse(90px 140px at 125px 140px);
}
.clip-star {
  clip-path: polygon(75px 150px, -4px 150px, 60px 203px, 26px 288px, 120px 248px, 206px 298px,
                     177px 211px, 239px 150px, 158px 150px, 122px 36px, 77px 150px);
}
img:hover {                                            
  clip-path: none;
}
Beachte: Das Box-Modell bleibt, nur die Sichtbarkeit ändert sich.
Die beschnittenen Elemente behalten ihre vorherige Größe, Klickbereich und Layout bleiben unverändert — nur was man sieht wird beschnitten.

Bei :hover erhält die Eigenschaft den Wert

  • none: (Standardwert) das Bild wird nicht zugeschnitten, sondern normal dargestellt

Das Rechteck wird mit der inset()-Funktion gezeichnet:

  • Sie legt als Beschneidungskontur ein Rechteck fest. Die Angaben gehen von der linken, oberen Ecke aus. Eine Angabe von Eckradien ist möglich.
    Im Gegensatz zur rect()-Funktion geben die Argumente für inset() an, welchen Abstand die Kante des Beschneidungsrechtecks von der entsprechenden Kante der Referenzbox haben soll.
    Alternativ gibt es noch die xywh()-Funktion. Sie verwendet die Werte, die man bei einem rect-Element in SVG verwenden würde: x und y Koordinate der oberen linken Ecke sowie Breite und Höhe. Eine Angabe von Eckradien ist möglich.

Der Kreis wird mit der circle()-Funktion beschnitten. circle(100px at 105px 100px) legt den Radius und den Mittelpunkt fest.

Für das Oval wurde die ellipse()-Funktion verwendet. Sie enthält zusätzlich neben dem x-Radius einen weiteren Wert für den y-Radius: ellipse(90px 140px at 125px 140px)

Der Stern wurde mit der polygon-Funktion gezeichnet, die wir im nächsten Abschnitt genauer vorstellen:

polygon() - Objekte von Punkt zu Punkt zeichnen

Polygone oder Vielecke sind geometrische Figuren, in denen Punkte durch Linien miteinander verbunden werden:

Polygone ansehen …
.warning {
 background: yellow;
 clip-path: polygon(5% 5%, 95% 5%, 50% 95%);
}

.raute {
 background: skyblue;
 clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0 50%);
}

Mit der polygon()-Funktion werden Beschneidungskonturen festgelegt. Dabei werden sowohl width und height als auch die Mindestmaße des Grid über die custom property --size festgelegt, damit sich die Proportionen nicht verziehen.

"Koordinatensystem"

In diesem Koordinatensystem werden Punkte festgelegt, zwischen denen Linien gezogen werden, die dann die Konturen einer Figur ergeben.

Woher erhalten wir solche Koordinaten? Es gibt eine Vielzahl von Generatoren.[3] Temani Afif zeigt in seinem Tutorial aber auch, dass es sinnvoller sein kann, anstelle von Magic Numbers mit Math-Funktionen und custom properties Sterne und andere regelmäßige Formen im CSS zu definieren und den Browser rechnen zu lassen.[4]

Clip-path-im-Seiteninspektor.png

Eine weitere Möglichkeit ist es, eine bestehende Beschneidung im Seiteninspektor zu öffnen und dann die Punkte zu verschieben.[5]


Was mit clip-path leider nicht geht, ist es den Objekten eine border oder einen drop-shadow() zu geben. Dieser wird vor dem Beschneiden gezeichnet und damit dann weggeschnitten.

Fake border Hack

Da sowohl border als auch box-shadow durch das Beschneiden entfernt werden, muss man die Form duplizieren.

Fake border Hack
.shape {
  position: relative;
  padding: 1em;
  inset: 2px;
  --shape: polygon(0% 0%, 90% 0%, 100% 10%, 100% 100%, 0% 100%);
  clip-path: var(--shape);
}
.shape::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--border-color);
  clip-path: var(--shape);
  z-index: -1;
}

Das Element mit der Klasse .shape enthält eine custom property mit dem Beschneidungspfad, der das Element durch clip-path beschneidet. Durch die inset-Eigenschaft erhält das Element einen Einzug von 2px.

Ein Pseudoelement mit der gleichen Beschneidungsform, aber ohne Einzug wird mit z-index: -1 hinter dem Element dargestellt. Der sichtbare Teil dient so als Randlinie.

path() – Linien und Kurven

In SVG tritt der Einsatz von Polygonen gegenüber Pfaden zurück, da dieser viele Vorteile haben:[6] [7]

  • Es können zusätzlich zu Geraden auch Bögen und Kurven verwendet werden.
  • Anstelle absolut positionierter Punkte, kann ein Ausgangspunkt (m x,y) definiert und von da an relativ positioniert werden, was spätere Verschiebungen erleichtert.


path()-Funktion als Beschneideform ansehen …
.star {
	clip-path: path('m 40,225 l 75-225 l75,225 l-190-140 h 235');
}
.heart {
	clip-path: path('m 125,225 l -100,-100 a 50,50 90 0,1 100,-75 a 50,50 90 0,1 100,75 z'); 
}
.beziergon {
	clip-path: path('m 5,30 c80,-80 160,80 230,-20 c-40,80 40,160 0,220 c-80,60 -140,-80 -220,10 c-50,-80 60,-140 -10-210'); 
}	
img:hover {
	clip-path: none;
}

shape() – die responsive Alternative

Warum shape(), wenn path() schon Kurven kann?
Ein path() ist eine starre SVG-Zeichnung. Ändert sich die Elementgröße, muss der gesamte Pfad neu berechnet werden. Bei shape() mit % und calc() passt sich alles automatisch an.

Tab-Navi mit shape() ansehen …
nav a {
  --ear: 2em;
  --radius: .75em;

  clip-path: shape(
    from 0% 100%,
    line to 0% var(--radius),
    curve to var(--radius) 0% with 0% 0%,
    line to calc(100% - var(--ear) - var(--radius)) 0%,
    curve to calc(100% - var(--ear)) var(--radius) with calc(100% - var(--ear)) 0%,
    line to calc(100% - var(--ear)) 50%,
    curve to 100% calc(50% + var(--radius)) with calc(100% - var(--ear)) calc(50% + var(--radius)),
    curve to calc(100% + var(--ear)) 100% with 100% calc(50% + var(--radius)),
    close
  );
}

Diese shape()-Definition erzeugt einen Tab mit:

  • abgerundeter linker oberer Ecke,
  • einer geraden Oberkante,
  • und einem nach rechts herausragenden „Ohr“ bzw. Zipfel.

Mit shape() werden Formen Schritt für Schritt aus Linien und Kurven aufgebaut.

Ist bereits ein SVG-Pfad vorhanden, kann er z.B. mit einem Online-Tool [8] in Shape-Angaben übersetzt werden.

url() – Referenzieren einer Beschneidungsform

Lange waren solche Beschneidungen nur SVG vorbehalten. Mit der oben bereits erwähnten clip-path-Eigenschaft kann man solche Beschneideformen in SVG anlegen und dann in CSS referenzieren: [9]

Beschneidung eines HTML-Elements durch ein SVG ansehen …
  <style>
img {
	clip-path: url(#beschneideform);
}	
  </style>
<figure>
  <img src="https://wiki.selfhtml.org/images/5/57/Alaska-rockies.jpg" alt="Rockie Mountains">
</figure>
<svg>
	<defs>
		<clipPath id="beschneideform">
			<text stroke="black" x="0" y="160">USA</text>
			<path transform="translate(90,145) scale(0.2)"	d="..."/ >
		</clipPath>
	</defs>
</svg>

Die Webseite enthält ein über ein img-Element eingebundenes Bild und ein nicht sichtbares SVG, das nur aus einem Definitionsabschnitt besteht. In diesem wird eine Beschneideform - bestehend aus einem text-Element und einem als Pfad vorhandenem Umriss der USA festgelegt.

Die clip-path-Eigenschaft erhält als Wert die über die url()-Funktion referenzierte Beschneideform.

Anwendungsbeispiele

Vorher-Nachher-Slider

Es ist möglich, die clip-path-Eigenschaft mit CSS-Animation zu animieren.[10] Dies kann für interessante Effekte, wie ein image reveal oder einen Vorher-Nachher-Vergleich (comparison slider) verwendet werden.


Vorher-Nachher-Vergleich mit Schieberegler ansehen …
#comparison figure:nth-of-type(2) {
  clip-path: inset(0 0 0 calc(var(--offset)*1%));
  height: auto;
}

In diesem Beispiel werden zwei Bilder (bzw. die sie umschließenden figure-Elemente) absolut übereinander gelegt, wobei das im Markup später vorkommende Bild das erste verdeckt.

Ein Schieberegler - ein input type="range"-Element - ermöglicht es, das verdeckende Bild so zu beschneiden, dass das darunterliegende sichtbar wird.

Ein kleines Script setzt mit document.body.style.setProperty('--offset', slider.value); die custom property --offset, die dann in die inset()-CSS-Funktion eingesetzt wird. Um einen Prozentwert zu erhalten, wird --offset mit der calc()-Funktion mit 1% multipliziert.

Der Zeiger des Schiebereglers könnte mit CSS noch über die gesamte Höhe des Vergleichs dargestellt werden.[11]

Wandernder Bildausschnitt

In diesem Bild werden die Details nacheinander wie mit einem Sucher „eingefangen“ (Circular Reveal):

img {
  width: 100%;
  height: auto;
  clip-path: circle(5% at -5% 50%);
  animation: reveal 15s linear infinite;
}

@keyframes reveal {
  0% { clip-path: circle(5% at -5% 50%); }
  10% { clip-path: circle(22% at 6% 50%); }
  20% { clip-path: circle(13% at 17% 50%); }
  

Die Beschneideform besteht aus einem Kreis. Das entsprechende circle-Element enthält zwei animate-Elemente, die cx und r animieren. Der Suchscheinwerfer ist außerhalb des sichtbaren Bereichs, sodass das gesamte Bild ausgeschnitten ist.

Im Verlauf der Animation bewegt sich die Beschneideform über das Bild und schneidet den Bereich der Suchscheinwerfers aus dem Bild und stellt diesen dar.

Zusammenspiel von clip-path und shape-outside

In diesem Beispiel wird ein Bild mit clip-path beschnitten und der Text umfließt dann das beschnittene Bild durch eine Festlegung mit shape-outside.

shape-outside: polygon() + clip-path: polygon() ansehen …
img {
  float: left;
  clip-path: polygon(0% 0%, 100% 50%, 0% 100%, 0% 0%);
  shape-outside: polygon(0% 0%, 100% 50%, 0% 100%, 0% 0%);
}

img:hover {
    shape-outside:polygon(0% 0%, 100% 0, 0% 100%, 0% 0%);
    clip-path: polygon(0% 0%, 100% 0%, 0% 100%, 0% 0%);
  }

Wenn du mit der Maus über das Bild fährst, verändert sich der mit clip-path festgelegte Bildausschnitt sowie der mit shape-outside definierte Textumfluss.

  • Textumfluss mit Shapes
    Shapes ermöglichen interessante Layouts mit fließendem Text um grafische Objekte herum.

Exkurs: clipPath (SVG)

Die oben vorgestellte clip-path-Eigenschaft lässt sich heute gleichermaßen auf HTML- und SVG-Elemente anwenden. Vorher konnte man Beschneidungen von Grafikobjekten nur in SVG realisieren.

Mit dem clipPath-Element kann man Ausschnitte oder Silhouettten im Definitionsabschnitt festlegen. Es kann beliebig viele Kind-Elemente wie Grundformen, Pfade oder Text haben. Sie bilden eine gemeinsame Grundfläche bzw. Silhouette, die den transparenten Bereich der Schablone darstellt. Die Fläche außerhalb der Schablone wird nicht dargestellt.

Beschneidung eines rechteckigen Bilds ansehen …
  <defs>
    <clipPath id="normal">
	  <rect x="0" y="0" width="160" height="120"/>
	  <circle cx="80" cy="120" r="80" />
    </clipPath>  
    <clipPath id="spitz">
      <path class="wappen" d="m200,0 h160 l-10,10 v130 a50,50 0 0,1 -50,50 a20,20 0 0,0 -20,10 a20,20 
                              0 0,0 -20,-10 a50,50 0 0,1 -50,-50 v-130z"/>
    </clipPath>  
    <clipPath id="elegant">
      <path class="wappen" d="M400,0 m80,0 q35,20 70,10 l10,10 q-20,70 0,100 q-10,50 -80,80 q-50,-20
                             -80,-80 q20,-35 0,-100 l10,-10 q23,15 70,-10" />
    </clipPath>  
  </defs>

 
  <image x="0"   y="0" width="160" height="200" href="Burg.svg" clip-path="url(#normal)" />
  <image x="200" y="0" width="160" height="200" href="Burg.svg" clip-path="url(#spitz)" />
  <image x="400" y="0" width="160" height="200" href="Burg.svg" clip-path="url(#elegant)" />

Die rechteckigen Bilder erhalten mit dem clip-path-Attribut unregelmäßige Beschneideformen.

Die Beschneideformen werden über ihre id im clip-path-Attribut des zu beschneidenden Elements referenziert.

Die erste Beschneideform besteht aus einem Quadrat und einem Kreis, die sich zur Wappenform ergänzen; die zweite und dritte Form werden von einem Pfad festgelegt. Um die Symmetrie der dritten Silhouette zu erhalten, wird nach dem ersten moveTo-Befehl M 400,0 noch ein relativer moveTo-Befehl m 80,0 zur Symmetrieachse notiert.

Um die Beschneideform zu akzentuieren, wurde der (unsichtbare) Beschneidepfad dupliziert und anschließend als Grafikobjekt dargestellt, wobei nur die Randlinie zu sehen ist und die Fläche dank fill: none; durchsichtig ist.

clip-rule

Analog zum Attribut fill-rule kann man beim Ausschnitt mit clip-rule Füllmethoden festlegen, falls sich einzelne Linien überschneiden:

  • nonzero: volle Fläche wird ausgeschnitten
  • evenodd: der "äußere" Hintergrund wird ausgeschnitten, die "inneren" Felder bleiben verdeckt
Beschneideform mit überschneidenden Linien ansehen …
 <defs>
    <clipPath id="eins">
	  <polygon points="200,10 140,198 290,78 110,78 260,198" clip-rule="nonzero"/>
    </clipPath> 
    <clipPath id="zwei">
	  <polygon points="200,10 140,198 290,78 110,78 260,198" clip-rule="evenodd"/>
    </clipPath>  

  </defs>
 
  <polygon id="original" points="200,10 140,198 290,78 110,78 260,198" /> 

  <image x="330" y="10" width="300" height="200" href="Alaska-rockies.jpg"/>
    
  <image x="10" y="0"  transform="translate(10,210)" width="300" height="200" 
    href="Alaska-rockies.jpg" 
    clip-path="url(#eins)" />

  <image x="10" y="0"  transform="translate(320,210)" width="300" height="200" 
     href="Alaska-rockies.jpg" 
     clip-path="url(#zwei)" />

In diesem Beispiel dient ein Stern als Beschneideform. Er besteht aus einem polygon mit sich überschneidenden Linien – eine Grafik die dies vermeidet, würde die doppelte Anzahl Linien erfordern. Im Definitionsabschnitt wird dieser Stern zweimal innerhalb eines ClipPath-Elements notiert; erhält aber unterschiedliche Werte im clip-rule-Attribut.

Nun wird eine Rastergrafik per image-Element referenziert. Mit dem clip-path-Attribut wird die Beschneideform, die das Polygon enthält, über das Foto gelegt.

Während der linke, untere Stern vollständig sichtbar ist, hat der rechts unten referenzierte Stern das Attribut clip-rule="evenodd", sodass der innenliegende Teil zusammen mit dem Außenbereich ausgeschnitten wird.

Beachte: clip-rule gilt nur für SVG-Elemente, die in einem <clipPath>-Element enthalten sind, und überschreibt dabei den Wert des Attributs „clip-rule“ des Elements, sofern vorhanden. Sie hat keinerlei Auswirkungen auf CSS-<basic-shape>-Elemente.

Suchscheinwerfer

In diesem Beispiel soll Text während des Lesens gehighlighted werden. In einer einfachen Variante könnte hinter dem Text (also im SVG-Markup vorher) ein gelber Suchscheinwerfer den Lesefluss nachahmen, indem er den Hintergrund von links nach rechts beleuchtet.

In unserem Fall soll auch die Textfarbe geändert werden, was nur über Umwege geht:

Hervorhebung von Text ansehen …
 <defs>
    <clipPath id="clipText">
        <text x="10" y="80">Energie des Verstehens</text>
    </clipPath>
</defs>
<circle id="light" cx="20" cy="60" r="50"  >
  <animate 
    attributeName="cx" 
    dur="6" 
    values="-50;750;" 
    repeatDur="indefinite"/>
  </circle>  
  
<g style="clip-path: url(#clipText);">
    <rect id="grundfarbe" width="100%" height="100%"/>
    <circle id="akzent" cx="20" cy="60" r="50" >
        <animate 
            attributeName="cx" 
            dur="6" 
            values="-50;750;" 
            repeatDur="indefinite"/>
    </circle>
</g>

Der Text ist nicht Teil des Inhalts, sondern der Beschneideform. Hintergrund ist das rect-Element mit der Grundfarbe des Texts. Daneben gibt es einen Kreis, der mit animate animiert wird. Seine Füllfarbe ist die Akzentfarbe rot. Beide Element sind innerhalb des g-Elements, auf das die Beschneideform angewandt wird. Nur der Text der Beschneideform ist sichtbar.

Da der Kreis mit der Akzentfarbe animiert wird, wandert er über die Schrift. Zur Verdeutlichung wurde ein gelber „Suchscheinwerfer“ hinzugefügt.

Siehe auch

  • Masken in CSS + SVG

    teiltransparente Bildmanipulationen

  • Textumfluss mit Shapes
    Shapes ermöglichen interessante Layouts mit fließendem Text um grafische Objekte herum.
  • Shape-Morphing
    eine Form fließend in eine andere verwandeln

Weblinks

  1. W3C: the clip-path property
  2. Diese Angaben können durch die optionale Angabe der Geometrie-Box, in dem das Zuschneiden stattfinden soll, erweitert werden. Diese Geometrie-Box wird nur für CSS-Formen als Zuschneidepfade festgelegt. Das clipPath-Element in SVG verwendet die border box des HTML-Elements als Referenz. Wenn das zugeschnittene Element ein HTML-Element ist sind folgende Schlüsselwerte möglich:
    • margin-box
    • border-box
    • padding-box
    • content-box
    Wenn die Grundformen mit clip-path auf ein SVG-Element angewandt werden, kann die Geometriebox folgende Werte annehmen:
    • fill-box
    • stroke-box
    • view-box – falls keine viewBox festgelegt ist, wird die nächstgelegene Viewbox verwendet. Falls eine ViewBox festgelegt ist, wird das Koordinatensystem dieser ViewBox verwendet.
  3. Generatoren
  4. The Modern Guide For Making CSS Shapes Temani Afif (smashingmagazine.com)
    .star {
      width: 200px;  
      aspect-ratio: 1;
      clip-path: polygon(50% 0, /* (1) */
        calc(50%*(1 + sin(.4turn))) calc(50%*(1 - cos(.4turn))), /* (2) */
        calc(50%*(1 - sin(.2turn))) calc(50%*(1 - cos(.2turn))), /* (3) */
        calc(50%*(1 + sin(.2turn))) calc(50%*(1 - cos(.2turn))), /* (4) */
        calc(50%*(1 - sin(.4turn))) calc(50%*(1 - cos(.4turn)))  /* (5) */
       ); 
    }
    
  5. CSS Tricks How To Master The clip-path Property Temani Afif (verpex.com)
  6. css-tricks: The CSS clip-path: path() function ships in Chrome 05.02.2021
  7. css-tricks: An Initial Implementation of clip-path: path();
  8. Path to CSS Shape: Convert SVG path data to CSS shape() function
  9. Sara Soueidan Clipping in CSS and SVG – The clip-path Property
  10. css-tricks: Animating with Clip-Path mit vielen guten Beispielen!
  11. Creating A Custom Range Input That Looks Consistent Across All Browsers Alyssa Holland (smashingmagazine.com)