Svelte and SvelteKit course Β· Module 9: Transitions and Animations
Motion: Tween and Spring
In this lesson4
The svelte/motion module has two classes for changing values smoothly: Tween and Spring (since Svelte 5.8). Both have two important fields:
target- the value you are heading to (you can assign it),current- the current, animated value (read-only and reactive).
The set(value, options) method also sets the target and returns a Promise that resolves when the animation finishes.
Tween - Smooth value transitions
<script>
import { Tween } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';
const progress = new Tween(0, {
duration: 400,
easing: cubicOut
});
</script>
<button onclick={() => progress.target = 100}>100%</button>
<button onclick={() => progress.target = 0}>0%</button>
<progress value={progress.current} max="100"></progress>
<p>{Math.round(progress.current)}%</p>
<style>
progress {
width: 100%;
height: 20px;
}
</style>Spring - Spring physics
<script>
import { Spring } from 'svelte/motion';
const coords = new Spring({ x: 50, y: 50 }, {
stiffness: 0.1, // stiffness
damping: 0.5 // damping
});
function handleMouseMove(e) {
coords.target = {
x: e.clientX,
y: e.clientY
};
}
</script>
<svelte:window onmousemove={handleMouseMove} />
<svg>
<circle cx={coords.current.x} cy={coords.current.y} r="20" fill="gold" />
</svg>Example: Animated chart
<script>
import { Tween } from 'svelte/motion';
const data = new Tween([10, 20, 30, 40, 50], { duration: 500 });
function randomize() {
data.target = Array(5).fill(0).map(() => Math.random() * 100);
}
</script>
<button onclick={randomize}>Randomize</button>
<div class="chart">
{#each data.current as value, i}
<div class="bar" style="height: {value}%">
{Math.round(value)}
</div>
{/each}
</div>
<style>
.chart {
display: flex;
gap: 10px;
height: 200px;
align-items: flex-end;
}
.bar {
flex: 1;
background: gold;
transition: height 0.3s;
}
</style>The older API: tweened and spring
Before Svelte 5.8 you used the tweened() and spring() functions, which return stores read with $progress. They still work, but they are marked as deprecated. In new code, choose the Tween and Spring classes.
// Before (store)
const progress = tweened(0); // in the template: {$progress}
// Svelte 5
const progress = new Tween(0); // in the template: {progress.current}Code for this lesson: App.svelte
<script>
// Motion: the Tween and Spring classes (Svelte 5)
import { Tween, Spring } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';
// Tween - smooth value interpolation over a set time
const progress = new Tween(0, {
duration: 400,
easing: cubicOut
});
// Spring - spring-physics animation
const coords = new Spring({ x: 50, y: 50 }, {
stiffness: 0.1,
damping: 0.25
});
function handleMouseMove(e) {
const rect = e.currentTarget.getBoundingClientRect();
coords.set({
x: e.clientX - rect.left,
y: e.clientY - rect.top
});
}
</script>
<main class="motion-demo">
<h1>Motion: Tween and Spring</h1>
<section>
<h2>Tween - Progress bar</h2>
<div class="progress-bar">
<div class="fill" style="width: {progress.current}%"></div>
</div>
<p>{Math.round(progress.current)}%</p>
<div class="buttons">
<button onclick={() => progress.target = 0}>0%</button>
<button onclick={() => progress.target = 50}>50%</button>
<button onclick={() => progress.target = 100}>100%</button>
</div>
</section>
<section>
<h2>Spring - Following element</h2>
<div class="spring-area" onmousemove={handleMouseMove}>
<div
class="follower"
style="left: {coords.current.x}px; top: {coords.current.y}px"
>
α
</div>
<p class="hint">Move cursor</p>
</div>
</section>
</main>
<style>
.motion-demo {
font-family: 'Georgia', serif;
max-width: 500px;
margin: 0 auto;
padding: 2rem;
background: #1a0a0a;
color: #f7d5e8;
min-height: 100vh;
}
h1 {
color: #ff6b6b;
text-align: center;
}
section {
background: rgba(255, 107, 107, 0.1);
border: 1px solid #ff6b6b;
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
}
h2 {
color: #ff6b6b;
font-size: 1rem;
margin-top: 0;
}
.progress-bar {
height: 30px;
background: rgba(0,0,0,0.5);
border-radius: 15px;
overflow: hidden;
margin-bottom: 0.5rem;
}
.fill {
height: 100%;
background: linear-gradient(90deg, #ff6b6b, #ffd700);
border-radius: 15px;
}
.buttons {
display: flex;
gap: 0.5rem;
}
button {
flex: 1;
padding: 0.5rem;
background: #3a1a2a;
color: #f7d5e8;
border: 1px solid #ff6b6b;
border-radius: 4px;
cursor: pointer;
}
.spring-area {
position: relative;
height: 200px;
background: rgba(0,0,0,0.3);
border-radius: 8px;
overflow: hidden;
}
.follower {
position: absolute;
font-size: 2rem;
transform: translate(-50%, -50%);
pointer-events: none;
}
.hint {
position: absolute;
bottom: 10px;
width: 100%;
text-align: center;
color: #a87ca8;
font-size: 0.85rem;
}
</style>Docs entries used in this lesson
Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. How does Spring differ from Tween?