linear() statt Spring-Bibliotheken
Echte Feder-Physik in Animationen war lange Domäne von JavaScript-Bibliotheken wie Framer Motion oder React Spring. Die CSS-Funktion linear() erlaubt jetzt, beliebig komplexe Easing-Kurven, inklusive Overshoot und Bounce, direkt als Stützpunkt-Liste im Stylesheet zu definieren, ganz ohne Laufzeit-JavaScript.
Inhaltsverzeichnis
- 1. Warum CSS bisher keine echten Federn kannte
- 2. Die linear() Funktion verstehen: Stützpunkte statt Kurve
- 3. Eine Feder-Funktion mathematisch generieren
- 4. Overshoot und Bounce mit linear() nachbilden
- 5. Werkzeuge zur Erzeugung von linear()-Werten
- 6. Spring-Easings als Custom Properties wiederverwendbar machen
- 7. Performance-Vergleich: linear() gegen JavaScript-Spring-Bibliotheken
- 8. Grenzen von linear() und wann JavaScript nötig bleibt
- 9. Easing-Funktionen im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum CSS bisher keine echten Federn kannte
Klassische CSS-Timing-Funktionen wie ease, ease-in-out oder selbst cubic-bezier() beschreiben ausschließlich monotone Kurven zwischen zwei Werten: die Geschwindigkeit ändert sich, die Richtung aber nie. Eine echte Feder-Bewegung dagegen schwingt über ihr Ziel hinaus und pendelt sich erst nach mehreren Oszillationen ein, ein Verhalten, das mit einem einzelnen Bézier-Polynom mathematisch nicht abbildbar ist. Genau deshalb griffen Entwickler für federartige Easing-Effekte bisher fast ausschließlich zu JavaScript-Bibliotheken, die die Physik einer gedämpften Feder pro Frame neu berechnen.
Diese JavaScript-Lösungen funktionieren zuverlässig, kosten aber etwas, das reines CSS nicht kostet: Hauptthread-Zeit bei jedem einzelnen Animationsframe, zusätzliches Bundle-Gewicht durch die Bibliothek selbst, und eine Abhängigkeit von der Ausführung des JavaScript-Codes, die bei langsamen Geräten oder blockiertem Main-Thread ins Stocken geraten kann. Eine federartige Easing-Funktion, die komplett in CSS lebt, umgeht all diese Probleme, weil der Browser die Animation auf dem Compositor-Thread abspielen kann.
Mit der CSS-Funktion linear() ist genau das jetzt möglich. Statt einer glatten mathematischen Formel beschreibt linear() eine Folge von Stützpunkten, zwischen denen linear interpoliert wird. Mit genügend Stützpunkten lässt sich jede beliebige Kurve annähern, einschließlich einer Kurve, die über 100 Prozent hinausschießt und zurückschwingt, exakt das Verhalten einer physikalischen Feder.
2. Die linear() Funktion verstehen: Stützpunkte statt Kurve
Die Syntax von linear() ist bewusst simpel: eine kommagetrennte Liste von Zahlen, optional mit Prozentangabe der Position auf der Zeitachse. linear(0, 0.5 50%, 1) etwa definiert drei Stützpunkte: bei 0 Prozent Zeit steht der Fortschritt bei 0, bei 50 Prozent Zeit bei 0.5, bei 100 Prozent Zeit bei 1. Zwischen den Stützpunkten interpoliert der Browser linear, was bei wenigen Punkten kantig wirkt, bei vielen Punkten aber praktisch nicht mehr von einer glatten Kurve zu unterscheiden ist.
Der entscheidende Unterschied zu cubic-bezier(): die Werte in linear() müssen nicht zwischen 0 und 1 liegen. Ein Wert von 1.1 bedeutet, dass die Animation an dieser Stelle 110 Prozent ihres Zielwerts erreicht, bevor sie wieder zurückschwingt. Diese Fähigkeit, den Zielwert kurzzeitig zu überschreiten und wieder zurückzukehren, ist die Grundlage jeder federartigen Easing-Kurve in reinem CSS.
/* Basic linear() syntax: comma-separated stops, optional position */
.simple-ease {
transition-timing-function: linear(0, 0.25, 0.75, 1);
}
/* Explicit position: value at a specific percentage of the duration */
.overshoot-preview {
transition-timing-function: linear(
0,
0.6 25%,
1.08 55%, /* overshoots past the target value */
0.98 75%,
1
);
}
Für ein einfaches Overshoot-Gefühl reichen bereits fünf bis sieben Stützpunkte aus. Für eine überzeugende, mehrfach oszillierende Federanimation mit sichtbarem Ausschwingen sind allerdings zwanzig bis vierzig Stützpunkte üblich, die man realistischerweise nicht mehr von Hand tippt, sondern aus einer physikalischen Formel generiert.
3. Eine Feder-Funktion mathematisch generieren
Die Physik einer gedämpften Feder lässt sich mit drei Parametern beschreiben: der Steifigkeit (stiffness), der Dämpfung (damping) und der Masse (mass) des bewegten Objekts. Die Standard-Formel für die Position einer gedämpften harmonischen Schwingung zur Zeit t lautet vereinfacht 1 - e^(-ζωt) · cos(ωd·t), wobei ζ den Dämpfungsgrad und ωd die gedämpfte Kreisfrequenz beschreibt. Diese Formel wird an vielen diskreten Zeitpunkten ausgewertet, jeder Wert wird zu einem Stützpunkt in der linear()-Liste.
In der Praxis schreibt man dafür ein kleines Skript, das für eine gewünschte Kombination aus Steifigkeit und Dämpfung eine feste Anzahl von Stützpunkten berechnet und als fertige linear()-Deklaration ausgibt. Dieses Skript muss nicht im Produktionscode laufen, es ist reines Build-Time-Tooling: das Ergebnis ist eine statische CSS-Zeile, die exakt dieselbe visuelle Kurve erzeugt wie eine JavaScript-Spring-Bibliothek zur Laufzeit, aber ohne jede Laufzeitkosten.
// Build-time script: generate a linear() easing string from spring physics
function springToLinear(stiffness = 300, damping = 20, mass = 1, steps = 30) {
const w0 = Math.sqrt(stiffness / mass);
const zeta = damping / (2 * Math.sqrt(stiffness * mass));
const wd = w0 * Math.sqrt(1 - zeta * zeta);
const points = [];
for (let i = 0; i <= steps; i++) {
const t = i / steps;
const decay = Math.exp(-zeta * w0 * t * 4); // scaled duration window
const value = 1 - decay * Math.cos(wd * t * 4);
points.push(value.toFixed(4));
}
return `linear(${points.join(', ')})`;
}
console.log(springToLinear(300, 20, 1, 24));
// linear(0, 0.1834, 0.4412, ..., 1.0812, ..., 0.9987, 1)
Diese generierte Zeichenkette wird einmalig in das Stylesheet kopiert und bleibt danach statisch. Ändert sich der gewünschte Feder-Charakter, etwa weniger Dämpfung für ein verspielteres Überschwingen, wird das Skript einmal neu ausgeführt und die neue Zeichenkette ersetzt die alte. Das ist derselbe Workflow wie bei generierten Sprite-Sheets oder komprimierten Assets: Generierung zur Build-Zeit, statisches Ergebnis zur Laufzeit.
4. Overshoot und Bounce mit linear() nachbilden
Nicht jede federartige Easing-Kurve muss aus einer vollständigen physikalischen Simulation stammen. Für viele UI-Zwecke reicht eine handgebaute Annäherung mit wenigen charakteristischen Stützpunkten, die den Eindruck einer Feder erzeugen, ohne den mathematischen Anspruch einer exakten Simulation zu erheben. Ein typisches Muster: schneller Anstieg, leichtes Überschwingen über den Zielwert, kurzes Zurückfedern, dann Einpendeln.
Für einen Bounce-Effekt, bei dem ein Element wie ein Ball mehrfach auf einer Fläche aufkommt, eignen sich mehrere aufeinanderfolgende Überschwinger mit abnehmender Amplitude. Diese Muster lassen sich als wiederverwendbare Rezepte katalogisieren: ein sanfter Spring für Tooltips, ein energischer Bounce für Erfolgsmeldungen, ein fast unmerklicher Overshoot für Hover-Zustände.
/* Gentle spring: subtle overshoot, good for tooltips and popovers */
:root {
--ease-spring-soft: linear(0, 0.52 30%, 0.98 55%, 1.02 70%, 1);
}
/* Energetic bounce: multiple decaying overshoots, good for success states */
:root {
--ease-spring-bounce: linear(
0, 0.35 15%, 0.72 30%, 1.06 45%,
0.92 58%, 1.03 68%, 0.98 78%, 1.01 88%, 1
);
}
.toast-enter {
animation: toast-in 0.5s var(--ease-spring-bounce) both;
}
@keyframes toast-in {
from { transform: translateY(-40px) scale(0.9); opacity: 0; }
}
Wichtig für glaubwürdige Federanimationen ist die Amplitude des Überschwingens: mehr als etwa acht bis zwölf Prozent über dem Zielwert wirkt schnell übertrieben und lenkt vom eigentlichen Inhalt ab. Für Mikro-Interaktionen wie Button-Klicks oder kleine Statuswechsel sind zwei bis fünf Prozent Überschwingen meist die überzeugendere Wahl, für auffälligere Elemente wie Modal-Dialoge oder Erfolgsmeldungen darf es etwas mehr sein.
5. Werkzeuge zur Erzeugung von linear()-Werten
Da niemand vierzig Stützpunkte von Hand berechnen möchte, haben sich mehrere webbasierte Generatoren etabliert, die eine Feder anhand von Steifigkeit, Dämpfung und Masse simulieren und die fertige linear()-Zeichenkette per Klick ausgeben, oft direkt mit einer Live-Vorschau der resultierenden Bewegung. Diese Werkzeuge übernehmen exakt die Rolle des im vorigen Abschnitt gezeigten Build-Skripts, nur mit grafischer Oberfläche statt Code.
Für Teams, die konsistente Federanimationen über viele Komponenten hinweg brauchen, lohnt sich trotzdem ein eigenes kleines Node-Skript im Repository, das exakt dieselbe Formel wie die Web-Tools verwendet, aber in die eigene Design-Token-Pipeline eingebunden ist. So bleiben Spring-Parameter versioniert und nachvollziehbar, statt in einer externen Web-App verborgen zu sein, deren Ergebnis nur als Copy-Paste-Text im Projekt landet.
6. Spring-Easings als Custom Properties wiederverwendbar machen
Eine generierte linear()-Zeichenkette ist lang und unhandlich, wenn sie in jeder einzelnen Regel wiederholt werden muss. Der saubere Weg ist, jede fertige federartige Easing-Kurve einmal als CSS-Custom-Property auf :root zu deklarieren und danach überall im Stylesheet per var() zu referenzieren, exakt wie ein Farbwert oder ein Spacing-Token.
Diese Herangehensweise macht Spring-Easings zu einem echten Design-Token: --ease-spring-soft, --ease-spring-bounce, --ease-spring-snappy und Ähnliches lassen sich zentral pflegen, in Dokumentation beschreiben und bei Bedarf projektweit austauschen, ohne dass jede einzelne Komponente angefasst werden müsste. Wird die Design-Sprache angepasst, ändert sich nur der Wert der Custom Property.
:root {
/* Named spring tokens — generated once, reused everywhere */
--ease-spring-snappy: linear(0, 0.62 22%, 1.05 42%, 0.99 60%, 1);
--ease-spring-soft: linear(0, 0.52 30%, 0.98 55%, 1.02 70%, 1);
}
.button {
transition: transform 0.3s var(--ease-spring-snappy);
}
.button:active {
transform: scale(0.96);
}
.modal-panel {
animation: modal-in 0.4s var(--ease-spring-soft) both;
}
7. Performance-Vergleich: linear() gegen JavaScript-Spring-Bibliotheken
Der Performance-Vorteil einer linear()-basierten Federanimation gegenüber einer JavaScript-Spring-Bibliothek liegt vor allem in der Ausführungsumgebung. Eine CSS-Animation mit linear()-Timing wird, sofern nur transform und opacity animiert werden, vollständig auf dem Compositor-Thread berechnet. Der Main-Thread bleibt frei für andere Aufgaben, selbst wenn er durch aufwendiges JavaScript blockiert ist, läuft die Animation weiter sichtbar flüssig.
Eine JavaScript-Spring-Bibliothek dagegen berechnet die Feder-Physik typischerweise pro requestAnimationFrame-Callback auf dem Main-Thread neu und schreibt das Ergebnis in Inline-Styles. Ist der Main-Thread durch andere Aufgaben belastet, etwa durch das Parsen einer großen JSON-Antwort, gerät die Animation ins Stocken. Für Bundle-Größe gilt derselbe Vorteil: eine linear()-Kurve kostet null zusätzliches JavaScript-Gewicht, während gängige Spring-Bibliotheken mehrere Kilobyte zur gezippten Bundle-Größe beitragen.
8. Grenzen von linear() und wann JavaScript nötig bleibt
So mächtig linear() für statische, vorab berechnete Kurven ist, für interaktive, physikbasierte Animationen mit laufend wechselnden Zielwerten stößt die Technik an ihre Grenzen. Ein Drag-Interaktion, bei der der Nutzer ein Element zieht und beim Loslassen eine Feder-Bewegung zum nächsten Rastpunkt einsetzen soll, kennt den exakten Zielwert erst zur Laufzeit. Eine vorab berechnete linear()-Kurve kann diesen dynamischen Zielwert nicht berücksichtigen, weil sie eine feste Sequenz von Stützpunkten für eine feste Distanz darstellt.
Für solche Fälle mit variabler Zieldistanz zur Laufzeit bleibt eine JavaScript-Spring-Bibliothek die richtige Wahl, weil sie die Physik bei jedem Frame neu mit dem aktuellen Zielwert berechnet. Ein sinnvoller Kompromiss in vielen Projekten: linear() für alle vorhersehbaren UI-Übergänge wie Modals, Toasts und Hover-Zustände nutzen, JavaScript-Springs nur dort einsetzen, wo tatsächlich Nutzerinteraktion in Echtzeit die Zieldistanz bestimmt.
9. Easing-Funktionen im Vergleich
Für die Entscheidung zwischen den verfügbaren Easing-Techniken hilft ein direkter Vergleich der wichtigsten Eigenschaften, von der klassischen Bézier-Kurve bis zur vollständig physikalisch generierten Feder.
| Technik | Überschwingen möglich | Laufzeitkosten | Dynamische Zieldistanz |
|---|---|---|---|
| cubic-bezier() | Nein | Keine | Nicht relevant |
| linear() Spring | Ja | Keine (Compositor) | Nein |
| JavaScript-Spring-Bibliothek | Ja | Main-Thread pro Frame | Ja |
| steps() | Nein | Keine | Nicht relevant |
Für die überwiegende Mehrheit der UI-Übergänge mit bekannter, fester Distanz ist linear() die überlegene Wahl: null Bundle-Gewicht, keine Main-Thread-Kosten, dieselbe visuelle Qualität wie eine Spring-Bibliothek. Nur bei echtzeit-interaktiven Drag-Gesten mit variabler Zieldistanz bleibt JavaScript unverzichtbar.
Mironsoft
Feingranulare Micro-Interaktionen und Animations-Design-Systeme
Animationen, die sich lebendig statt mechanisch anfühlen?
Wir entwickeln eigene linear()-basierte Spring-Easings für euer Design-System, inklusive Build-Tooling zur Generierung und Dokumentation als wiederverwendbare Custom Properties.
Analyse
Bestehende Animationen auf Konsistenz und Overshoot-Verhalten prüfen
Umsetzung
Spring-Token-Set generieren und als Custom Properties einbinden
Performance
Compositor-Thread-Check und Vergleich gegen bestehende JS-Bibliotheken
10. Zusammenfassung
Die CSS-Funktion linear() macht echte federartige Easing-Kurven ohne jede JavaScript-Bibliothek möglich. Anders als cubic-bezier() erlaubt sie Werte über 100 Prozent hinaus und damit ein Überschwingen über den Zielwert, das Kernmerkmal jeder Feder-Bewegung. Kurven mit wenigen Stützpunkten reichen für ein leichtes Overshoot-Gefühl, physikalisch generierte Kurven mit zwanzig bis vierzig Stützpunkten liefern mehrfach oszillierende, überzeugende Federanimationen.
Die generierten linear()-Werte werden am besten als benannte Custom Properties gepflegt, damit Spring-Easings zum echten Design-Token werden, statt in jeder Regel dupliziert zu sein. Der Performance-Vorteil gegenüber JavaScript-Spring-Bibliotheken ist erheblich: keine Main-Thread-Kosten, keine zusätzliche Bundle-Größe, dieselbe visuelle Qualität. Nur bei dynamischer Zieldistanz zur Laufzeit, etwa bei Drag-Interaktionen, bleibt eine JavaScript-Lösung die richtige Wahl.
Federartige Easing-Funktionen bauen — Das Wichtigste auf einen Blick
linear() Grundlage
Werte über 1 erzeugen Überschwingen, das mit cubic-bezier() nicht möglich ist.
Generierung
Feder-Physik einmalig zur Build-Zeit berechnen, statische linear()-Zeichenkette ins CSS kopieren.
Design-Tokens
Spring-Kurven als Custom Properties auf :root pflegen, zentral austauschbar.
Grenzen
Bei dynamischer Zieldistanz zur Laufzeit bleibt eine JavaScript-Spring-Bibliothek nötig.