Skizzenhafte Avatare mit CSS clip-path

Avatar of Chris Coyier
Chris Coyier am

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

Der folgende Text ist ein Gastbeitrag von Ryan Scherf. Ryan hat einen raffinierten Weg gefunden, Avataren grobe, unebene, abwechslungsreiche Kanten zu geben. So als wären sie mit einer Schere ausgeschnitten worden, und zwar von jemandem, der nicht besonders gut mit Scheren umgehen kann. Das Schöne daran ist, dass es sich von Natur aus um eine progressive Enhancement-Technik handelt und nur mit CSS realisiert werden kann.

Mit einer kreativen und unterhaltsamen Marke wie Quirky denken wir ständig darüber nach, wie wir diese Ausstrahlung ins Web bringen können. Auf der gesamten Website gibt es bei einigen Elementen einen „handgezeichneten“ Look. Ohne die Verwendung vieler Bilder ist es sehr schwierig, diese handgezeichnete Anmutung zu erzielen. Mit etwas leichter Trigonometrie und sehr grundlegenden Kenntnissen von CSS `clip-path` können wir dies mit relativer Leichtigkeit und guter Performance erreichen.

Was wir bauen. Beachten Sie die unebenen und abwechslungsreichen Kanten bei jedem.

Warum keine Bildmasken verwenden?

Zum Beispiel eine Maske, die in SVG definiert ist

img {
  mask: url(mask.svg) top left / cover;
}

Die mask-Eigenschaft kann auf externe SVGs oder auf im Dokument per ID definierte SVGs verweisen.

Aber was ist, wenn Sie für jeden einzelnen angezeigten Avatar eine einzigartige Form wünschen, nicht die gleiche Form? Sie könnten programmatisch viele verschiedene SVG-Formen generieren, um sie anzuwenden. Aber wir können dasselbe erreichen und diese mathematische Generierung erzielen, indem wir clip-paths mit (S)CSS generieren.

Wie ist die Browserunterstützung?

Die Browserunterstützung für clip-path, wenn es mit einem Formwert wie polygon() verwendet wird, ist Chrome 24+, Safari 7+, Opera 25+, iOS 7.1+, Android 4.4+. Firefox unterstützt clip-path nur mit dem in SVG definierten Pfad (das werden wir behandeln). Noch keine Unterstützung in IE.

Sie müssen -webkit-clip-path verwenden, da dies die einzige Möglichkeit ist, wie es derzeit unterstützt wird, aber wahrscheinlich am besten, auch clip-path dort einzubringen. Wenn IE oder Firefox dies auf diese Weise unterstützen, wird es wahrscheinlich ohne Präfix sein.

Clipping-Pfade im Überblick

Es gibt verschiedene Formwerte, die Sie für CSS-Clipping verwenden können, aber in unserem Fall ist die polygon-Form am besten geeignet, da sie uns die meisten Punkte und Flexibilität bietet, um unseren handgezeichneten Effekt zu erzeugen.

Sie übergeben polygon() eine Liste von X-, Y-Punktwerten, wie z. B.: <x0> <y0>, <x1> <y1>, ... <xn> <yn>. Dies zeichnet einen Pfad um Ihre Punkte **in der Reihenfolge** und schneidet alle Inhalte **außerhalb** der neu erstellten Form aus.

/* 
  This will create a Hexagon, with the first 
  point being the top tip of the shape 
*/

.hexagon {
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
}

Hier ist dieses einfache Beispiel in Aktion

Siehe den Pen Hexagon with clip-path von Chris Coyier (@chriscoyier) auf CodePen.

Keine beängstigende Mathematik

Unser Sechseck ist ziemlich cool, aber es erzeugt noch keinen wirklich skizzenhaften Effekt. Es ist ziemlich starr – zu wenige Linien. Der beste Weg, eine handgezeichnete Form zu betrachten, ist eine Reihe von kleinen Linien, die zwei Punkte verbinden. Je mehr Punkte wir haben, desto mehr kurze Linien erstellen wir. Tatsächlich könnten wir mit genügend Punkten eine polygon-Form erstellen, die so glatt ist, dass sie eine circle nachahmt.

Hier ist ein Beispiel für die Verwendung von 200 Punkten

Siehe den Pen 200 Points von Chris Coyier (@chriscoyier) auf CodePen.

Woher kommen die Punkte?

Hier kommt ein wenig Mathematik ins Spiel. Haben Sie vielleicht Trigonometrie in der High School belegt? Eine der grundlegenden Ideen, die Sie in diesem Kurs lernen, betrifft den **Einheitskreis**. Grundsätzlich gibt es eine feste Formel (unter Berücksichtigung von Pi), mit der jede Anzahl von Punkten um einen Kreis erzeugt werden kann.

Der Einheitskreis (via Wikipedia)

Wenn wir unsere Segmente verbinden würden, erhielten wir eine Form, die aussieht wie

Verbinden Sie die Punkte!

Immer noch etwas steif, aber auch etwas handgezeichneter.

Mehr Punkte!

Wir wissen, wie man Sechsecke und Kreise mit clip-path: polygon() erstellt, also wie bekommen wir ihn dazu, handgezeichnet auszusehen?

  • Passen Sie die Anzahl der Punkte an (je mehr es gibt, desto kürzer sind die Segmentlängen)
  • Fügen Sie einige X- und Y-Varianten hinzu (damit die Segmente nicht gleichmäßig sind)

Lassen Sie uns das in SCSS einbringen und eine Funktion erstellen, die die schmutzige Arbeit für uns erledigt. Wir werden verwenden

  • random()
  • cos()
  • sin()

Die relevanteste Mathematik ist

/* 
  To generate an arbitrary points on 
  the unit circle at angle t 
*/
  
$x: cos(t);
$y: sin(t);

Und das in die richtige Syntax zu bringen, sieht so aus

$w: 160px    // Avatar width
$n: 60;      // Number of points on the circle

@function sketchAvatar() {
  $points: ();

  @for $i from 0 through $n {
    $points: append($points, ($w / 2) * (1 + cos((2 * pi() * $i / $n))) ($w / 2) * (1 + sin((2 * pi() * $i / $n))), comma);
  } 
  
  @return $points;
}

Das ist ein bisschen knifflig. Was passiert, ist, dass wir oben in der Mitte unserer Form beginnen und eine Liste von Punktesätzen um den Kreis für 60 gleichmäßig verteilte Punkte erzeugen.

Alles zusammen mit Variationen

Der obige Code erzeugt immer noch recht langweilige und gleichmäßige Polygone, daher müssen wir Varianz hinzufügen. Alles, was wir tun müssen, ist, die Punkte in beliebige Richtungen anzupassen, um das gewünschte Offset-Gefühl zu erzielen. Die `$lower`- und `$upper`-Variationszahlen können je nach gewünschtem Aussehen fast alles sein.

$w:     120px;   // Overall width

@function sketchAvatar() {
  $n: 	  60;     // Number of points
  $lower: -80;    // Lower variance
  $upper: 80;     // Upper variance

  $points: ();

  @for $i from 0 through $n {
    $points: append($points, ($w / 2) * (1 + cos((2 * pi() * $i / $n))) + (rand($lower, $upper) / 100) ($w / 2) * (1 + sin((2 * pi() * $i / $n))), comma);
  } 
  
  @return $points;
}

Wir haben es geschafft! Skizzenhafte, einzigartige Avatare mit CSS clip-path: polygon()

Siehe den Pen Sketchy Avatars von Chris Coyier (@chriscoyier) auf CodePen.

Funktioniert es auch in Firefox?

Chris hier! Ich dachte mir, da Firefox dies auf diese Weise nicht unterstützt, aber die SVG-Syntax unterstützt, könnten wir es quasi polyfillen.

.avatar {
  clip-path: polygon( ... ) /* Firefox: nope */
  clip-path: url(#clip); /* Firefox: yep */
}

Also habe ich für jeden Avatar…

  1. Die Polygonpunkte im `content`-Attribut eines Pseudo-Elements (eines Elements mit einem gültigen Pseudo-Element wie dem übergeordneten Div) in CSS ausgeben
  2. Diesen Wert mit JavaScript extrahiert
  3. Die Punkte in das SVG-Format umformatiert (z. B. keine „px“)
  4. Ein neues <svg> mit einem <clipPath>pfad eingefügt, der bereit ist
$(".user").each(function(i) {
 
  var path = window.getComputedStyle(this, ':after').getPropertyValue('content');
  
  // clean house
  svgPolygonPoints = 
    path
      .replace(/px/g, "")
      .replace(/polygon/, "")
      .replace(/\(/, "")
      .replace(/\)/, "")
      .replace(/\;/g, "")
      .replace(/"/g, "")
      .replace(/\'/g, "");
    
  // To get this to actually work, create a <div> instead with this inside, see below.
  var svg = $("<svg width='0' height='0'>")
    .append("<defs><clipPath id='clip-" + (i+1) +"'><polygon points='" + svgPolygonPoints +"' /></clipPath></defs>");
  
  $("body").append(svg);
    
});

Es funktioniert nicht! haha. Selbst wenn man einen Neuzeichnen erzwingt, mag es das injizierte SVG aus irgendeinem Grund einfach nicht. Schauen Sie sich Amelias Lösung an

Es ist im Grunde so etwas wie

.user:nth-child(1)  {
  clip-path: polygon(120.04px 60px ...);
}

wird zu

<svg width="0" height="0">
  <defs>
    <clippath id="clip-1">
      <polygon points="120.04 60, ... "></polygon>
    </clippath>
  </defs>
</svg>