Mit „CSS-Bildern“ meine ich Bilder, die nur mit HTML-Elementen und CSS erstellt werden. Sie sehen aus, als wären es SVGs, die in Adobe Illustrator gezeichnet wurden, aber sie wurden direkt im Browser erstellt. Einige der verwendeten Techniken sind das Tüfteln mit border-radius, box-shadows und manchmal clip-path. Wenn Sie nach „daily css images” auf CodePen suchen, finden Sie viele großartige Beispiele. Ich habe einige selbst gezeichnet, darunter diesen Infinity Gauntlet, aber in einem Element mit nur Hintergründen und minimaler Verwendung anderer Eigenschaften.
Werfen wir einen Blick darauf, wie Sie selbst auf diese Weise CSS-Bilder erstellen können.
Die Methode
Das Verständnis der Kurzschreibweise background sowie der Funktionsweise von CSS-Verläufen ist praktisch alles, was Sie benötigen, um etwas in einem Element zu zeichnen. Zur Erinnerung, die Argumente sind wie folgt:
background: <'background-color'> || <image> || <position> [ / <size> ]? || <repeat> || <attachment> || <origin> || <clip>;
Sie können in beliebiger Reihenfolge erscheinen, mit der Ausnahme, dass zwischen position und size ein / stehen muss. Wir müssen diese beiden Argumente auch in dieser Reihenfolge beibehalten, sonst erhalten wir unerwartete Ergebnisse. Nicht alle davon müssen einbezogen werden, und für diesen Zweck werden wir die Argumente color, repeat, attachment, origin oder clip nicht verwenden. Das lässt uns mit image, size und position. Da sich Hintergründe standardmäßig wiederholen, müssen wir background-repeat: no-repeat; direkt unter allem in background platzieren (wenn bestimmte Hintergründe wiederholt werden sollen, können wir repeating-linear-gradient() und repeating-radial-gradient() verwenden). In diesem Fall ist das Grundgerüst-CSS folgendes:
.image {
background: <image> <position> / <size>;
background-repeat: no-repeat;
}
Wir können sogar mehrere Sätze von Hintergrundargumenten verwenden! Daher können wir sie wie folgt stapeln und durch Kommas trennen:
.image {
background:
<image> <position> / <size>,
<image> <position> / <size>,
<image> <position> / <size>;
background-repeat: no-repeat;
}
Die obige Struktur ist die Grundlage dafür, wie wir Bilder zeichnen werden – eine Zeile pro Form. Beachten Sie, dass die Renderreihenfolge umgekehrt zur Reihenfolge von absolut oder fest positionierten Elementen ist. Das erste wird oben angezeigt, nicht unten. Mit anderen Worten, die Kreise (radiale Verläufe) unten werden von unten nach oben gerendert (blau unten, rot oben).
.circles {
background:
radial-gradient(7em 7em at 35% 35%, red 50%, transparent 50%),
radial-gradient(7em 7em at 50% 50%, gold 50%, transparent 50%),
radial-gradient(7em 7em at 65% 65%, blue 50%, transparent 50%);
background-repeat: no-repeat;
width: 240px;
height: 240px;
}

Zeichnen
Wir werden Sass (SCSS) verwenden, um diese Bilder zu zeichnen, damit wir Variablen für eine Farbpalette nutzen können. Das macht den Code kürzer, leichter lesbar und einfacher, abgedunkelte oder hellere Varianten von Farben zu ändern. Wir könnten stattdessen Variablen in normalem CSS verwenden und Sass vergessen, aber aufgrund der fehlenden Unterstützung durch Internet Explorer bleiben wir bei Sass. Um zu erklären, wie das funktioniert, werden wir mit Formen experimentieren, die sowohl lineare als auch radiale Verläufe verwenden.
Einrichten einer Farbpalette
Unsere Palette besteht aus RGB- oder HSL-Farben. Ich werde später erklären, warum wir die Farben in einem dieser Formate beibehalten. Für dieses Beispiel verwenden wir RGB.
$r: rgb(255,0,0); // hsl(0,100%,50%)
$o: rgb(255,128,0); // hsl(32,100%,50%)
Was ich gerne tue, um den Code kurz und gut lesbar zu halten, ist die Verwendung von mindestens einem Buchstaben pro Farbe (z. B. $r für Rot). Wenn ich dunklere oder hellere Schattierungen einer Farbe verwende, füge ich ein d vor dem Basisbuchstaben oder den Buchstaben für Dunkel oder ein l für Hell hinzu. Ich würde $dr für Dunkelrot und $lr für Hellrot verwenden. Wenn mehr als zwei weitere Schattierungen benötigt werden, füge ich eine Zahl am Ende hinzu, um den Schattierungsgrad anzugeben. Zum Beispiel $dr1 für Dunkelrot, $dr2 für ein dunkleres Rot und $lr1 für Hellrot, $lr2 für ein helleres Rot. Eine solche Palette würde so aussehen (dunkel zuerst, normal danach und hell zuletzt):
$dr1: rgb(224,0,0);
$dr2: rgb(192,0,0);
$r: rgb(255,0,0);
$lr1: rgb(255,48,48);
$lr2: rgb(255,92,92);
Skalierung und Leinwand einrichten
Wir werden em-Einheiten für die Bildabmessungen verwenden, damit das Bild leicht proportional skaliert werden kann. Da 1em gleich der Schriftgröße des Elements ist, wird jede Einheit des Bildes entsprechend angepasst, wenn sie geändert wird. Setzen wir eine Schriftgröße von 10px und stellen sowohl die Breite als auch die Höhe auf 24em ein. Einheiten von 10px sind am einfachsten zu bedenken, da Sie, wenn Sie die Rechnung im Kopf durchführen, sofort 240 × 240px erhalten. Dann verwenden wir zur Verdeutlichung der Ränder der Leinwand eine 1px graue Umrandung.
$r: rgb(255,0,0); // hsl(0,100%,50%)
$o: rgb(255,128,0); // hsl(32,100%,50%)
.image {
background-repeat: no-repeat;
font-size: 10px;
outline: 1px solid #aaa;
width: 24em;
height: 24em;
}
Seien Sie sich jedoch bewusst, wenn Sie kleinere Schriftgrößen verwenden; Browser haben eine Mindestschriftgröße (aus Gründen der Barrierefreiheit). Wenn Sie eine Schriftgröße von 4px einstellen und das Minimum 6px beträgt, wird es auf 6px erzwungen.
Darüber hinaus können Sie die Reaktionsfähigkeit aktivieren, indem Sie einfach calc() und Viewport-Einheiten verwenden. Vielleicht können wir etwas wie calc(10px + 2vmin) verwenden, wenn gewünscht, aber wir bleiben vorerst bei 10px.
Formen zeichnen
Hier beginnt der Spaß. Um ein Quadrat von 8 × 8em in der Mitte zu zeichnen, verwenden wir einen linear-gradient() mit zwei gleichfarbigen Stops.
.image {
background:
linear-gradient($r, $r) 50% 50% / 8em 8em;
...
}

Um es in etwas mehr wie ein Trapez zu formen, stellen Sie einen Winkel von 60 Grad ein. Gleichzeitig fügen wir $T für transparent zu unserer Palette hinzu und platzieren dann sowohl die Stops für $r als auch für $T bei 63 % (direkt bevor die untere rechte Ecke abgeschnitten wird).
$T: transparent;
.image {
background:
linear-gradient(60deg,$r 63%, $T 63%) 50% 50% / 8em 8em;
...
}

Wenn beide Stops auf denselben Wert gesetzt sind, ist die schräge Seite so scharf wie die anderen. Wenn man es genauer betrachtet, erscheint es pixelig

Um dies zu korrigieren, passen wir einen der Stops leicht an (um 1 % oder ungefähr), damit die Kante glatt genug ist. Ändern wir also $rs 63 % auf 62 %.
Dies wird auch bei runden Kanten ein Problem sein, wenn wir mit radialen Verläufen arbeiten, was wir später sehen werden. Wenn Sie dies in einem anderen Browser als Safari betrachten, sieht alles gut aus, selbst wenn Sie zu einer nicht-transparenten Farbe übergehen (sagen wir Orange). Das Problem beim Übergang zu transparent in Safari ist, dass Sie einen schwarzen Saum an der schrägen Seite bemerken werden.

Das liegt daran, dass das Schlüsselwort transparent in Safari immer eine schwarze Transparenz ist, und wir sehen infolgedessen etwas Schwarz. Ich wünschte wirklich, Apple würde das beheben, aber sie werden es nie tun. Vorerst fügen wir eine neue Variable $redT für rote Transparenz unter $r hinzu. Streichen wir das $T, das wir für transparent verwendet haben, da wir es nicht mehr verwenden werden.
$rT: rgba(255,0,0,0); // hsla(0,100%,50%,0)
Dann ersetzen wir transparent durch $redT. Das löst unser Safari-Problem!
.image {
background:
linear-gradient(60deg,$r 62%, $rT 63%) 50% 50% / 8em 8em;
...
}

Wenn Sie sich gefragt haben, warum wir keine Hex-Farben verwendet haben: Internet Explorer und Edge unterstützen die #rgba- und #rrggbbaa-Notation nicht (ja, Hex hat seit Ende 2016 einen Alpha-Kanal, falls Sie es nie wussten), und wir möchten, dass dies browsers übergreifend funktioniert. Wir möchten auch bei unserer Wahl des Farbformats konsistent bleiben.
Verschieben wir nun die Form vertikal um 20 % und zeichnen einen orangen Kreis mit den gleichen Abmessungen. Fügen Sie außerdem eine weitere Variable für seine transparente Version hinzu. Für die glatte Kante fügen Sie eine Lücke von 1 % zwischen dem soliden und dem transparenten Orange ein.
$oT: rgba(255,128,0,0); // hsla(32,100%,50%,0)
.image {
background:
linear-gradient(60deg,$r 62%, $rT 63%) 50% 20% / 8em 8em,
radial-gradient(8em 8em at 50% 80%, $o 49%, $oT 50%);
...
}

Um die Konsistenz mit unserer Skalierung beizubehalten, sollte der zweite Farb-Stop bei 50 % und nicht bei 100 % liegen.
Positionieren von Formen
Die Positionierung von Verläufen basiert darauf, ob die Einheit fest oder prozentual ist. Nehmen wir an, wir verwandeln beide Verläufe in Quadrate und versuchen, sie horizontal über das gesamte div zu platzieren.
.image {
background:
linear-gradient($r, $r) 24em 20% / 8em 8em,
linear-gradient($o, $o) 100% 80% / 8em 8em;
...
}

Das rote Quadrat liegt vollständig außerhalb der Leinwand (umrandet), und die rechte Seite des orangen Quadrats berührt die andere Seite. Feste Einheiten sind wie absolut positionierte Elemente oder das Zeichnen von Formen im HTML5 Canvas. In diesem Sinne ist der Ursprungspunkt oben links. Bei Verwendung von Prozent und einer festgelegten Hintergrundgröße erhält das div einen „Fake-Padding“ der halben Hintergrundgröße. Gleichzeitig ist der Ursprungspunkt des Hintergrunds zentriert (nicht zu verwechseln mit background-origin, das sich auf Box-Ecken bezieht).

Wenn wir diese Verläufe in radiale Verläufe als Kreise verwandeln und die gleichen x-Positionen 24em und 100 % anwenden, enden beide auf der anderen Seite, halbiert. Dies liegt daran, dass der Ursprungspunkt immer in der Mitte liegt, wenn wir den Hintergrund so schreiben:
.image {
background:
radial-gradient(8em 8em at 24em 20%, $r 49%, $rT 50%),
radial-gradient(8em 8em at 100% 80%, $o 49%, $oT 50%);
...
}

Wenn wir den Hintergrund umschreiben, sodass Position und Größe nach dem Verlauf und 100% 100% at center stehen, werden sie wie die linearen Verläufe positioniert. Das rote liegt außerhalb, und das orange berührt den rechten Rand. Das „Fake-Padding“ tritt mit dem Orange erneut auf.
.image {
background:
radial-gradient(100% 100% at center, $r 49%, $rT 50%) 24em 20% / 8em 8em,
radial-gradient(100% 100% at center, $o 49%, $oT 50%) 100% 80% / 8em 8em;
...
}

Es gibt keine einzelne richtige Methode zur Positionierung von Formen, aber um sie wie ein absolut oder fest positioniertes HTML-Element zu platzieren, verwenden Sie feste Einheiten. Wenn Sie eine schnelle Möglichkeit benötigen, eine Form zu platzieren (unter Verwendung der Parameter position / size) in der exakten Mitte, ist 50 % die beste Option, da der Ursprung der Form sein Zentrum ist. Verwenden Sie 100 %, wenn sie die rechte Seite berühren soll.
Formen skalieren
Die Skalierung in CSS-Hintergründen funktioniert wie erwartet, wird aber immer noch von der Art der für die Position verwendeten Einheit beeinflusst – fest oder prozentual. Wenn wir unsere Quadrate nehmen und ihre Breite auf 10em ändern, dehnt sich das rote nach rechts aus, und das orange dehnt sich seitlich aus.
.image {
background:
linear-gradient($r, $r) 12em 20% / 10em 8em,
linear-gradient($o, $o) 50% 80% / 10em 8em;
...
}

Wenn wir em-Einheiten für die y-Position verwenden, wächst die Form nach unten oder schrumpft nach oben, nachdem die Höhe geändert wurde. Wenn wir einen Prozentsatz verwenden, dehnt sie sich in beide vertikalen Richtungen aus.
Vor einer Weile haben wir zwei Möglichkeiten betrachtet, Kreise mit radialen Verläufen zu zeichnen. Die erste Methode besteht darin, Breite und Höhe zwischen ( und at anzugeben und dann die Position danach:
.image {
background:
radial-gradient(8em 8em at 50% 50%, $r 49%, $rT 50%);
...
}
Die zweite Methode ist, 100% 100% in der Mitte zu verwenden und dann die Position und Größe anzugeben:
.image {
background:
radial-gradient(100% 100% at 50% 50%, $r 49%, $rT 50%) 50% 50% / 8em 8em;
...
}
Diese Methoden zeichnen beide Kreise, führen aber zu unterschiedlichen Ergebnissen, weil:
- Die erste Methode füllt das gesamte div aus, da es keine wirkliche Hintergrundposition oder -größe gab.
- Eine tatsächliche Position und Größe für die zweite festzulegen, gibt ihr einen Begrenzungsrahmen. Folglich verhält sie sich genau wie eine lineare Verlaufform.
Nehmen wir an, wir ersetzen $rT durch $o. Sie werden sehen, dass das Orange das Weiße oder darunter liegende Formen (wenn wir welche hinzugefügt hätten) für die erste Methode überdeckt. Bei der zweiten Methode erkennen Sie leicht den Begrenzungsrahmen, der vom Orange enthüllt wird.

Zusätzlich ist der Zweck von 100% 100% anstelle der Verwendung von circle oder ellipse, um dem Kreis zu ermöglichen, den gesamten Begrenzungsrahmen auszufüllen. Es gibt uns sogar die volle Kontrolle über seine Abmessungen. So bleibt er gleich, wenn Sie die 50% 50% Position zu etwas anderem ändern. Wenn Sie eines der beiden Schlüsselwörter verwenden, stoppt der Rand des Kreises bei Zentrierung nur etwa 71 % des Weges und wird verzerrter, wenn seine Position angepasst wird. Hier ist zum Beispiel, was passiert, wenn wir die x-Position für circle und ellipse auf 0 ändern:

Langfristig können Sie die Syntax neu vorstellen als radial-gradient(Breite Höhe bei x y) oder radial-gradient(100% 100% bei x-im-Begrenzungsrahmen y-im-Begrenzungsrahmen) x y / Breite Höhe. Wenn Sie nur einen Kreis oder eine Ellipse zeichnen, können Sie den Code mit der ersten Methode vereinfachen. Wenn Sie einen Teil eines Kreises oder einen Teil eines Rings zeichnen, kommt die zweite Methode zum Einsatz. Diese wird in den Beispielen, die wir als nächstes erstellen werden, viele Anwendungen finden.
Beispiele
Bereit, jetzt etwas Echtes zu zeichnen? Wir werden drei Beispiele Schritt für Schritt durchgehen. Die ersten beiden werden statisch sein – eines mit vielen Halbkreisen und das andere mit abgerundeten Rechtecken. Das letzte Beispiel wird kleiner, konzentriert sich aber auf Animation.
Statisches Bild
Dieser Sonnenschirm wird unser erstes statisches Bild sein.

Wir verwenden eine Palette mit Rot ($r und $rT), Weiß ($w und $wT), Orange ($o und $oT) und Dunkelorange ($do und $doT).
$r: rgb(255,40,40);
$rT: rgba(255,40,40,0);
$w: rgb(240,240,240);
$wT: rgba(240,240,240,0);
$o: rgb(255,180,70);
$oT: rgba(255,180,70,0);
$do: rgb(232,144,0);
$doT: rgba(232,144,0,0);
Richten wir unseren Zeichenbereich von 30 × 29em ein.
.parasol {
// background to go here
background-repeat: no-repeat;
font-size: 10px;
outline: 1px solid #aaa;
width: 30em;
height: 29em;
}
Über dem background-repeat beginnen wir mit dem Zeichnen der Teile des Sonnenschirms. Fügen Sie zuerst die Verläufe hinzu, die den Stab bilden (da sie sich nicht überlappen, spielt die Reihenfolge von unten nach oben hier keine Rolle).
.parasol {
background:
// 1
radial-gradient(200% 200% at 100% 100%, $do 49%, $doT 50%) 14em 0 / 1em 1em,
radial-gradient(200% 200% at 0% 100%, $o 49%, $oT 50%) 15em 0 / 1em 1em,
// 2
linear-gradient(90deg, $do 50%, $o 50%) 14em 1em / 2em 25em,
// 3
radial-gradient(100% 200% at 50% 0, $oT 0.95em, $o 1em, $o 1.95em, $do 2em, $do 2.95em, $doT 3em) 14em 26em / 6em 3em,
// 4
radial-gradient(200% 200% at 100% 100%, $o 49%, $oT 50%) 18em 25em / 1em 1em,
radial-gradient(200% 200% at 0% 100%, $do 49%, $doT 50%) 19em 25em / 1em 1em;
...
}

- Um jede Seite des oberen Stabendes zu zeichnen, haben wir Viertelkreise von 1 × 1em verwendet. Um sie ihre Begrenzungsrahmen ausfüllen zu lassen, haben wir Kreise von doppelter Größe (
200% 200%) verwendet, die unten rechts und unten links positioniert sind. Wir könnten auch Schlüsselwortpaare wieright bottomoderleft bottomverwenden, aber es ist kürzer, die Prozentäquivalente zu verwenden. Beachten Sie die 1% Lücken zwischen den Stops, um Glätte zu gewährleisten. - Für den langen Teil haben wir ein langes Rechteck mit einem abrupten Dunkelorange-zu-Orange-Übergang verwendet. Es besteht keine Notwendigkeit für eine fraktionale winzige Lücke, da wir nicht mit einer Kurve oder Schräge arbeiten.
- Dieser Teil des Stabs ist etwas schwieriger zu zeichnen als die anderen, da wir den Durchmesser von 2em beibehalten müssen. Um diesen Bogen zu zeichnen, verwenden wir eine Box von 6 × 3em, sodass es einen Abstand von 2em zwischen den Enden gibt, die ebenfalls 2em sind. Dann verwenden wir einen radialen Verlauf am oberen Mittelpunkt, wo jeder Stop 1em beträgt (und um 0,05em für Glätte ausgedehnt wird).
- Die letzten beiden sind wie die ersten, außer dass sie am rechten Ende des Bogens positioniert sind, damit sie nahtlos passen. Auch die Farben tauschen die Plätze.
Dann fügen wir oberhalb der vorherigen Verläufe von unten nach oben Folgendes hinzu, um die Oberseite des Regenschirms ohne die spitzen Enden zu zeichnen:
.parasol {
background:
radial-gradient(100% 200% at 50% 100%, $r 50%, $rT 50.25%) 50% 1.5em / 9em 12em,
radial-gradient(100% 200% at 50% 100%, $w 50%, $wT 50.25%) 50% 1.5em / 21em 12em,
radial-gradient(100% 200% at 50% 100%, $r 50%, $rT 50.25%) 50% 1.5em / 30em 12em,
...
}

Um die Halbkreise zu zeichnen, aus denen dieser Teil besteht, haben wir eine Gradientengröße von 100% 200% verwendet, wodurch jeder Durchmesser in die Breite seines Hintergrunds passt, aber die doppelte Höhe hat und unten zentriert ist. Indem wir sie von unten nach oben anordnen, sodass die größte unten und die kleinste oben liegt, erhalten wir die gewünschten Kurven.
Da unser Stapel von Verläufen höher wird, wird es irgendwann schwierig zu erkennen, welcher Hintergrund oder welche Gruppe von Hintergründen zu welchem Teil des Bildes gehört. Um sie leichter zu identifizieren, können wir sie in Gruppen aufteilen, die von einem Kommentar eingeleitet werden, der beschreibt, wofür jede Gruppe ist. Derzeit haben wir den Stapel in Gruppen für die Oberseite des Sonnenschirms und den Stab aufgeteilt.
.parasol {
background:
/* top */
radial-gradient(100% 200% at 50% 100%, $r 50%, $rT 50.25%) 50% 1.5em / 9em 12em,
radial-gradient(100% 200% at 50% 100%, $w 50%, $wT 50.25%) 50% 1.5em / 21em 12em,
radial-gradient(100% 200% at 50% 100%, $r 50%, $rT 50.25%) 50% 1.5em / 30em 12em,
/* pole */
radial-gradient(200% 200% at 100% 100%, $do 49%, $doT 50%) 14em 0 / 1em 1em,
radial-gradient(200% 200% at 0% 100%, $o 49%, $oT 50%) 15em 0 / 1em 1em,
linear-gradient(90deg, $do 50%, $o 50%) 14em 1em / 2em 25em,
radial-gradient(100% 200% at 50% 0, $oT 0.95em, $o 1em, $o 1.95em, $do 2em, $do 2.95em, $doT 3em) 14em 26em / 6em 3em,
radial-gradient(200% 200% at 100% 100%, $o 49%, $oT 50%) 18em 25em / 1em 1em,
radial-gradient(200% 200% at 0% 100%, $do 49%, $doT 50%) 19em 25em / 1em 1em;
...
}
Dann, zwischen der Oberseite und dem Stab, fügen wir den nächsten Block von Hintergründen hinzu, um die spitzen Enden zu rendern. Um die Breiten jedes Segments zu bestimmen, müssen wir den Abstand zwischen jedem Punkt ermitteln, an dem Rot und Weiß aufeinandertreffen. Alle müssen sich zu 30em summieren.

Beginnend mit den weißen und schmalsten roten Halbkreisen ziehen wir die rote Breite von 9em von der weißen Breite von 21em ab und teilen das Ergebnis durch 2, um die Breite der beiden weißen Segmente zu erhalten (Punkt „b“ in der Abbildung). Das Ergebnis wäre also 6em (b = (21 – 9) / 2 = 6). Dann wäre das mittlere rote Segment 9em (21 – (6 + 6) = 9). Was wir jetzt noch haben, sind die äußeren roten Segmente (Punkt „a“ in der Abbildung). Ziehen Sie die Summe dessen, was wir jetzt haben, von der Breite des größeren roten Halbkreises ab und teilen Sie das Ergebnis durch 2. Das wäre der Wert für Punkt a: (30em – (6 + 6 + 9)) / 2 = 4,5em.
.parasol {
background:
...
/* pointy ends */
radial-gradient() 0 13.5em / 4.5em 3em,
radial-gradient() 4.5em 13.5em / 6em 3em,
radial-gradient() 50% 13.5em / 9em 3em,
radial-gradient() 19.5em 13.5em / 6em 3em,
radial-gradient() 25.5em 13.5em / 4.5em 3em,
...
}
Um die Halbkreise ähnlich wie den oberen Teil zu zeichnen, beginnen wir mit dem transparenten Gegenstück der Farbe für jede Form, damit sie wie Bogenbrücken aussehen. Wir werden jedem Gradienten auch zusätzliche 5 % zur Breite hinzufügen (nicht zur Hintergrundbox-Breite), damit jeder Punkt, der durch benachbarte Hintergründe gebildet wird, nicht übermäßig scharf und dünn ist.
.parasol {
background:
...
/* pointy ends */
radial-gradient(105% 200% at 50% 100%, $rT 49%, $r 50%) 0 13.5em / 4.5em 3em,
radial-gradient(105% 200% at 50% 100%, $wT 49%, $w 50%) 4.5em 13.5em / 6em 3em,
radial-gradient(105% 200% at 50% 100%, $rT 49%, $r 50%) 50% 13.5em / 9em 3em,
radial-gradient(105% 200% at 50% 100%, $wT 49%, $w 50%) 19.5em 13.5em / 6em 3em,
radial-gradient(105% 200% at 50% 100%, $rT 49%, $r 50%) 25.5em 13.5em / 4.5em 3em,
...
}

Endlich brauchen Sie die 1px solid #aaa Umrandung nicht mehr. Unser Sonnenschirm ist fertig!
Siehe den Pen Parasol von Jon Kantner (@jkantner) auf CodePen.
Etwas mit abgerundeten Rechtecken
Das nächste Beispiel wird ein altes iPhone-Modell sein, das mehr Details als die neueren Modelle aufweist. Das Besondere daran sind die beiden abgerundeten Rechtecke, die den Außen- und Mittelteil des Home-Buttons bilden.

Die Palette besteht aus Schwarz ($bk und $bkT) für den Rand des Home-Buttons, Dunkelgrau ($dg und $dgT) für den Körper, Grau ($g und $gT) für die Kamera und den Lautsprecher, Hellgrau ($lg und $lgT) für den äußeren Rand, Blau ($bl und $blT) für die Kameralinse und ein sehr dunkles Lila ($p und $pT) für den Bildschirm.
$bk: rgb(10,10,10);
$bkT: rgba(10,10,10,0);
$dg: rgb(50,50,50);
$dgT: rgba(50,50,50,0);
$g: rgb(70,70,70);
$gT: rgba(70,70,70,0);
$lg: rgb(120,120,120);
$lgT: rgba(120,120,120,0);
$bl: rgb(20,20,120);
$blT: rgba(20,20,120,0);
$p: rgb(25,20,25);
$pT: rgba(25,20,25,0);
Richten wir unsere Leinwand auf 20 × 40em ein und verwenden die gleiche Schriftgröße wie für den Sonnenschirm, 10px.
.iphone {
// background goes here
background-repeat: no-repeat;
font-size: 10px;
outline: 1px solid #aaa;
width: 20em;
height: 40em;
}
Bevor wir mit dem Zeichnen unseres ersten abgerundeten Rechtecks beginnen, müssen wir über unseren border-radius nachdenken, der 2em betragen wird. Außerdem möchten wir links Platz für den Sperrschalter und die Lautstärketasten lassen, was 0,25em entspricht. Aus diesem Grund wird das Rechteck 19,75 × 40em groß sein. Unter Berücksichtigung des 2em border-radius benötigen wir zwei sich schneidende lineare Verläufe. Einer muss eine Breite von 15,75em (19,75em – 2 × 2) haben und der andere eine Höhe von 36em (40em – 2 × 2). Positionieren Sie den ersten 2,25em links und den zweiten 0,25em links und 2em von oben.
.iphone {
background:
/* body */
linear-gradient() 2.25em 0 / 15.75em 40em,
linear-gradient() 0.25em 2em / 19.75em 36em;
...
}
Da der hellgraue Rand 0,5em dick sein wird, lassen wir jeden Gradienten-Stop sofort von Hellgrau ($lg) zu Dunkelgrau ($dg) und umgekehrt bei 0,5em und 0,5em vor dem Ende wechseln (40em – 0,5 = 39,5em für den ersten Verlauf, 19,75em – 0,5 = 19,25em für den zweiten). Stellen Sie einen Winkel von 90 Grad für den zweiten ein, um ihn horizontal zu machen.
.iphone {
background:
/* body */
linear-gradient($lg 0.5em, $dg 0.5em, $dg 39.5em, $lg 39.5em) 2.25em 0 / 15.75em 40em,
linear-gradient(90deg, $lg 0.5em, $dg 0.5em, $dg 19.25em, $lg 19.25em) 0.25em 2em / 19.75em 36em;
...
}

In jeder quadratischen Ecke, wie im Bild mit der orangefarbenen Box angedeutet, platzieren wir die abgerundeten Kanten. Um diese Formen zu erstellen, verwenden wir radiale Verläufe, die doppelt so groß sind wie ihre Begrenzungsrahmen und in jeder Ecke platziert sind. Fügen Sie sie oberhalb der Körper-Hintergründe ein.
.iphone {
background:
/* corners */
radial-gradient(200% 200% at 100% 100%, $dg 1.45em, $lg 1.5em, $lg 50%, $lgT 51%) 0.25em 0 / 2em 2em,
radial-gradient(200% 200% at 0% 100%, $dg 1.45em, $lg 1.5em, $lg 50%, $lgT 51%) 18em 0 / 2em 2em,
radial-gradient(200% 200% at 100% 0%, $dg 1.45em, $lg 1.5em, $lg 50%, $lgT 51%) 0.25em 38em / 2em 2em,
radial-gradient(200% 200% at 0% 0%, $dg 1.45em, $lg 1.5em, $lg 50%, $lgT 51%) 18em 38em / 2em 2em,
...
}

Um die 0,5em dicken hellgrauen Enden zu erhalten, denken Sie darüber nach, wo der Verlauf beginnt und führen Sie dann die Berechnung durch. Da das Hellgrau am Ende ist, ziehen wir 0,5em von 2em ab, um den ersten Stop richtig zu platzieren. Für die Glätte nehmen wir ein winziges bisschen vom ersten 1,5em ab und fügen 1 % zum zweiten 50 % hinzu, damit sich die runden Kanten mit den flachen Kanten vermischen.
Wenn wir nun das Bild vergrößern, indem wir die Schriftgröße auf 40px oder mehr ändern, bemerken wir Nähte zwischen den abgerundeten und flachen Kanten (in Orange eingekreist).

Da sie so klein erscheinen, können wir sie leicht beheben, indem wir zu den Körper-Hintergründen zurückkehren und die Gradienten-Stops leicht ändern, solange alles noch richtig aussieht, wenn wir die Schriftgröße zurück auf 10px stellen.
.iphone {
background:
/* body */
linear-gradient($lg 0.5em, $dg 0.55em, $dg 39.5em, $lg 39.55em) 2.25em 0 / 15.75em 40em,
linear-gradient(90deg, $lg 0.5em, $dg 0.55em, $dg 19.175em, $lg 19.25em) 0.25em 2em / 19.75em 36em;
...
}
Dann fügen wir in einem linearen Verlauf den Sperrschalter und die Lautstärketasten hinzu, um den 0,25em-Raum links auszufüllen. Wenn es einen 1px-Abstand zwischen den Tasten und dem Körper gibt, können wir eine winzige Überlappung von 0,05em auf die Hintergrundbreite hinzufügen (was sie auf 0,3em macht), damit sie nicht in das Dunkelgrau hineinragt.
.iphone {
background:
/* volume buttons */
linear-gradient($lgT 5em, $lg 5em, $lg 7.5em, $lgT 7.5em, $lgT 9.5em, $lg 9.5em, $lg 11em, $lgT 11em, $lgT 13em, $lg 13em, $lg 14.5em, $lgT 14.5em) 0 0 / 0.3em 100%,
...
}

Es sieht so aus, als hätten wir drei hellgrau-zu-hellgrau-Verläufe verwenden können, aber da es möglich war, war nur einer notwendig. Es sind nur viele abrupte Übergänge zwischen dem transparenten und dem opaken Hellgrau, die nach unten verlaufen.
Als Nächstes fügen wir den Rand des Home-Buttons sowie die flachen Kanten des inneren Quadrats hinzu. Nun wird das Quadrat im Home-Button 1,5 × 1,5em groß sein und im Grunde dem gleichen Verfahren wie der Körper folgen: zwei sich schneidende lineare Verläufe und radiale Elemente zum Ausfüllen der Ecken. Um sie horizontal in der Mitte zu platzieren, ist calc() praktisch. 50% + 0,125em ist der Ausdruck; wenn wir nur den Telefonkörper zentrieren würden, gäbe es auf jeder Seite einen 0,125em-Abstand. Daher verschieben wir ihn um weitere 0,125em nach rechts. Die gleiche x-Positionierung gilt für die oberen beiden Hintergründe.
.iphone {
background:
/* home button */
linear-gradient() calc(50% + 0.125em) 36.5em / 0.5em 1.5em,
linear-gradient() calc(50% + 0.125em) 37em / 1.5em 0.5em,
radial-gradient(3em 3em at calc(50% + 0.125em) 37.25em, $bkT 1.25em, $bk 1.3em, $bk 49%, $bkT 50%),
...
}
Ähnlich wie wir die linearen Verlaufsteile des Telefonkörpers schattiert haben, beginnen und enden die Stops mit Hellgrau, aber mit Transparent in der Mitte. Beachten Sie, dass wir 0,05em Lücken zwischen jedem Grau-zu-Transparent-Übergang (und umgekehrt) gelassen haben. Genau wie die Ecken des Körpers dient dies dazu, die Mischung zwischen einer runden Ecke und einem flachen Ende innen zu gewährleisten.
.iphone {
background:
/* home button */
linear-gradient($lg 0.15em, $lgT 0.2em, $lgT 1.35em, $lg 1.35em) calc(50% + 0.125em) 36.5em / 0.5em 1.5em,
linear-gradient(90deg, $lg 0.15em, $lgT 0.2em, $lgT 1.3em, $lg 1.35em) calc(50% + 0.125em) 37em / 1.5em 0.5em,
radial-gradient(3em 3em at calc(50% + 0.125em) 37.25em, $bkT 1.25em, $bk 1.3em, $bk 49%, $bkT 50%),
...
}

Übrigens, da die Ränder wie bereits erwähnt sehr klein sein werden, können wir besser sehen, was wir tun, indem wir die Schriftgröße auf mindestens 20px erhöhen. Es ist wie die Verwendung des Zoom-Werkzeugs in Bildbearbeitungssoftware.
Um nun die Ecken des grauen Quadrats genau dort zu platzieren, wo sie sein sollen, konzentrieren wir uns zuerst auf die x-Position. Wir beginnen mit calc(50% + 0.125em), dann addieren oder subtrahieren wir die Breite jedes Teils, oder besser gesagt, den border-radius des Quadrats. Diese Hintergründe kommen über die letzten drei.
.iphone {
background:
/* home button */
radial-gradient(200% 200% at 100% 100%, $lgT 0.3em, $lg 0.35em, $lg 0.48em, $lgT 0.5em) calc(50% + 0.125em - 0.5em) 36.5em / 0.5em 0.5em,
radial-gradient(200% 200% at 0% 100%, $lgT 0.3em, $lg 0.35em, $lg 0.48em, $lgT 0.5em) calc(50% + 0.125em + 0.5em) 36.5em / 0.5em 0.5em,
radial-gradient(200% 200% at 100% 0%, $lgT 0.3em, $lg 0.35em, $lg 0.48em, $lgT 0.5em) calc(50% + 0.125em - 0.5em) 37.5em / 0.5em 0.5em,
radial-gradient(200% 200% at 0% 0%, $lgT 0.3em, $lg 0.35em, $lg 0.48em, $lgT 0.5em) calc(50% + 0.125em + 0.5em) 37.5em / 0.5em 0.5em,
...
}

Dann wird der Bildschirm ein 17,25 × 30em Rechteck sein. Ähnlich wie bei Teilen des Home-Buttons zentrieren wir ihn horizontal mit calc(50% + 0.125em). Von oben wird er 5em entfernt sein.
.iphone {
background:
/* screen */
linear-gradient($p, $p) calc(50% + 0.125em) 5em / 17.25em 30em,
...
}

Zuletzt fügen wir Kamera und Lautsprecher hinzu. Die Kamera ist ein einfacher 1 × 1 blau-zu-grauer radialer Verlauf ohne besondere Berechnungen. Der reine graue Lautsprecher ist jedoch etwas aufwendiger. Er wird ein 5 × 1em Rechteck mit einem 0,5em border-radius sein. Um ihn zu zeichnen, zeichnen wir zuerst ein Rechteck mit den ersten 4em der Breite und zentrieren es mit calc(50% + 0.125em). Dann verwenden wir 0,5 × 1em Halbkreise, bei denen die Gradientenpositionen 100% 50% und 0% 50% sind. Die beste Methode, diese links und rechts vom Rechteck zu positionieren, ist die Verwendung einiger neuer calc()-Ausdrücke. Für links ziehen wir die halbe Rechteckbreite und die halbe Halbkreisbreite vom Mittelpunkt des Körpers ab (50% + 0.125em - 2em - 0.25em). Rechts folgt das gleiche Muster, aber mit Addition, also 50% + 0.125em + 2em + 0.25em.
.iphone {
background:
/* camera */
radial-gradient(1em 1em at 6.25em 2.5em, $bl 0.2em, $g 0.21em, $g 49%, $gT 50%),
/* speaker */
radial-gradient(200% 100% at 100% 50%, $g 49%, $gT 50%) calc(50% + 0.125em - 2em - 0.25em) 2em / 0.5em 1em,
radial-gradient(200% 100% at 0% 50%, $g 49%, $gT 50%) calc(50% + 0.125em + 2em + 0.25em) 2em / 0.5em 1em,
linear-gradient($g, $g) calc(50% + 0.125em) 2em / 4em 1em,
...
}

Entfernen Sie die graue Umrandung um das div, und das iPhone ist fertig!
Siehe den Pen iPhone von Jon Kantner (@jkantner) auf CodePen.
Animierte Bilder
Sie denken vielleicht, dass Sie background-position verwenden könnten, um solche Bilder zu animieren, aber Sie können nur so viel tun. Zum Beispiel ist es unmöglich, die Drehung eines einzelnen Hintergrunds allein zu animieren. Tatsächlich performen background-position-Animationen in der Regel nicht so gut wie transform-Animationen, daher empfehle ich sie nicht.
Um beliebige Teile eines Bildes nach Belieben zu animieren, können wir die Pseudo-Elemente :before oder :after für diesen Teil verantwortlich machen. Wenn wir mehr Auswahlmöglichkeiten benötigen, können wir auf mehrere untergeordnete divs zurückgreifen, aber nicht für jedes kleine Detail. Für unser animiertes Bildbeispiel erstellen wir dieses animierte Radar:

Wir zeichnen zuerst den statischen Teil, der alles außer dem grauen Rahmen und dem rotierenden Zeiger ist. Davor liefern wir unsere Palette (Hinweis: Wir brauchen kein $dgnT für $dgn) und den Basiss-Code.
$gn: rgb(0,192,0);
$gnT: rgba(0,192,0,0);
$dgn: rgb(0,48,0);
$gy: rgb(128,128,128);
$gyT: rgba(128,128,128,0);
$bk: rgb(0,0,0);
$bkT: rgba(0,0,0,0);
.radar {
background-repeat: no-repeat;
font-size: 10px;
outline: 1px solid #aaa;
width: 20em;
height: 20em;
}
Da dieses Bild komplett rund sein wird, können wir bedenkenlos einen border-radius von 50 % anwenden. Dann können wir einen wiederholten radialen Verlauf verwenden, um die Ringe zu zeichnen – etwa 1/3 voneinander entfernt.
.radar {
background:
/* rings */
repeating-radial-gradient($dgn, $dgn 2.96em, $gn 3em, $gn 3.26em, $dgn 3.3em);
background-repeat: no-repeat;
border-radius: 50%;
...
}

Beachten Sie auch das zusätzliche $dgn am Anfang. Damit wiederholte Verläufe wie erwartet beginnen, enden und wiederholen, müssen wir die Startfarbe bei 0 (oder ohne 0) angeben.
Im Gegensatz zum vorherigen Beispiel verwenden wir kein calc(), um die Linien zu zentrieren, da Internet Explorer das Ganze unbeholfen rendern wird, wenn wir später ein Pseudo-Element verwenden. Um vier 0,4em dicke Linien zu zeichnen, die sich in der Mitte kreuzen, wissen Sie, dass die Hälfte der Linie die halbe Breite des div bei 10em sein sollte. Also subtrahieren und addieren wir die Hälfte von 0,4 (0,4 / 2 = 0,2) auf jeder Seite. Mit anderen Worten, der linke Teil des Grüns sollte 9,8em und der rechte 10,2em sein. Für die 45-Grad-Diagonalen müssen wir jedoch 10em mit der Quadratwurzel von 2 multiplizieren, um ihre Mitte zu erhalten (10 × √2 ≈ 14,14). Dies ist die Länge der längsten Seite eines 10em Rechtwinkligen Dreiecks. Infolgedessen liegen die Seiten jeder Diagonale ungefähr bei 13,94 und 14,34em.
.radar {
background:
/* lines */
linear-gradient($gnT 9.8em, $gn 9.8em, $gn 10.2em, $gnT 10.2em),
linear-gradient(45deg,$gnT 13.94em, $gn 13.98em, $gn 14.3em, $gnT 14.34em),
linear-gradient(90deg,$gnT 9.8em, $gn 9.8em, $gn 10.2em, $gnT 10.2em),
linear-gradient(-45deg,$gnT 13.94em, $gn 13.98em, $gn 14.3em, $gnT 14.34em),
...
}

Um die Pixelbildung der Diagonalen zu verhindern, haben wir eine winzige 0,04em Lücke zwischen Grün und transparentem Grün gelassen. Dann, um etwas Beleuchtung zu erzeugen, fügen Sie diesen transparent-zu-schwarzen radialen Verlauf hinzu:
.radar {
background:
/* lighting */
radial-gradient(100% 100%, $bkT, $bk 9.9em,$bkT 10em),
...
}

Damit ist der statische Teil des Radars abgeschlossen. Nun zeichnen wir den grauen Rahmen und den Zeiger in einem weiteren Hintergrundstapel unter :before und fügen die Animation hinzu. Es gibt einen Grund, warum wir den Rahmen hier nicht aufgenommen haben. Da der Zeiger-Container das gesamte div ausfüllen soll, möchten wir nicht, dass er den Rahmen überlappt.
Dieses Pseudo-Element füllt den Raum aus, und um sicherzustellen, dass es dort bleibt, positionieren wir es absolut. Wir verwenden den gleichen border-radius, damit es in Safari animiert rund bleibt.
.radar {
...
position: relative;
&:before {
background-repeat: no-repeat;
border-radius: 50%;
content: "";
position: absolute;
width: 100%;
height: 100%;
}
}
Dann machen wir es für den Zeiger halb so groß wie sein Container und behalten es oben links. Schließlich zeichnen wir darauf den Rahmen.
.radar {
...
&:before {
animation: scan 5s linear infinite;
background:
/* frame */
radial-gradient($gyT 9.20em, $gy 9.25em, $gy 10em, $gyT 10.05em),
/* hand */
linear-gradient(45deg, $gnT 6em, $gn) 0 0 / 50% 50%;
...
}
}
@keyframes scan {
from {
transform: rotate(0);
}
to {
transform: rotate(1turn);
}
}
Jetzt ist unser kleines Gadget fertig!
Siehe den Pen Radar von Jon Kantner (@jkantner) auf CodePen.
Vorteile (Plus ein Nachteil)
Dieser Ansatz zum Zeichnen von CSS-Bildern hat mehrere Vorteile. Erstens ist das HTML im Vergleich zu einer gerasterten Bilddatei sehr schlank. Zweitens eignet es sich hervorragend für Bilder, die ohne die Verwendung experimenteller Eigenschaften und APIs, die möglicherweise nicht weit verbreitet sind, nicht gut gezeichnet werden können.
Das soll nicht heißen, dass diese Methode besser ist als die Verwendung eines übergeordneten Elements, das mit untergeordneten Elementen für die Formen verschachtelt ist. Es gibt jedoch einen Nachteil. Sie müssen darauf verzichten, einzelne Formen mit den Browser-Entwicklertools hervorheben zu können. Sie müssen einen Hintergrund aus- und einkommentieren, um zu identifizieren, welcher es ist. Solange Sie jede Gruppe von Hintergründen gruppieren und kennzeichnen, können Sie diesen bestimmten Hintergrund schneller finden.
Fazit
Kurz gesagt, die Methode zum Zeichnen von CSS-Bildern, die wir in diesem Beitrag behandelt haben, ermöglicht es uns:
- Erstellen Sie eine Palette aus Variablen für die Farben.
- Deaktivieren Sie die Hintergrundwiederholung, stellen Sie eine Skalierung mit
font-sizeund eine Canvas-Breite und -Höhe inem-Einheiten für das Ziel-Element ein. - Verwenden Sie eine temporäre
outline, um die Kanten während der Arbeit anzuzeigen. - Zeichnen Sie jede Form von unten nach oben, da Hintergründe in dieser Reihenfolge gerendert werden. Die
background-Syntax für jede Form folgtimage position / size(mit oder ohne Position und Größe).
Es steckt viel Querdenken und Experimentieren dahinter, um das gewünschte Ergebnis zu erzielen. Die drei von uns erstellten Beispiele reichten gerade aus, um das Konzept zu demonstrieren. Wir haben uns angesehen, wie wir jeden Hintergrund ordnen, Teile von Kreisen und abgerundeten Rechtecken zeichnen und Farbverläufe leicht anpassen, um glatte Kanten zu erhalten. Um mehr zu erfahren, können Sie gerne andere Beispiele zerlegen und studieren, die ich in dieser CodePen-Sammlung erstellt habe!
Wow, das ist erstaunlich
Dies ist ein wirklich gut geschriebener Artikel & die Beispiele sind gut ausgearbeitet, aber ich kann nicht sehen, warum ich stattdessen nicht den verschachtelten Elementansatz verwenden würde, da er eindeutig einfacher zu debuggen ist. Was dieser Artikel jedoch wirklich hervorhebt, ist, wie flexibel CSS-Verläufe sind!
Absolut einverstanden
Das treibt Hintergründe auf ein ziemlich verrücktes Niveau. Ich würde wahrscheinlich nicht mehr als einen Pfeil in einem Kreis zeichnen.
Aber ich wollte Ihnen nur für die Erwähnung und die Behandlung der Internet Explorer-Unterstützung danken. Es ist so frustrierend, wenn ich einen Artikel über ein Thema sehe und dann feststelle, dass ein wichtiger Browser ihn nicht unterstützt. Das könnte ich tatsächlich verwenden!
IE 11 unterstützt diese Demos.
iPhone und Regenschirm glitchen bei Änderung von Zoom / DPI in Firefox 61 / Chrome 67 – wenn ich weiße Linien (Hintergrund) oder einen 1-Pixel-Rand mit farbigem Element sehe.
Schöne Erklärung und tolle Beispiele. Zur Information, dies ist, was die Technik des Zeichnens von Bildern mit Verläufen im Jahr 2011 ins Leben gerufen hat: http://lea.verou.me/css3patterns
Ich kann es kaum erwarten, dass conic-gradient() weit verbreitet unterstützt wird, es wird vieles davon viel einfacher machen!
Ja, stellen Sie sich nur vor, was Sie damit alles machen könnten! Es wird wahrscheinlich noch ein paar Jahre dauern, bis die Unterstützung jeden Browser erreicht (außer IE).