Während transition: und animate: Elemente animieren, animiert svelte/motion Werte. Wenn ein Counter von 0 auf 100 springt, kannst du ihn stattdessen sanft hochzählen lassen — von Frame zu Frame interpoliert. Das Modul svelte/motion bietet dafür zwei Klassen: Tween für gleichmäßige Animation und Spring für physikalisch wirkende Feder-Bewegungen. Dieser Artikel erklärt beide mit verständlichen Beispielen.

Was ist motion?

Stell dir vor, du hast einen Zähler, der sich von 0 auf 100 ändern soll. Standardmäßig springt er sofort auf den neuen Wert. Mit svelte/motion läuft er in 400 Millisekunden hoch — und der Wert lässt sich währenddessen reaktiv anzeigen.

Das funktioniert für alles, was sich numerisch ausdrücken lässt: Counter, Positionen, Skalierungen, Farben (in RGB-Werten), Maße — sogar verschachtelte Objekte und Arrays.

Tween — gleichmäßige Animation

Eine Tween-Instanz hält einen aktuellen Wert und eine Ziel-Komponente. Sobald du das Ziel änderst, läuft der aktuelle Wert in der eingestellten Dauer dorthin.

svelte Sanft hochzählen
<script>
    import { Tween } from 'svelte/motion';

    const progress = new Tween(0, { duration: 800 });
</script>

<div class="bar" style:width="{progress.current}%"></div>

<button onclick={() => progress.target = Math.random() * 100}>
    Zufallswert
</button>

<style>
    .bar {
        height: 24px;
        background: teal;
        transition: none;
    }
</style>

Das Spannende:

  • progress.current ist der aktuelle, animierte Wert. Ihn liest du im Markup.
  • progress.target ist das Ziel. Setzt du es, fängt der Tween an, dort hinzulaufen.
  • Der Übergang ist glatt — kein abruptes Springen.

Optionen

OptionBeschreibung
durationDauer in Millisekunden (Default: 400) oder Funktion (from, to) => ms
delayVerzögerung vor Start
easingEasing-Funktion (Default: linear)
interpolateEigene Interpolations-Funktion für komplexe Werte
ts Tween mit Easing
import { Tween } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';

const opacity = new Tween(0, {
    duration: 600,
    easing: cubicOut,
});

opacity.target = 1; // löst Animation aus

Spring — physikalisch wirkende Feder

Spring simuliert eine Feder. Statt einer festen Dauer hat sie zwei Parameter:

  • stiffness — wie „stramm” die Feder ist (Default 0.15). Höher = schneller.
  • damping — wie viel Energie pro Schwingung verloren geht (Default 0.8). Höher = weniger Nachschwingen.
svelte Spring-basierter Cursor-Follower
<script>
    import { Spring } from 'svelte/motion';

    const position = new Spring({ x: 0, y: 0 }, {
        stiffness: 0.1,
        damping: 0.25,
    });

    function moveTo(event) {
        position.target = { x: event.clientX, y: event.clientY };
    }
</script>

<svelte:window onmousemove={moveTo} />

<div
    class="dot"
    style:transform="translate({position.current.x}px, {position.current.y}px)"
></div>

<style>
    .dot {
        position: fixed;
        width: 24px;
        height: 24px;
        background: teal;
        border-radius: 999px;
        pointer-events: none;
    }
</style>

Der Punkt folgt der Maus — aber mit leichtem Nachzieh-Effekt. Schnelle Bewegungen lassen ihn etwas „über das Ziel hinausschießen”, danach pendelt er ein. Das ist der typische Federn-Effekt.

Wann Tween, wann Spring?

SituationEmpfehlung
Klar definierte Animation mit fester DauerTween
Gefühl von Trägheit und NachschwingenSpring
Werte, die laufend in unterschiedliche Ziele wechselnSpring (nahtlos)
Skalen-Bar mit ZielwertTween
Drag-FollowerSpring

Animierte Zahlen-Anzeige

Ein praktisches Beispiel: Ein Counter, der bei Klick sanft auf einen neuen Wert hochläuft.

svelte Animated-Number.svelte
<script>
    import { Tween } from 'svelte/motion';
    import { cubicOut } from 'svelte/easing';

    let { value } = $props();

    const tween = new Tween(value, {
        duration: 600,
        easing: cubicOut,
    });

    $effect(() => {
        tween.target = value;
    });
</script>

<span>{Math.round(tween.current)}</span>
svelte Verwendung
<script>
    import AnimatedNumber from '$lib/components/AnimatedNumber.svelte';

    let count = $state(0);
</script>

<AnimatedNumber value={count} />
<button onclick={() => count = Math.floor(Math.random() * 1000)}>
    Zufallszahl
</button>

Der $effect triggert beim Wechsel von value ein neues Animations-Ziel. Math.round sorgt dafür, dass keine Nachkommastellen angezeigt werden.

Komplexe Werte: Objekte und Arrays

Sowohl Tween als auch Spring können nicht nur Zahlen, sondern auch Objekte oder Arrays animieren — solange jede Zahl darin animierbar ist.

ts Objekt-Animation
import { Spring } from 'svelte/motion';

const box = new Spring(
    { x: 0, y: 0, rotation: 0, scale: 1 },
    { stiffness: 0.1, damping: 0.6 }
);

// Alle vier Werte gleichzeitig animieren
box.target = { x: 100, y: 50, rotation: 45, scale: 1.5 };

Beim Auslesen kannst du auf jede Property einzeln zugreifen — box.current.x, box.current.rotation etc. — und sie im Markup verwenden.

Direktes Setzen ohne Animation

Manchmal willst du den Wert ohne Animation ändern — etwa beim Initialisieren oder bei einem harten Reset. Beide Klassen bieten dafür set():

ts Sofort setzen
await tween.set(100, { duration: 0 });
// oder
await spring.set({ x: 0, y: 0 }, { hard: true });

set() gibt ein Promise zurück, das aufgelöst wird, wenn die Animation fertig ist. Mit duration: 0 (Tween) oder hard: true (Spring) ist sie sofort fertig.

Vergleich zu CSS-Transitions

Wer hauptsächlich Style-Properties animieren will, kann oft auch klassische CSS-Transitions verwenden:

css Klassische CSS-Transition
.bar {
    transition: width 600ms cubic-bezier(0.4, 0, 0.2, 1);
}

Wann ist svelte/motion trotzdem die bessere Wahl?

  • Wenn du den animierten Wert im Code lesen willst (z. B. zum Anzeigen einer Zahl).
  • Wenn du Spring-Verhalten willst — CSS hat das nicht.
  • Wenn du den Wert für mehrere Stellen im Markup brauchst (eine Quelle, viele Ausgaben).
  • Wenn die Animation dynamisch auf Wert-Änderungen reagieren soll, nicht nur auf einmalige Auslöser.

Sonst ist klassisches CSS leichter und performanter.

Häufige Stolperfallen

current und target verwechselt. current ist der animierte Zwischenwert, den du im Markup anzeigst. target ist das Endziel, das du setzt. Wer current direkt setzt, springt — keine Animation.

Initialwert nach Mount setzen. Wenn du einen Tween mit Initial 0 erzeugst und dann tween.target = props.value setzt, animiert die erste Anzeige von 0 auf den Wert. Manchmal nicht gewünscht. Lösung: mit tween.set(props.value, { duration: 0 }) initial setzen.

Spring mit zu hartem damping. Bei damping = 1 schwingt die Feder gar nicht — sie kommt direkt zur Ruhe. Bei damping = 0 schwingt sie ewig. Realistische Werte zwischen 0.4 und 0.9.

Tween für UI-Animationen, die per CSS gehen. Etwas, das nur ein einzelnes Style-Property ändert, ist mit klassischem CSS transition meist einfacher und performanter.

Speicher-Leak bei wechselnden Tweens. Eine neue Tween-Instanz pro Render wäre ineffizient. Stattdessen einmal außerhalb von Effects erzeugen und mit target = aktualisieren.

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Transitions & Animations

Zur Übersicht