The transition

We can create a more attractive user interface by graciously displaying elements in and out of the DOM. This becomes very easy in Svelte using the Transition instruction.

First, import the fade in and out function from svelte/ Transition:

<script>
	import { fade } from 'svelte/transition';
	let visible = true;
</script>
Copy the code

Then add it to the

element:

<p transition:fade>Fades in and out</p>
Copy the code

Complete code:

<script>
	import { fade } from 'svelte/transition';
	let visible = true;
</script>

<label>
	<input type="checkbox" bind:checked={visible}>
	visible
</label>

{#if visible}
	<p transition:fade>
		Fades in and out
	</p>
{/if}
Copy the code

Add parameters

Transition functions can take arguments. Replace fade with fly:

<script>
	import { fly } from 'svelte/transition';
	let visible = true;
</script>
Copy the code

And apply it to

and some options:

<p transition:fly="{{ y: 200, duration: 2000 }}">
	Flies in and out
</p>
Copy the code

Note that the transition is reversible. If you switch the checkbox while the transition is in progress, it will transition from the current point, not from the start or end point.

Complete code:

<script>
	import { fly } from 'svelte/transition';
	let visible = true;
</script>

<label>
	<input type="checkbox" bind:checked={visible}>
	visible
</label>

{#if visible}
	<p transition:fly="{{ y: 200, duration: 2000 }}">
		Flies in and out
	</p>
{/if}
Copy the code

The fading

An element can have an in or out instruction, or both, instead of the transition instruction. Import fade and fly:

import { fade, fly } from 'svelte/transition';
Copy the code

Then replace the transition instructions with separate input and output instructions:

<p in:fly="{{ y: 200, duration: 2000 }}" out:fade>
	Flies in, fades out
</p>
Copy the code

In this case, the transition is not reversed.

Customize CSS transitions

The Svelte/Transition module has some transitions built in, but it’s easy to create your own. For example, here is the source code for the fade transition:

function fade(node, {
	delay = 0,
	duration = 400
}) {
	const o = +getComputedStyle(node).opacity;

	return {
		delay,
		duration,
		css: t= > `opacity: ${t * o}`
	};
}
Copy the code

This function takes two arguments: the node to which the transition is applied and the passed parameter object, and returns an object with the following properties:

  • Delay: The number of milliseconds before the transition begins

  • Duration: The duration of the transition (in milliseconds)

  • Easing: a p=>t function

  • CSS: a (t,u)=> CSS function where u===1-t

  • Tick: one (t, u) => {… } function, which has some effect on the node

At t=0, the animation is played from the beginning, and at t=1, the animation is reversed.

In most cases, you should return the CSS property instead of the TICK property, because the CSS animation runs from the main thread. Svelte “emulates” the transition and constructs a CSS animation, and then lets it run.

For example, the fade transition produces a CSS animation that looks like the following:

0% { opacity: 0 }
10% { opacity: 0.1 }
20% { opacity: 0.2 }
/ *... * /
100% { opacity: 1 }
Copy the code

However, we can be more creative:

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

	let visible = true;

	function spin(node, { duration }) {
		return {
			duration,
			css: t= > {
				const eased = elasticOut(t);

				return `
					transform: scale(${eased}) rotate(${eased * 1080}deg);
					color: hsl(
						${~~(t * 360)}.The ${Math.min(100.1000 - 1000 * t)}%,
						The ${Math.min(50.500 - 500 * t)}%
					);`}}; }</script>
Copy the code

Remember: with great power comes great responsibility.

Complete code:

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

	let visible = true;

	function spin(node, { duration }) {
		return {
			duration,
			css: t= > {
				const eased = elasticOut(t);

				return `
					transform: scale(${eased}) rotate(${eased * 1080}deg);
					color: hsl(
						${~~(t * 360)}.The ${Math.min(100.1000 - 1000 * t)}%,
						The ${Math.min(50.500 - 500 * t)}%
					);`}}; }</script>

<label>
	<input type="checkbox" bind:checked={visible}>
	visible
</label>

{#if visible}
	<div class="centered" in:spin="{{duration: 8000}}" out:fade>
		<span>transitions!</span>
	</div>
{/if}

<style>
	.centered {
		position: absolute;
		left: 50%;
		top: 50%;
		transform: translate(-50%, -50%);
	}

	span {
		position: absolute;
		transform: translate(-50%, -50%);
		font-size: 4em;
	}
</style>
Copy the code

Custom JS transitions

While you should generally use CSS as much as possible for transitions, there are some effects you can’t achieve without JavaScript, such as the typewriter effect:

function typewriter(node, { speed = 50 }) {
	const valid = (
		node.childNodes.length === 1 &&
		node.childNodes[0].nodeType === Node.TEXT_NODE
	);

	if(! valid) {throw new Error(`This transition only works on elements with a single text node child`);
	}

	const text = node.textContent;
	const duration = text.length * speed;

	return {
		duration,
		tick: t= > {
			const i = ~~(text.length * t);
			node.textContent = text.slice(0, i); }}; }Copy the code

Complete code:

<script>
	let visible = false;

	function typewriter(node, { speed = 50 }) {
		const valid = (
			node.childNodes.length === 1 &&
			node.childNodes[0].nodeType === Node.TEXT_NODE
		);

		if(! valid) {throw new Error(`This transition only works on elements with a single text node child`);
		}

		const text = node.textContent;
		const duration = text.length * speed;

		return {
			duration,
			tick: t= > {
				const i = ~~(text.length * t);
				node.textContent = text.slice(0, i); }}; }</script>

<label>
	<input type="checkbox" bind:checked={visible}>
	visible
</label>

{#if visible}
	<p in:typewriter>
		The quick brown fox jumps over the lazy dog
	</p>
{/if}

Copy the code

Transition events

It can be useful to know when the transition begins and ends. Like any other DOM event, Svelte can listen for transition events:

<p
	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'}"
>
	Flies in and out
</p>
Copy the code

Complete code:

<script>
	import { fly } from 'svelte/transition';

	let visible = true;
	let status = 'waiting... ';
</script>

<p>status: {status}</p>

<label>
	<input type="checkbox" bind:checked={visible}>
	visible
</label>

{#if visible}
	<p
		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'}"
	>
		Flies in and out
	</p>
{/if}
Copy the code

Local transition

Typically, transitions work on elements when any container block is added or destroyed. In this example, switching the visibility of the entire list also applies transitions to individual list elements.

Instead, we want transitions to play only when individual items are added and removed.

We can do this with a local transformation that plays only when the current node is added or removed:

<div transition:slide|local>
	{item}
</div>
Copy the code

Complete code:

<script>
	import { slide } from 'svelte/transition';

	let showItems = true;
	let i = 5;
	let items = ['one'.'two'.'three'.'four'.'five'.'six'.'seven'.'eight'.'nine'.'ten'];
</script>

<label>
	<input type="checkbox" bind:checked={showItems}>
	show list
</label>

<label>
	<input type="range" bind:value={i} max=10>

</label>

{#if showItems}
	{#each items.slice(0, i) as item}
		<div transition:slide|local>
			{item}
		</div>
	{/each}
{/if}

<style>
	div {
		padding: 0.5 em 0;
		border-top: 1px solid #eee;
	}
</style>
Copy the code

Delay the transition

A particularly powerful feature of Svelte’s transition engine is the ability to delay transitions so that they can be coordinated between multiple elements.

Take this pair of Todo lists, where todo todo todo sends it to the opposite list. In the real world, objects don’t behave like this, they move through a series of intermediate positions, rather than disappearing and reappearing in another place. Using Motion helps users understand what is happening in the application.

We can do this using the Crossfade function, which creates a pair of transitions called Send and Receive. When an element is “sent,” it looks for a corresponding element to “receive,” and generates a transition that transitions the element to the corresponding position and fades out. When an element is “received,” the reverse is true. If there is no corresponding entry, the fallback transition is used.

Complete code:

<script>
	import { quintOut } from 'svelte/easing';
	import { crossfade } from 'svelte/transition';

	const [send, receive] = crossfade({
		duration: d= > Math.sqrt(d * 200),

		fallback(node, params) {
			const style = getComputedStyle(node);
			const transform = style.transform === 'none' ? ' ' : style.transform;

			return {
				duration: 600.easing: quintOut,
				css: t= > `
					transform: ${transform} scale(${t});
					opacity: ${t}
				`}; }});let uid = 1;

	let todos = [
		{ id: uid++, done: false.description: 'write some docs' },
		{ id: uid++, done: false.description: 'start writing blog post' },
		{ id: uid++, done: true.description: 'buy some milk' },
		{ id: uid++, done: false.description: 'mow the lawn' },
		{ id: uid++, done: false.description: 'feed the turtle' },
		{ id: uid++, done: false.description: 'fix some bugs'},];function add(input) {
		const todo = {
			id: uid++,
			done: false.description: input.value
		};

		todos = [todo, ...todos];
		input.value = ' ';
	}

	function remove(todo) {
		todos = todos.filter(t= >t ! == todo); }function mark(todo, done) {
		todo.done = done;
		remove(todo);
		todos = todos.concat(todo);
	}
</script>

<div class='board'>
	<input
		placeholder="what needs to be done?"
		on:keydown={e= > e.key === 'Enter' && add(e.target)}
	>

	<div class='left'>
		<h2>todo</h2>{#each todos.filter(t => ! t.done) as todo (todo.id)}<label
				in:receive="{{key: todo.id}}"
				out:send="{{key: todo.id}}"
			>
				<input type=checkbox on:change={()= > mark(todo, true)}>
				{todo.description}
				<button on:click="{() => remove(todo)}">remove</button>
			</label>
		{/each}
	</div>

	<div class='right'>
		<h2>done</h2>
		{#each todos.filter(t => t.done) as todo (todo.id)}
			<label
				class="done"
				in:receive="{{key: todo.id}}"
				out:send="{{key: todo.id}}"
			>
				<input type=checkbox checked on:change={()= > mark(todo, false)}>
				{todo.description}
				<button on:click="{() => remove(todo)}">remove</button>
			</label>
		{/each}
	</div>
</div>

<style>
	.board {
		display: grid;
		grid-template-columns: 1fr 1fr;
		grid-gap: 1em;
		max-width: 36em;
		margin: 0 auto;
	}

	.board > input {
		font-size: 1.4 em;
		grid-column: 1/3;
	}

	h2 {
		font-size: 2em;
		font-weight: 200;
		user-select: none;
		margin: 0 0 0.5 em 0;
	}

	label {
		position: relative;
		line-height: 1.2;
		padding: 0.5 em 2.5 em 0.5 em 2em;
		margin: 0 0 0.5 em 0;
		border-radius: 2px;
		user-select: none;
		border: 1px solid hsl(240.8%.70%);
		background-color:hsl(240.8%.93%);
		color: # 333;
	}

	input[type="checkbox"] {
		position: absolute;
		left: 0.5 em;
		top: 0.6 em;
		margin: 0;
	}

	.done {
		border: 1px solid hsl(240.8%.90%);
		background-color:hsl(240.8%.98%);
	}

	button {
		position: absolute;
		top: 0;
		right: 0.2 em;
		width: 2em;
		height: 100%;
		background: no-repeat 50% 50% url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23676778' D ='M12,2C17.53, 222,6.47,22 C22,17.53 17.53,22 12, 22c6.47,22 2,17.53,12C2,6.47 6.47,2 M17 12, 2, 7 h14. 5 l13. 5 or 6 H10.5 L9.5, 7 h7v9h17v7m9, 18 h15a1, 16 or 17 V10H8V17A1 1 0 0, 0, 1, 0, 0, 0 9, 18 '% 3 e % 3 c/Z path % 3 e % 3 c/SVG % 3 e");
		background-size: 1.4 em 1.4 em;
		border: none;
		opacity: 0;
		transition: opacity 0.2 s;
		text-indent: -9999px;
		cursor: pointer;
	}

	label:hover button {
		opacity: 1;
	}
</style>
Copy the code

The tutorial examples of transition animation are excellent and recommended to check out the official website. The transition