Sie sind wahrscheinlich bereits ein wenig mit CSS-Variablen vertraut. Wenn nicht, hier ist eine Zweisekundenübersicht: Sie heißen eigentlich Custom Properties, Sie setzen sie in Deklarationsblöcken wie --size: 1em und verwenden sie als Werte wie font-size: var(--size);, sie unterscheiden sich von Preprocessor-Variablen (z. B. sie kaskadieren) und hier ist ein Leitfaden mit weitaus mehr Informationen.
Aber nutzen wir sie ihr volles Potenzial? Verfallen wir in alte Gewohnheiten und übersehen wir Gelegenheiten, bei denen Variablen die Menge an Code, die wir schreiben, erheblich reduzieren könnten?
Dieser Artikel wurde durch einen kürzlichen Tweet von mir über die Verwendung von CSS-Variablen zur Erstellung dynamischer Animationsverhaltensweisen angeregt.
CSS-Variablen sind fantastisch, oder? Aber die Macht des Scopes wird oft übersehen. Nehmen Sie zum Beispiel diese Demo, 3 verschiedene Animationen, aber nur 1 definierte Animation 💪 Das bedeutet dynamische Animationen 😎 https://#/VN02NlC4G8 via @CodePen #CSS #animation #webdev #webdesign #coding pic.twitter.com/ig8baxr7F3
— Jhey @ NodeConfEU 2019 📍🇮🇪⌨️ (@jh3yy) 5. November 2019
Schauen wir uns ein paar Beispiele an, bei denen CSS-Variablen verwendet werden können, um ziemlich coole Dinge zu tun, die wir vielleicht noch nicht in Betracht gezogen haben.
Grundlegende Scope-Vorteile
Das einfachste und wahrscheinlich häufigste Beispiel sind gescopten Farben. Und was ist unsere Lieblingskomponente, die wir mit Farbe verwenden? Der Button. 😅
Betrachten wir die Standardkonfiguration von primären und sekundären Buttons. Beginnen wir mit einem einfachen Markup, das eine BEM-Syntax verwendet.
<button class="button button--primary">Primary</button>
<button class="button button--secondary">Secondary</button>
Traditionell würden wir vielleicht so etwas tun, um sie zu stylen
.button {
padding: 1rem 1.25rem;
color: #fff;
font-weight: bold;
font-size: 1.25rem;
margin: 4px;
transition: background 0.1s ease;
}
.button--primary {
background: hsl(233, 100%, 50%);
outline-color: hsl(233, 100%, 80%);
}
.button--primary:hover {
background: hsl(233, 100%, 40%);
}
.button--primary:active {
background: hsl(233, 100%, 30%);
}
.button--secondary {
background: hsl(200, 100%, 50%);
outline-color: hsl(200, 100%, 80%);
}
.button--secondary:hover {
background: hsl(200, 100%, 40%);
}
.button--secondary:active {
background: hsl(200, 100%, 30%);
}
Das ist eine Menge Code für etwas, das nicht besonders komplex ist. Wir haben nicht viele Stile hinzugefügt und viele Regeln hinzugefügt, um den verschiedenen Zuständen und Farben des Buttons gerecht zu werden. Mit einer gescopten Variable könnten wir den Code erheblich reduzieren.
In unserem Beispiel ist der einzige Wert, der sich zwischen den beiden Button-Varianten unterscheidet, die Farbe (Hue). Lassen Sie uns diesen Code etwas umgestalten. Wir werden das Markup nicht ändern, aber wenn wir die Stile etwas aufräumen, erhalten wir Folgendes
.button {
padding: 1rem 1.25rem;
color: #fff;
font-weight: bold;
font-size: 1.25rem;
margin: 1rem;
transition: background 0.1s ease;
background: hsl(var(--hue), 100%, 50%);
outline-color: hsl(var(--hue), 100%, 80%);
}
.button:hover {
background: hsl(var(--hue), 100%, 40%);
}
.button:active {
background: hsl(var(--hue), 100%, 30%);
}
.button--primary {
--hue: 233;
}
.button--secondary {
--hue: 200;
}
Das reduziert nicht nur den Code, sondern erleichtert auch die Wartung erheblich. Ändern Sie die Kern-Button-Stile an einer Stelle und alle Varianten werden aktualisiert! 🙌
Ich würde es wahrscheinlich dort belassen, um es Entwicklern, die diese Buttons verwenden möchten, zu erleichtern. Aber wir könnten es noch weiter treiben. Wir könnten die Variable direkt auf das Element einfügen und die Klassendefinitionen vollständig entfernen. 😲
<button class="button" style="--hue: 233;">Primary</button>
<button class="button" style="--hue: 200;">Secondary</button>
Jetzt brauchen wir diese nicht mehr. 👍
.button--primary {
--hue: 233;
}
.button--secondary {
--hue: 200;
}
Das Einbetten dieser Variablen ist möglicherweise nicht das Beste für Ihr nächstes Designsystem oder Ihre nächste App, aber es eröffnet Möglichkeiten. Zum Beispiel, wenn wir eine Button-Instanz hätten, bei der wir die Farbe überschreiben müssten.
button.button.button--primary(style=`--hue: 20;`) Overridden
Spaß mit Inline-Variablen
Eine weitere Möglichkeit ist, damit ein wenig Spaß zu haben. Dies ist eine Technik, die ich für viele der Pens verwende, die ich auf CodePen erstelle. 😉
Sie schreiben vielleicht geradliniges HTML, aber in vielen Fällen verwenden Sie vielleicht ein Framework wie React oder einen Präprozessor wie Pug, um Ihr Markup zu schreiben. Diese Lösungen ermöglichen es Ihnen, JavaScript zu nutzen, um zufällige Inline-Variablen zu erstellen. Für die folgenden Beispiele werde ich Pug verwenden. Pug ist eine auf Einrückungen basierende HTML-Templating-Engine. Wenn Sie mit Pug nicht vertraut sind, keine Angst! Ich werde versuchen, das Markup einfach zu halten.
Beginnen wir damit, die Farbe für unsere Buttons zu randomisieren
button.button(style=`--hue: ${Math.random() * 360}`) First
Mit Pug können wir ES6-Template-Literale verwenden, um randomisierte CSS-Variablen einzufügen. 💪
Animationsänderungen
Jetzt, da wir die Möglichkeit haben, zufällige Eigenschaften für ein Element zu definieren, was könnten wir noch tun? Nun, eine übersehene Gelegenheit ist die Animation. Richtig, wir können die Variable selbst nicht animieren, wie hier
@keyframes grow {
from { --scale: 1; }
to { --scale: 2; }
}
Aber wir können dynamische Animationen basierend auf gescopten Variablen erstellen. Wir können das Verhalten der Animation im laufenden Betrieb ändern! 🤩
Beispiel 1: Der aufgeregte Button
Erstellen wir einen Button, der vor sich hin schwebt und sich dann aufregt, wenn wir ihn mit der Maus darüber fahren.
Beginnen wir mit dem Markup
button.button(style=`--hue: ${Math.random() * 360}`) Show me attention
Eine einfache Schwebeanimation könnte so aussehen
@keyframes flow {
0%, 100% {
transform: translate(0, 0);
}
50% {
transform: translate(0, -25%);
}
}
Das gibt uns so etwas
Ich habe einen kleinen Schatten als Extra hinzugefügt, aber er ist nicht entscheidend. 👍
Machen wir es so, dass unser Button aufgeregt wird, wenn wir mit der Maus darüber fahren. Nun, wir könnten einfach die verwendete Animation zu etwas wie diesem ändern
.button:hover {
animation: shake .1s infinite ease-in-out;
}
@keyframes shake {
0%, 100% {
transform: translate(0, 0) rotate(0deg);
}
25% {
transform: translate(-1%, 3%) rotate(-2deg);
}
50% {
transform: translate(1%, 2%) rotate(2deg);
}
75% {
transform: translate(1%, -2%) rotate(-1deg);
}
}
Und es funktioniert
Aber wir müssen eine weitere Keyframes-Definition einführen. Was wäre, wenn wir die beiden Animationen zu einer zusammenführen könnten? Sie sind sich in Bezug auf die Struktur nicht allzu unähnlich.
Wir könnten versuchen
@keyframes flow-and-shake {
0%, 100% {
transform: translate(0, 0) rotate(0deg);
}
25%, 75% {
transform: translate(0, -12.5%) rotate(0deg);
}
50% {
transform: translate(0, -25%) rotate(0deg);
}
}
Obwohl dies funktioniert, landen wir bei einer Animation, die aufgrund der Translationsschritte nicht ganz so flüssig ist. Was könnten wir also noch tun? Finden wir einen Kompromiss, indem wir die Schritte bei 25 % und 75 % entfernen.
@keyframes flow-and-shake {
0%, 100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(0, -25%) rotate(0deg);
}
}
Es funktioniert wie erwartet, aber hier kommt der Trick: Aktualisieren wir unseren Button mit einigen Variablen.
.button {
--y: -25;
--x: 0;
--rotation: 0;
--speed: 2;
}
Lassen Sie uns sie nun in die Animationsdefinition einfügen, zusammen mit den Animationseigenschaften des Buttons.
.button {
animation-name: flow-and-shake;
animation-duration: calc(var(--speed) * 1s);
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
@keyframes flow-and-shake {
0%, 100% {
transform: translate(calc(var(--x) * -1%), calc(var(--y) * -1%))
rotate(calc(var(--rotation) * -1deg));
}
50% {
transform: translate(calc(var(--x) * 1%), calc(var(--y) * 1%))
rotate(calc(var(--rotation) * 1deg));
}
}
Alles ist gut. 👍
Ändern wir diese Werte, wenn der Button gehovert wird
.button:hover {
--speed: .1;
--x: 1;
--y: -1;
--rotation: -1;
}
Schön! Jetzt hat unser Button zwei verschiedene Arten von Animationen, die aber über einen einzigen Satz von Keyframes definiert werden. 🤯
Lassen Sie uns damit noch ein bisschen mehr Spaß haben. Wenn wir es noch weiter treiben, können wir den Button ein bisschen spielerischer gestalten und vielleicht aufhören zu animieren, wenn er aktiv ist. 😅
Beispiel 2: Blasen
Nachdem wir nun verschiedene Techniken für das, was wir mit der Macht des Scopes tun können, durchgegangen sind, fassen wir alles zusammen. Wir werden eine zufällig generierte Blasenszene erstellen, die stark auf gescopten CSS-Variablen basiert.
Beginnen wir mit der Erstellung einer Blase. Einer statischen Blase.

.bubble {
background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%),
radial-gradient(15% 15% at 75% 75%, #80dfff, transparent),
radial-gradient(100% 100% at 50% 25%, transparent, #66d9ff 98%);
border: 1px solid #b3ecff;
border-radius: 100%;
height: 50px;
width: 50px;
}
Wir verwenden background mit mehreren Werten und einen border, um den Blasen-Effekt zu erzielen – aber er ist nicht sehr dynamisch. Wir wissen, dass der border-radius immer derselbe sein wird. Und wir wissen, dass die Struktur des border und background sich nicht ändern wird. Aber die Werte innerhalb dieser Eigenschaften und die anderen Eigenschaftswerte könnten alle zufällig sein.
Lassen Sie uns den CSS refaktorieren, um Variablen zu nutzen
.bubble {
--size: 50;
--hue: 195;
--bubble-outline: hsl(var(--hue), 100%, 50%);
--bubble-spot: hsl(var(--hue), 100%, 75%);
--bubble-shade: hsl(var(--hue), 100%, 70%);
background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%),
radial-gradient(15% 15% at 75% 75%, var(--bubble-spot), transparent),
radial-gradient(100% 100% at 50% 25%, transparent, var(--bubble-shade) 98%);
border: 1px solid var(--bubble-outline);
border-radius: 100%;
height: calc(var(--size) * 1px);
width: calc(var(--size) * 1px);
}
Das ist ein guter Anfang. 👍
Fügen wir weitere Blasen hinzu und nutzen wir den Inline-Scope, um sie zu positionieren und zu dimensionieren. Da wir mehr als einen Wert randomisieren werden, ist es praktisch, eine Funktion zum Generieren einer Zufallszahl im Bereich für unser Markup zu haben.
- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min
Mit Pug können wir Iteration nutzen, um eine große Menge an Blasen zu erstellen
- const baseHue = randomInRange(0, 360)
- const bubbleCount = 50
- let b = 0
while b < bubbleCount
- const size = randomInRange(10, 50)
- const x = randomInRange(0, 100)
.bubble(style=`--x: ${x}; --size: ${size}; --hue: ${baseHue}`)
- b++
Die Aktualisierung unserer .bubble-Styles ermöglicht es uns, die neuen Inline-Variablen zu nutzen.
.bubble {
left: calc(var(--x) * 1%);
position: absolute;
transform: translate(-50%, 0);
}
Was uns eine zufällige Menge von Blasen gibt
Lassen Sie uns es noch weiter treiben und diese Blasen animieren, damit sie von oben nach unten schweben und ausblenden.
.bubble {
animation: float 5s infinite ease-in-out;
top: 100%;
}
@keyframes float {
from {
opacity: 1;
transform: translate(0, 0) scale(0);
}
to {
opacity: 0;
transform: translate(0, -100vh) scale(1);
}
}
Das ist ziemlich langweilig. Sie tun alle dasselbe zur gleichen Zeit. Also randomisieren wir die Geschwindigkeit, Verzögerung, Endskalierung und Distanz, die jede Blase zurücklegen wird.
- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min
- const baseHue = randomInRange(0, 360)
- const bubbleCount = 50
- let b = 0
while b < bubbleCount
- const size = randomInRange(10, 50)
- const delay = randomInRange(1, 10)
- const speed = randomInRange(2, 20)
- const distance = randomInRange(25, 150)
- const scale = randomInRange(100, 150) / 100
- const x = randomInRange(0, 100)
.bubble(style=`--x: ${x}; --size: ${size}; --hue: ${baseHue}; --distance: ${distance}; --speed: ${speed}; --delay: ${delay}; --scale: ${scale}`)
- b++
Und jetzt aktualisieren wir unsere Stile
.bubble {
animation-name: float;
animation-duration: calc(var(--speed) * 1s);
animation-delay: calc(var(--delay) * -1s);
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
@keyframes float {
from {
opacity: 1;
transform: translate(-50%, 0) scale(0);
}
to {
opacity: 0;
transform: translate(-50%, calc(var(--distance) * -1vh)) scale(var(--scale));
}
}
Und wir erhalten dies
Mit etwa 50 Zeilen Code können Sie eine zufällig generierte animierte Szene erstellen, indem Sie die Macht des Scopes verfeinern! 💪
Das ist alles!
Wir können mit sehr wenig Code ziemlich coole Dinge erstellen, indem wir CSS-Variablen nutzen und einige kleine Tricks anwenden.
Ich hoffe, dieser Artikel hat ein Bewusstsein für die Macht des CSS-Variablen-Scopes geschaffen, und ich hoffe, Sie werden die Macht verfeinern und weitergeben 😎
Alle Demos in diesem Artikel sind in dieser CodePen-Sammlung verfügbar.
Ich liebe das.
ABER, ich habe Unternehmenskunden, die noch IE11 verwenden.
Die Blasenelemente sind nur schwarz…
Aus diesem Grund verwende ich diesen Polyfill und er ist fantastisch! https://www.npmjs.com/package/css-vars-ponyfill
Vorausgesetzt, die Einbeziehung dieses Effekts ist eine progressive Verbesserung, vielleicht über einem statischen Hintergrundbild und innerhalb von
@supports, sollte es kein Problem sein, ihn nicht IE11 und früher anzuzeigen (obwohl die Erklärung dem Kunden gegenüber ein ganz anderes Thema ist).Ich habe es persönlich noch nicht ausprobiert, aber dieser Polyfill sieht sehr vielversprechend aus
https://github.com/nuxodin/ie11CustomProperties
Warum sollte man Blasen für eine Firmenwebsite wollen? Außerdem ist IE11 sowieso schrecklich. Fangen Sie, wie von Microsoft ohnehin empfohlen, an, sie zu ermutigen, sich davon zu entfernen. Unabhängig davon, ob MS Sicherheitsupdates durchführt, fügt sie keine Funktionen hinzu.
Fantastisch. Ich habe viele meiner Pens mit dieser Technik refaktoriert :)
Das ist großartig @alphardex! Ich bin froh, dass es für Sie nützlich war :D
Danke! Das kam genau zur richtigen Zeit. Ich habe das heute genutzt, um ein Problem zu lösen.
Ich hatte keine Ahnung, dass CSS-Variablen gescopt sein könnten!
Kein Problem @Shannon! Ich bin froh, dass du es nutzen konntest, um etwas zu lösen :D
Scoping hat sicherlich viel Macht!
Ich liebe CSS-Variablen absolut. Sie sind weitaus flexibler als etwas wie SASS-Variablen und man braucht nicht einmal einen Präprozessor. Leider kann ich sie derzeit nur in meinen privaten Projekten oder Electron-Apps verwenden, da die Firma, für die ich arbeite, immer noch IE11 unterstützen muss..
Ein kleiner Einwand: Ein vollständiger Kreis wird bereits mit
anstelle von
Das Ergebnis ist dasselbe, aber ich wollte das trotzdem klarstellen.
Hallo Tim!
Das ist schade. Aber wenigstens kannst du damit irgendwo spielen :D
Ja, absolut. Ich denke, das ist eine schlechte Angewohnheit von mir haha. Ich schreibe immer
100%.Guter Artikel, mach weiter so und teile einen solchen Artikel, der sehr informativ ist. Viele Qualitätsinformationen wurden in diesem Artikel gegeben. Muss man lesen!!
Danke Aquadsoft! Ich weiß das wirklich zu schätzen. Ich werde mein Bestes tun! :D
Wow! Nur wow!
Liebe diesen Artikel, so viel zu verdauen. Ich habe die
--hueCustom Property zum.button__shadowElement hinzugefügt, da Schatten realistischer aussehen können, wenn sie Farbe vom Schatten werfenden Objekt aufnehmen.