Ratgeber · best practices
SVG-Pfade in CSS und JavaScript: clip-path, path() und Animation
Pfade sind mehr als Grafik: Mit clip-path und der CSS-path()-Funktion beschneidest du Elemente, mit der Web Animations API und animateMotion bewegst du Objekte entlang eines Pfades. Wie du ein d-Attribut aus dem Editor in echten Code übernimmst.
Ein SVG-Pfad ist zunächst nur eine Zeichenanweisung: das d-Attribut beschreibt eine Kurve, die der Browser rendert. Doch derselbe Pfad kann viel mehr, als eine Form auf den Bildschirm zu malen. Du kannst ihn als Schnittmaske über ein beliebiges HTML-Element legen, ein Objekt daran entlang bewegen oder ihn Strich für Strich “zeichnen” lassen. In diesem Ratgeber zeige ich Dir, wie Du ein d-Attribut aus dem Editor exportierst und in echten CSS- und JavaScript-Code übernimmst.
Wenn Du die Pfad-Syntax selbst (also was M, L, C und Q bedeuten) noch nicht sicher liest, lohnt vorher ein Blick in die SVG-Path-Grundlagen. Und wenn Dein Pfad aus einem exportierten Grafikprogramm stammt und unnötig lang ist, hilft Dir der Ratgeber zum SVG-Pfade optimieren, ihn vorher schlank zu bekommen.
Das d-Attribut aus dem Editor übernehmen
Alles beginnt mit der Zeichenkette, die im d-Attribut steht. Im Editor baust Du Deinen Pfad visuell zusammen und exportierst am Ende etwas wie das hier:
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<path d="M20,100 C20,40 180,40 180,100 S120,180 100,120 Z" fill="none" stroke="currentColor" />
</svg>
Der Wert von d ist genau die Zeichenkette, die Du in andere Kontexte kopierst: M20,100 C20,40 180,40 180,100 S120,180 100,120 Z. Wichtig ist, dass Du Dir merkst, in welchem Koordinatensystem der Pfad entstanden ist. Im Beispiel läuft der viewBox von 0 bis 200 in beiden Achsen. Diese Zahlen sind später der entscheidende Bezugspunkt, denn CSS und SVG interpretieren dieselben Koordinaten je nach Kontext unterschiedlich.
clip-path mit path(): Elemente beschneiden
Mit clip-path legst Du eine Maske über ein Element und blendest alles aus, was außerhalb liegt. Die CSS-Funktion path() nimmt dabei genau dieselbe Zeichenkette wie das d-Attribut entgegen. So beschneidest Du zum Beispiel ein Bild mit einer freien Form:
.beschnitten {
width: 200px;
height: 200px;
clip-path: path("M20,100 C20,40 180,40 180,100 S120,180 100,120 Z");
}
<img class="beschnitten" src="foto.jpg" alt="Beschnittenes Foto" />
Der wichtigste Stolperstein: Die Koordinaten in path() beziehen sich auf Pixel im Koordinatensystem des Elements, nicht auf einen viewBox. Ein Pfad, der für einen 200 mal 200 großen viewBox gezeichnet wurde, passt daher exakt auf ein Element von 200px mal 200px. Ist Dein Element größer oder kleiner, wird der Pfad nicht automatisch mitskaliert. Er bleibt bei seinen absoluten Pixelwerten stehen und sitzt dann falsch.
Es gibt zwei saubere Auswege. Entweder Du gibst dem Element genau die Maße des viewBox, oder Du wechselst auf shape() beziehungsweise nutzt prozentuale Formen wie polygon(), die relativ zur Elementgröße rechnen. Für einen fixen, exakt reproduzierten Editor-Pfad ist es meist am einfachsten, das Element auf die Pfad-Maße festzunageln und die Skalierung per transform: scale() zu erledigen.
Ein zweiter Hinweis: path() unterstützt optional eine Füllregel als erstes Argument, etwa path(evenodd, "..."). Das brauchst Du nur bei sich selbst überschneidenden Pfaden, bei denen Du steuern willst, welche Bereiche als “innen” gelten.
offset-path: ein Element entlang eines Pfades bewegen
offset-path (früher hieß die Eigenschaft motion-path) definiert eine unsichtbare Bahn, an der ein Element entlangwandert. Die Position auf dieser Bahn steuerst Du mit offset-distance, das von 0% (Anfang des Pfades) bis 100% (Ende) läuft. Auch hier akzeptiert offset-path die vertraute path()-Funktion:
.laeufer {
width: 24px;
height: 24px;
background: #e11d48;
border-radius: 50%;
offset-path: path("M20,100 C20,40 180,40 180,100 S120,180 100,120");
offset-distance: 0%;
offset-rotate: auto;
transition: offset-distance 1.2s ease-in-out;
}
.laeufer:hover {
offset-distance: 100%;
}
<div class="laeufer"></div>
Fährst Du mit der Maus über den Punkt, gleitet er weich vom Pfadanfang bis zum Ende. offset-rotate: auto sorgt dafür, dass sich das Element in Bewegungsrichtung dreht, was bei einem Pfeil oder einer Rakete sehr natürlich aussieht. Willst Du keine Drehung, setzt Du offset-rotate: 0deg.
Statt einer Transition kannst Du die Bewegung auch als Endlosschleife per Keyframes bauen:
@keyframes runde {
from { offset-distance: 0%; }
to { offset-distance: 100%; }
}
.laeufer {
offset-path: path("M20,100 C20,40 180,40 180,100 S120,180 100,120");
animation: runde 4s linear infinite;
}
Anders als bei clip-path beziehen sich die Koordinaten hier auf das umgebende Koordinatensystem des Elements, und das Element wird an seinem eigenen Ankerpunkt (standardmäßig der Mittelpunkt der Box) entlanggeführt. Auch das steuerst Du bei Bedarf über offset-anchor.
animateMotion: Bewegung direkt im SVG
Wenn Du ohnehin schon innerhalb einer SVG-Grafik arbeitest, brauchst Du für Bewegung entlang eines Pfades gar kein CSS. Das SVG-eigene Element animateMotion erledigt das per SMIL (Synchronized Multimedia Integration Language). Der Clou: Mit einem verschachtelten mpath verweist die Animation auf einen bereits vorhandenen path, sodass Du die Zeichenkette nur ein einziges Mal pflegst.
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<path id="bahn" d="M20,100 C20,40 180,40 180,100 S120,180 100,120"
fill="none" stroke="#cbd5e1" />
<circle r="8" fill="#e11d48">
<animateMotion dur="4s" repeatCount="indefinite" rotate="auto">
<mpath href="#bahn" />
</animateMotion>
</circle>
</svg>
Der circle wandert hier vier Sekunden lang an der grauen Bahn entlang und beginnt danach von vorn. rotate="auto" erfüllt denselben Zweck wie offset-rotate: auto in CSS. Über mpath href="#bahn" bindest Du exakt den Pfad ein, der schon sichtbar gezeichnet ist. Änderst Du das d-Attribut des Pfades, folgt die Animation automatisch.
Ein ehrliches Wort zu SMIL: Die Technik gilt als teils veraltet, Chrome hatte vor Jahren sogar eine Abschaffung angekündigt und wieder zurückgenommen. In der Praxis funktioniert animateMotion heute in allen gängigen Browsern zuverlässig. Für neue Projekte, die stark auf Steuerbarkeit per JavaScript setzen, ist offset-path oder die Web Animations API aber meist die zukunftssicherere Wahl.
Web Animations API und das d-Attribut animieren
Mit JavaScript hast Du die feinste Kontrolle. Die Web Animations API (element.animate()) lässt Dich zum Beispiel offset-distance direkt animieren, ohne dafür CSS-Keyframes anzulegen:
const laeufer = document.querySelector(".laeufer");
laeufer.animate(
[{ offsetDistance: "0%" }, { offsetDistance: "100%" }],
{ duration: 4000, iterations: Infinity, easing: "ease-in-out" }
);
Besonders reizvoll ist das Animieren des d-Attributs selbst: Du lässt eine Form in eine andere übergehen (Morphing). Damit das sauber interpoliert, müssen Start- und Zielpfad dieselbe Anzahl und Art von Befehlen haben. Das ist der Punkt, an dem sich vorheriges Aufräumen der Pfade auszahlt.
const pfad = document.querySelector("#bahn");
pfad.animate(
[
{ d: 'path("M20,100 C20,40 180,40 180,100 S120,180 100,120")' },
{ d: 'path("M20,100 C60,20 140,20 180,100 S120,140 100,110")' }
],
{ duration: 800, fill: "forwards", easing: "ease" }
);
Der Zeichnen-Effekt mit stroke-dasharray und stroke-dashoffset
Zum Schluss ein Klassiker, der fast immer beeindruckt: der Line-Drawing-Effekt, bei dem sich ein Pfad wie von Geisterhand selbst zeichnet. Das Prinzip dahinter ist ein cleverer Trick mit gestrichelten Linien.
stroke-dasharray legt ein Strichmuster fest. Setzt Du die Strichlänge auf die Gesamtlänge des Pfades, besteht die “gestrichelte” Linie aus genau einem einzigen langen Strich plus einer ebenso langen Lücke. Mit stroke-dashoffset verschiebst Du dieses Muster. Startest Du mit einem Offset in Höhe der vollen Länge, ist der Strich komplett aus dem Sichtfeld geschoben und der Pfad wirkt unsichtbar. Fährst Du den Offset auf 0 herunter, schiebt sich der Strich Stück für Stück ins Bild und der Pfad “wird gezeichnet”.
Die Pfadlänge liest Du in JavaScript zuverlässig mit getTotalLength() aus:
const pfad = document.querySelector("#bahn");
const laenge = pfad.getTotalLength();
pfad.style.strokeDasharray = laenge;
pfad.style.strokeDashoffset = laenge;
#bahn {
transition: stroke-dashoffset 2s ease-in-out;
}
requestAnimationFrame(() => {
pfad.style.strokeDashoffset = "0";
});
Der requestAnimationFrame-Aufruf stellt sicher, dass der Browser den Ausgangszustand (Offset gleich Länge) einmal rendert, bevor der Wert auf 0 springt. Sonst gäbe es keinen sichtbaren Übergang. Wer keine Berechnung per JavaScript möchte, kann in modernen Browsern auch mit dem CSS-Schlüsselwort pathLength und dem Wert context-stroke arbeiten, aber der getTotalLength()-Weg ist der robusteste über alle Browser hinweg.
Browser-Unterstützung ehrlich eingeordnet
Damit Du weißt, worauf Du Dich verlassen kannst:
clip-path: path()wird von allen aktuellen Browsern breit unterstützt und ist alltagstauglich.offset-pathmitoffset-distanceist ebenfalls breit unterstützt, teils musst Du bei sehr alten Versionen mit dem alten Namenmotion-pathrechnen. Für heutige Projekte ist der Support solide.animateMotion(SMIL) funktioniert in allen gängigen Browsern, gilt aber als teils veraltet. Setze es ein, wenn Du innerhalb von SVG bleibst und keine komplexe JS-Steuerung brauchst.stroke-dasharrayundstroke-dashoffsetsind uralte, überall unterstützte SVG-Eigenschaften. Der Zeichnen-Effekt läuft praktisch überall.- Das Animieren des
d-Attributs per Web Animations API ist die modernste Technik hier und wird von aktuellen Browsern unterstützt, du solltest sie aber im Zweifel gezielt gegenprüfen.
Der rote Faden bleibt in allen Fällen derselbe: Du baust Deinen Pfad visuell im Editor, exportierst die d-Zeichenkette und übernimmst sie unverändert in clip-path, offset-path, mpath oder Dein JavaScript. Ein einziger sauber gezeichneter Pfad trägt so vom stillen Bild bis zur animierten Interaktion. Wenn Du die Zeichenkette vor dem Einbau noch verschlankst, wie im Ratgeber zum SVG-Pfade optimieren beschrieben, bleibt Dein Code dabei angenehm lesbar und Deine Animationen laufen flüssig.
Quellen
- https://developer.mozilla.org/de/docs/Web/CSS/basic-shape/path
- https://developer.mozilla.org/de/docs/Web/CSS/offset-path
- https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animateMotion
Korrekturen oder bessere Quellen? Schreib an info@akara-solutions.de. Änderungen landen mit Datum auf /korrekturen.
