Ich war extrem aufgeregt, als ich zum ersten Mal hörte, dass clip-path: path() nach Firefox kommt. Stell dir vor, du könntest einfach eine atmende Box wie die unten mit nur einem HTML-Element und sehr wenig CSS programmieren, ohne SVG oder eine riesige Liste von Punkten in der polygon()-Funktion zu benötigen!
Chris war auch begeistert von der ersten Implementierung.
Wie lustig wäre das denn

Ich beschloss, es auszuprobieren. Ich ging zu CodePen, gab eine <div> im HTML-Panel ein, gab ihr Abmessungen in Viewport-Einheiten, damit sie gut skaliert, fügte einen background hinzu, damit ich sie sehen konnte. Dann ging ich zu MDN, um einige Anwendungsbeispiele zu prüfen... und meine flauschige Wolke der Träume begann zu zerplatzen!
Beachten Sie, dass clip-path: path() nur in Firefox 63-70 mit dem auf true gesetzten Flag layout.css.clip-path-path.enabled in about:config funktioniert und in Firefox 71+ ohne Aktivierung eines Flags. (Quelle: MDN.)
Das waren die Beispiele, die ich gefunden habe
path('M0 200L0 110A110 90 0 0 1 240 100L 200 340z')
path('M.5 1C.5 1 0 .7 0 .3A.25 .25 1 1 1 .5 .3 .25 .25 1 1 1 1 .3C1 .7 .5 1 .5 1Z')
Was sind das für Koordinaten? Die traurige Antwort ist: Pixelwerte! Diese werden verwendet, weil die path()-Funktion einen SVG <path>-String als Argument nimmt, der – ähnlich wie der Wert des SVG d-Attributs auf einem <path>-Element – nur eine Art von Koordinatenwert enthält: Einheitenlose Pixel. Im SVG-Fall skalieren diese Pixel mit der viewBox des <svg>-Elements, aber innerhalb der CSS path()-Funktion skalieren sie überhaupt nicht!
Das bedeutet, dass das Element immer auf denselben festen Bereich zugeschnitten wird, wenn wir ein responsives Element mit einem path()-Wert für die clip-path-Eigenschaft haben. Betrachten wir zum Beispiel ein Quadrat .box mit einer Kantenlänge von 35vw. Wir schneiden es mit der path()-Funktion zu einer Herzform
clip-path: path('M256 203C150 309 150 309 44 203 15 174 15 126 44 97 73 68 121 68 150 97 179 68 227 68 256 97 285 126 285 174 256 203')
Diese Herzform behält die gleiche Größe, während sich die Abmessungen unseres tatsächlichen .box-Elements mit dem Viewport ändern

path().Das sind schlechte Nachrichten hier im Jahr 2020, wo Responsive Design der Standard und nicht die Ausnahme ist. Abgesehen von dem seltenen Fall, dass das zu beschneidende Element tatsächlich eine feste Pixelgröße hat, ist die path()-Funktion völlig nutzlos! Wir sind heute immer noch besser dran, ein tatsächliches SVG zu verwenden, oder sogar einen polygon()-Näherungswert für clip-path. Kurz gesagt, path() muss noch verbessert werden, obwohl es an den Start gegangen ist.
Amelia Bellamy-Royds hat zwei Möglichkeiten vorgeschlagen
Option 1: Erlauben von
calc()-Werten/Einheiten innerhalb derpath-Daten. Dies würde wahrscheinlich geschehen, indem die SVGpath-Syntax im Allgemeinen erweitert wird.Option 2: Definieren Sie
viewBoxin derclip-path-Deklaration, skalieren Sie den Pfad, um ihn anzupassen.
Ich bevorzuge persönlich die erste Option. Der einzige Vorteil, den die zweite gegenüber der Verwendung von SVG bietet, ist die Tatsache, dass wir kein tatsächliches SVG einfügen müssen. Dennoch wird das Einfügen eines tatsächlichen SVG immer eine bessere Unterstützung haben.
Die erste Option könnte jedoch eine enorme Verbesserung gegenüber der Verwendung von SVG darstellen – zumindest genug, um die Verwendung von clip-path für ein HTML-Element anstelle der Einbettung eines SVG darin zu rechtfertigen. Betrachten wir die atmende Box am Anfang dieses Beitrags. Mit SVG haben wir die folgende Markierung
<svg viewBox='-75 -50 150 100'>
<path/>
</svg>
Beachten Sie, dass die viewBox so eingestellt ist, dass der Punkt 0,0 genau in der Mitte liegt. Das bedeutet, dass wir die Koordinaten der oberen linken Ecke (d. h. die ersten beiden viewBox-Werte) gleich dem Minus der halben viewBox-Abmessungen (d. h. den letzten beiden viewBox-Werten) setzen müssen.
In SCSS legen wir die Kantenlänge ($l) der anfänglichen quadratischen Box als kleinste viewBox-Abmessung fest (was der kleinste der letzten beiden Werte ist). Das ist in unserem Fall 100.
Wir beginnen den Pfad an der oberen linken Ecke unserer quadratischen Box. Das bedeutet einen move to (M) Befehl zu diesem Punkt, mit Koordinaten, die beide gleich dem Minus der halben Kantenlänge sind.
Dann gehen wir zur unteren linken Ecke. Dies erfordert das Zeichnen einer vertikalen Linie mit einer Länge, die der Kantenlänge ($l) entspricht und nach unten, in die positive Richtung der y-Achse, verläuft. Wir verwenden also den Befehl v.
Als Nächstes gehen wir zur unteren rechten Ecke. Wir zeichnen eine horizontale Linie mit einer Länge, die der Kantenlänge ($l) entspricht und nach rechts, in die positive Richtung der x-Achse, verläuft. Wir verwenden den Befehl h, um dies zu erreichen.
Die obere rechte Ecke zu erreichen bedeutet, eine weitere vertikale Linie mit einer Länge zu zeichnen, die der Kantenlänge ($l) entspricht. Wir verwenden also erneut den Befehl v – nur diesmal gehen wir in die entgegengesetzte Richtung der y-Achse, was bedeutet, dass wir dieselben Koordinaten verwenden, aber mit einem Minuszeichen.
Zusammengenommen erhalten wir das SCSS, mit dem wir die anfängliche quadratische Box erstellen können
.box {
d: path('M#{-.5*$l},#{-.5*$l} v#{$l} h#{$l} v#{-$l}');
fill: darkorange
}
Das generierte CSS (bei dem $l durch 100 ersetzt wurde) sieht so aus
.box {
d: path('M-50,-50 v100 h100 v-100');
fill: darkorange;
}
Das Ergebnis ist in der interaktiven Demo unten zu sehen, bei der das Überfliegen eines Teils der Pfaddaten den entsprechenden Teil in der resultierenden SVG-Grafik hervorhebt und umgekehrt
Siehe den Pen von thebabydino (@thebabydino) auf CodePen.
Wenn wir jedoch wollen, dass die seitlichen Kanten "atmen", können wir keine geraden Linien verwenden. Ersetzen wir diese durch quadratische Bézierkurven (q). Der Endpunkt bleibt derselbe, nämlich eine Kantenlänge nach unten entlang derselben vertikalen Linie. Wir bewegen uns um 0,#{$l}, um dorthin zu gelangen.
Aber was ist mit dem Kontrollpunkt, den wir vorher angeben müssen? Wir platzieren den Punkt vertikal in der Mitte zwischen dem Start- und dem Endpunkt, was bedeutet, dass wir uns um die Hälfte der Strecke zum Endpunkt nach unten bewegen.
Und sagen wir, wir platzieren ihn horizontal um ein Viertel der Kantenlänge in die eine oder andere Richtung. Wenn wir wollen, dass die Linien hervortreten, um die Box zu verbreiten, oder sie eindrücken, um sie zu verengen, müssen wir etwas Ähnliches tun
d: path('M#{-.5*$l},#{-.5*$l}
q#{-.25*$l},#{.5*$l} 0,#{$l}
h#{$l}
v#{-$l}'); /* swollen box */
d: path('M#{-.5*$l},#{-.5*$l}
q#{.25*$l},#{.5*$l} 0,#{$l}
h#{$l}
v#{-$l}'); /* squished box */
Dies wird in das folgende CSS kompiliert
d: path('M-50,-50
q-25,50 0,100
h100
v-100'); /* swollen box */
d: path('M-50,-50
q25,50 0,100
h100
v-100'); /* squished box */
Die interaktive Demo unten zeigt, wie dieser path funktioniert. Sie können über Pfaddatenkomponenten schweben, um sie auf der SVG-Grafik hervorzuheben. Sie können auch zwischen den aufgeblähten und gequetschten Versionen wechseln.
Siehe den Pen von thebabydino (@thebabydino) auf CodePen.
Das ist nur die linke Kante. Wir müssen dasselbe auch für die rechte Kante tun. Der Unterschied hier ist, dass wir uns von der unteren rechten Ecke zur oberen rechten Ecke bewegen, was nach oben ist (in die negative Richtung der y-Achse). Wir platzieren den Kontrollpunkt außerhalb der Box, um den breiten Ox-Effekt zu erzielen, was auch bedeutet, ihn rechts von seinen Endpunkten zu platzieren (in die positive Richtung der x-Achse). In der Zwischenzeit platzieren wir den Kontrollpunkt innen, um den engen Box-Effekt zu erzielen, was bedeutet, ihn links von seinen Endpunkten zu platzieren (in die negative Richtung der x-Achse).
d: path('M#{-.5*$l},#{-.5*$l}
q#{-.25*$l},#{.5*$l} 0,#{$l}
h#{$l}
q#{.25*$l},#{-.5*$l} 0,#{-$l}'); /* swollen box */
d: path('M#{-.5*$l},#{-.5*$l}
q#{.25*$l},#{.5*$l} 0,#{$l}
h#{$l}
q#{-.25*$l},#{-.5*$l} 0,#{-$l}'); /* squished box */
Das obige SCSS generiert das folgende CSS
d: path('M-50,-50
q-25,50 0,100
h100
q25,-50 0,100'); /* swollen box */
d: path('M-50,-50
q25,50 0,100
h100
q-25,-50 0,-100'); /* squished box */
Siehe den Pen von thebabydino (@thebabydino) auf CodePen.
Um den Atmungseffekt zu erzielen, animieren wir zwischen dem aufgeblähten und dem gequetschten Zustand
.box {
d: path('M#{-.5*$l},#{-.5*$l}
q#{-.25*$l},#{.5*$l} 0,#{$l}
h#{$l}
q#{.25*$l},#{-.5*$l} 0,#{-$l}'); /* swollen box */
animation: breathe .5s ease-in-out infinite alternate
}
@keyframes breathe {
to {
d: path('M#{-.5*$l},#{-.5*$l}
q#{.25*$l},#{.5*$l} 0,#{$l}
h#{$l}
q#{-.25*$l},#{-.5*$l} 0,#{-$l}'); /* squished box */
}
}
Da sich zwischen den beiden Zuständen nur das Vorzeichen der horizontalen Differenz zu den Kontrollpunkten unterscheidet (das Vorzeichen der ersten Zahl nach dem Befehl für die quadratische Bézierkurve q), können wir die Dinge mit einem Mixin vereinfachen
@mixin pdata($s: 1) {
d: path('M#{-.5*$l},#{-.5*$l}
q#{-.25*$s*$l},#{.5*$l} 0,#{$l}
h#{$l}
q#{.25*$s*$l},#{-.5*$l} 0,#{-$l}')
}
.box {
@include pdata();
animation: breathe .5s ease-in-out infinite alternate
}
@keyframes breathe { to { @include pdata(-1) } }
Das ist ziemlich genau das, was ich für die eigentliche Demo der atmenden Box mache, obwohl die Bewegung etwas dezenter ist. Dennoch tut dies absolut nichts für das generierte CSS – wir haben immer noch zwei lange, hässliche und fast identische Pfade im kompilierten Code.
Wenn wir jedoch ein <div> verwenden könnten, das mit einem clip-path: path() beschnitten wird, das alle Arten von Werten unterstützt, einschließlich calc()-Werten, dann könnten wir das Vorzeichen zu einer benutzerdefinierten Eigenschaft --sgn machen, die wir dann mit Hilfe von Houdini zwischen -1 und 1 animieren könnten.
div.box {
width: 40vmin; height: 20vmin;
background: darkorange;
--sgn: 1;
clip-path: path(M 25%,0%
q calc(var(--sgn)*-25%),50% 0,100%
h 50%
q calc(var(--sgn)*25%),-50% 0,-100%);
animation: breathe .5s ease-in-out infinite alternate
}
@keyframes breathe { to { --sgn: -1 } }
Dies zu können würde einen großen Unterschied machen. Unser Element würde sich schön mit dem Viewport skalieren, und so auch die atmende Box, die wir daraus ausschneiden. Und, was am wichtigsten ist, wir müssten diesen Clipping-Pfad nicht wiederholen, um die beiden verschiedenen Versionen davon (die aufgeblähte und die gequetschte) zu erhalten, da die Verwendung der Vorzeichen-Benutzereigenschaft (--sgn) innerhalb eines calc()-Wertes den Trick machen würde. Wie die Dinge im Moment stehen, ist clip-path: path() jedoch ziemlich nutzlos.
Ich bin letzte Woche auf dieses Problem gestoßen und dachte, ich könnte clip-path: path für etwas verwenden, aber selbst mit SVG konnte ich keine Lösung finden.
Mein Problem war, dass ich ein "Ding" mit fester Größe in der unteren Ecke mit einem "Ding mit responsiver Größe" haben wollte, das sich von ihm zur oberen Ecke erstreckt, innerhalb einer Box mit variabler Höhe. Ging nicht.
Ich bräuchte etwas wie calc( 100% – 40px ) für die Höhe des "Dings mit responsiver Größe", und ich sehe keinen Weg, das in CSS-Pfaden noch in SVG zu tun.
CSS-Maskenränder (auch
webkit-mask-box-imagegenannt) sind perfekt dafür (https://css-tricks.de/almanac/properties/m/mask-border/)!Ähnlich wie bei
border-imagekönnen Sie vier Ecken, vier Seiten und sogar eine Füllung als Maske definieren und alle Feinsteuerungen haben.Der einzige Nachteil ist, dass Firefox es noch nicht unterstützt (alle anderen wichtigen Browser bereits).
In einigen Fällen ist ein Fallback mit einem normalen CSS-Maskenbild möglich, das CSS-Verläufe verwendet, die nicht durch die Elementdimensionen verzerrt werden.
Um diese Art von Dingen praktikabel zu machen, glaube ich, dass wir einen Online-Generator bräuchten. Ich sehe ein paar, die Ihnen helfen, den clip-path überhaupt erst zu erstellen, aber nichts, das Animationen ermöglicht.
so cool!
Ihre vorgeschlagene Pfadsyntax hat sehr schwerwiegende Auswirkungen auf die CSS-Grammatik im Allgemeinen.
Die von Ihnen zitierten funktionsfähigen Beispiele haben die Pfaddaten als String, umschlossen von Anführungszeichen. Das macht diesen String zu *einem* Parameter, der der
path()-Funktion übergeben wird. Ihr Vorschlag lässt die Anführungszeichen weg, und das hat scheinbare Vorteile: Der CSS-Parser könnte die Wert-Einheiten und verschachtelten (calc()) Funktionen auf die gleiche Weise interpretieren, wie er es allgemein tut (Sie verwenden in Ihren Beispielen auch Zeilenumbrüche innerhalb von Zeichenketten, aber das ist ein Syntaxfehler. Die Verwendung von Zeilenumbrüchen innerhalb einer Zeichenkette ist XML-Attribut-Syntax.)Ohne die Anführungszeichen werden die Pfaddaten zu einer Liste von Komponentenwerten (Parametern, könnte man sagen)
Das Problem hier ist, dass die SVG-Pfadsyntax Kommas optional macht, CSS aber nicht und stattdessen eine verschachtelte Liste erzeugt: Für einen Pfad wie
"M 0%, 0% 50%,0%"würde der Parser (konzeptionell) zurückgebenwas überhaupt nicht dem entspricht, was geliefert werden sollte, d. h. entweder eine flache Liste oder das Interpretieren von Befehlsargumentsequenzen
Noch schlimmer ist, dass die SVG-Syntax den Leerraum so weit wie möglich optional macht. Um konsistent und abwärtskompatibel zu sein, möchten Sie, dass
"M 0 0 H 100"äquivalent zu"M0,0H100"ist, wie es jetzt ist, aber auch zu"M0px0pxH100px". Aber die CSS Values Spec widerspricht demMeine Schlussfolgerung ist, dass, um die Neudefinition des gesamten CSS-Parsers, wahrscheinlich mit Breaking Changes, zu vermeiden, die Pfaddaten *als String* belassen werden müssen und ihre Interpretation dem SVG-Parser überlassen wird, mit hinzugefügten Einheiten und
calc()-Funktionen. Wie dievar()-Funktion dort einbezogen werden soll, weiß ich nicht.SVG hat also auch clipPathUnits=„objectBoundingBox“. Dies erfordert, dass alle einheitenlosen Zahlen dezimale Vielfache der Breite (für X-Werte) und Höhe (für Y-Werte) des umschließenden Elements sind. Das ist offensichtlich responsiv. Angesichts dessen bin ich überrascht, dass die CSS-Pfad-Leute es nicht zur Standardeinheit für Pfade gemacht haben. Könnte es vielleicht noch als Option hinzugefügt werden?