Svelte journey | Motion, Transitions, Animations
Tags: svelte, tutorial, javascript, frontend
Published: 2023-12-26 07:30:18



Since this chapter we shift to more in-depth Svelte themes. In this part we’ll cover visual tooling that comes from Svelte. It is really great that you can have beauty things right out of the box, without reinventing the wheel and with no need for extra tooling.

## Motion

Functions that produce store-like value that changes in a way to have a smooth & good looking behaviour. Basically, it calculates all required values and emits them in the required interval of time (it has something more under the hood, this description is rather just to catch the idea).

### Tweened

```jsx
<script>
	import { tweened } from 'svelte/motion';
	import { cubicOut } from 'svelte/easing';

	
	const progress = tweened(0, { // second param is optional 
		duration: 400,
		easing: cubicOut,
		delay: ..., // ms
		easing: ..., // p => t
		duration: ..., // (from, to) => milliseconds
		interpolate: ..., // (from, to) => t => value
	});
</script>
...
progress.set(50); // or progress.update(v => v + 1);
...
<input bind:value={progress} type="range"	/>
```

### Spring

The `spring` function is an alternative to `tweened` that often works better for values that are frequently changing.

```jsx
<script>
	import { spring } from 'svelte/motion';
	let coords = spring({ x: 50, y: 50 }, {
		stiffness: 0.1,
		damping: 0.25
	});
</script>
...
<input bind:value={coords.stiffness} /> // damping/stiffness update is possible later
<circle	cx={$coords.x} cy={$coords.y} /> // operate then as with regular store
```

## [Transition](https://svelte.dev/docs/svelte-transition)

Differs from **motion** as motion it rather about **target value**, but transition is rather about **target DOM Object.**
```jsx
<script>
	import { fade, fly } from 'svelte/transition';
	let visible = true;
</script>

<input type="checkbox" bind:checked={visible} /> // <p /> will fade in/out
{#if visible} 
 <p transition:fade>Fades in and out</p>
 <p transition:fly={{ y: 200, duration: 2000 }}>Params are optional</>
 <p in:fly={{ y: 200, duration: 2000 }} out:fade>Flies in, fades out</p> // diferent in, out
{/if}
...
```

### `If` block + transition = ❤️

Remember times when you hide something with your framework via if-else templating and this block just disappears instantly as a ninja because it literally is cut from DOM? Forget about this in Svelte (at least, in this case) — it takes care and hides/shows the element in transitioned, expected manner. If there are several blocks, you may need to use **global transition** that is described next.

### Global transitions — transition for any element

Add **|global** when you need to apply transition when *any* block containing the transitions is added or removed. E.g. [toggling the visibility of the entire list](https://learn.svelte.dev/tutorial/global-transitions) does not apply transitions to individual list elements.

```jsx
{#if showItems}
	{#each items.slice(0, i) as item}
		<div transition:slide|global> // when showItems changes, global still makes transition to work as expected
			{item}
		</div>
	{/each}
{/if}
```

### `{#key}` Key blocks — recreate children’s DOM when an expression changes

When you need to have transition not only when element enters/leaves the DOM, but when some value changes.

```jsx
{#key i} // when I changes, inside stuff recreates. As a result in: transition is shown when fresh 'i' comes
	<p in:typewriter={{ speed: 10 }}>
		{messages[i] || ''}
	</p>
{/key}
```

### Transition events

Usage as regular DOM event:

```jsx
<div
	transition:fly={{ y: 200, duration: 2000 }}
	on:introstart={() => status = 'intro started'}
	on:outrostart={() => status = 'outro started'}
	on:introend={() => status = 'intro ended'}
	on:outroend={() => status = 'outro ended'}
/>
```

### Custom transitions  — write own functions if default set is not enough

To have something custom you need to build a function that meets transition functions contract. Attaching here samples from official tutorial:

```jsx

<script>
	import { fade } from 'svelte/transition';
	import { elasticOut } from 'svelte/easing';

	let visible = true;

  // Custom CSS (css property is returned)
	function spin(node, { duration }) {
		return {
			duration,
			css: (t) => {
				const eased = elasticOut(t);

				return `
					transform: scale(${eased}) rotate(${eased * 1080}deg);
					color: hsl(
						${Math.trunc(t * 360)},
						${Math.min(100, 1000 * (1 - t))}%,
						${Math.min(50, 500 * (1 - t))}%
					);`;
			}
		};
	}
// Custom JS (tick property is returned)
function typewriter(node, { speed = 1 }) {
	const text = node.textContent;
	const duration = text.length / (speed * 0.01);

	return {
		duration,
		tick: (t) => {
			const i = Math.trunc(text.length * t);
			node.textContent = text.slice(0, i);
		}
	};
</script>
...
<div out:fade	... />
<p transition:typewriter ... />
```

### [Deferred transitions](https://learn.svelte.dev/tutorial/deferred-transitions) — when you need a smooth transit between different host components

This feature looks really cool. DOM elements literally fly out from on parent component into another. To achieve it you need to define a `crossfade` function and then assign its output to target object.

The `crossfade` function creates a pair of [transitions](https://svelte.dev/docs/element-directives#transition-fn) called `send` and `receive`. Then, its output is assigned to desired markup element that is expected to be moved across:

```jsx
export const [send, receive] = crossfade({...});
...
<li
	class:done
	in:receive={{ key: todo.id }}
	out:send={{ key: todo.id }}
>
```

When an element is '**sent**', it looks for a corresponding element being '**received**', and generates a transition that transforms the element to its counterpart's position and fades it out. When an element is 'received', the reverse happens. If there is no counterpart, the `fallback` transition is used.

### Animation

```jsx
<script>
	import { flip } from 'svelte/animate';
	...
</script>
...
<li
	...
	animate:flip // animate:flip={{ duration: 200 }} | animate:flip={{ duration: d => millis }}
>
```

We’ve covered the main topics regarding the theme, still it is rather an overview what exists in the toolbox and to master this properly will require some extra efforts.

Take care, go Svelte!

## Resources

- [Svelte Tutorial](https://learn.svelte.dev/tutorial) | Motions, Transitions, Animations
- [Svelte transition docs](https://svelte.dev/docs/svelte-transition)