You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte/documentation/docs/02-template-syntax/06-transitions-and-animatio...

11 KiB

title
Transitions & Animations
  • how to use (template syntax)
  • when to use
  • global vs local
  • easing & motion
  • mention imports
  • key block

Svelte provides different techniques and syntax for incorporating motion into your Svelte projects.

transition:fn

<!--- copy: false --->
transition:fn
<!--- copy: false --->
transition:fn={params}
<!--- copy: false --->
transition:fn|global
<!--- copy: false --->
transition:fn|global={params}
<!--- copy: false --->
transition:fn|local
<!--- copy: false --->
transition:fn|local={params}
/// copy: false
// @noErrors
transition = (node: HTMLElement, params: any, options: { direction: 'in' | 'out' | 'both' }) => {
	delay?: number,
	duration?: number,
	easing?: (t: number) => number,
	css?: (t: number, u: number) => string,
	tick?: (t: number, u: number) => void
}

A transition is triggered by an element entering or leaving the DOM as a result of a state change.

When a block is transitioning out, all elements inside the block, including those that do not have their own transitions, are kept in the DOM until every transition in the block has been completed.

The transition: directive indicates a bidirectional transition, which means it can be smoothly reversed while the transition is in progress.

{#if visible}
	<div transition:fade>fades in and out</div>
{/if}

Transitions are local by default. Local transitions only play when the block they belong to is created or destroyed, not when parent blocks are created or destroyed.

{#if x}
	{#if y}
		<p transition:fade>fades in and out only when y changes</p>

		<p transition:fade|global>fades in and out when x or y change</p>
	{/if}
{/if}

By default intro transitions will not play on first render. You can modify this behaviour by setting intro: true when you create a component and marking the transition as global.

Transition parameters

Transitions can have parameters.

(The double {{curlies}} aren't a special syntax; this is an object literal inside an expression tag.)

{#if visible}
	<div transition:fade={{ duration: 2000 }}>fades in and out over two seconds</div>
{/if}

Custom transition functions

Transitions can use custom functions. If the returned object has a css function, Svelte will create a CSS animation that plays on the element.

The t argument passed to css is a value between 0 and 1 after the easing function has been applied. In transitions run from 0 to 1, out transitions run from 1 to 0 — in other words, 1 is the element's natural state, as though no transition had been applied. The u argument is equal to 1 - t.

The function is called repeatedly before the transition begins, with different t and u arguments.

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

	/** @type {boolean} */
	export let visible;

	/**
	 * @param {HTMLElement} node
	 * @param {{ delay?: number, duration?: number, easing?: (t: number) => number }} params
	 */
	function whoosh(node, params) {
		const existingTransform = getComputedStyle(node).transform.replace('none', '');

		return {
			delay: params.delay || 0,
			duration: params.duration || 400,
			easing: params.easing || elasticOut,
			css: (t, u) => `transform: ${existingTransform} scale(${t})`
		};
	}
</script>

{#if visible}
	<div in:whoosh>whooshes in</div>
{/if}

A custom transition function can also return a tick function, which is called during the transition with the same t and u arguments.

If it's possible to use css instead of tick, do so — CSS animations can run off the main thread, preventing jank on slower devices.

<!--- file: App.svelte --->
<script>
	export let visible = false;

	/**
	 * @param {HTMLElement} node
	 * @param {{ speed?: number }} params
	 */
	function typewriter(node, { speed = 1 }) {
		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 * 0.01);

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

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

If a transition returns a function instead of a transition object, the function will be called in the next microtask. This allows multiple transitions to coordinate, making crossfade effects possible.

Transition functions also receive a third argument, options, which contains information about the transition.

Available values in the options object are:

  • direction - one of in, out, or both depending on the type of transition

Transition events

An element with transitions will dispatch the following events in addition to any standard DOM events:

  • introstart
  • introend
  • outrostart
  • outroend
{#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}

in:fn/out:fn

<!--- copy: false --->
in:fn
<!--- copy: false --->
in:fn={params}
<!--- copy: false --->
in:fn|global
<!--- copy: false --->
in:fn|global={params}
<!--- copy: false --->
in:fn|local
<!--- copy: false --->
in:fn|local={params}
<!--- copy: false --->
out:fn
<!--- copy: false --->
out:fn={params}
<!--- copy: false --->
out:fn|global
<!--- copy: false --->
out:fn|global={params}
<!--- copy: false --->
out:fn|local
<!--- copy: false --->
out:fn|local={params}

Similar to transition:, but only applies to elements entering (in:) or leaving (out:) the DOM.

Unlike with transition:, transitions applied with in: and out: are not bidirectional — an in transition will continue to 'play' alongside the out transition, rather than reversing, if the block is outroed while the transition is in progress. If an out transition is aborted, transitions will restart from scratch.

{#if visible}
	<div in:fly out:fade>flies in, fades out</div>
{/if}

animate:fn

<!--- copy: false --->
animate:name
<!--- copy: false --->
animate:name={params}
/// copy: false
// @noErrors
animation = (node: HTMLElement, { from: DOMRect, to: DOMRect } , params: any) => {
	delay?: number,
	duration?: number,
	easing?: (t: number) => number,
	css?: (t: number, u: number) => string,
	tick?: (t: number, u: number) => void
}
/// copy: false
// @noErrors
DOMRect {
	bottom: number,
	height: number,
	left: number,
	right: number,
	top: number,
	width: number,
	x: number,
	y: number
}

An animation is triggered when the contents of a keyed each block are re-ordered. Animations do not run when an element is added or removed, only when the index of an existing data item within the each block changes. Animate directives must be on an element that is an immediate child of a keyed each block.

Animations can be used with Svelte's built-in animation functions or custom animation functions.

<!-- When `list` is reordered the animation will run-->
{#each list as item, index (item)}
	<li animate:flip>{item}</li>
{/each}

Animation Parameters

As with actions and transitions, animations can have parameters.

(The double {{curlies}} aren't a special syntax; this is an object literal inside an expression tag.)

{#each list as item, index (item)}
	<li animate:flip={{ delay: 500 }}>{item}</li>
{/each}

Custom animation functions

Animations can use custom functions that provide the node, an animation object and any parameters as arguments. The animation parameter is an object containing from and to properties each containing a DOMRect describing the geometry of the element in its start and end positions. The from property is the DOMRect of the element in its starting position, and the to property is the DOMRect of the element in its final position after the list has been reordered and the DOM updated.

If the returned object has a css method, Svelte will create a CSS animation that plays on the element.

The t argument passed to css is a value that goes from 0 and 1 after the easing function has been applied. The u argument is equal to 1 - t.

The function is called repeatedly before the animation begins, with different t and u arguments.

<script>
	import { cubicOut } from 'svelte/easing';

	/**
	 * @param {HTMLElement} node
	 * @param {{ from: DOMRect; to: DOMRect }} states
	 * @param {any} params
	 */
	function whizz(node, { from, to }, params) {
		const dx = from.left - to.left;
		const dy = from.top - to.top;

		const d = Math.sqrt(dx * dx + dy * dy);

		return {
			delay: 0,
			duration: Math.sqrt(d) * 120,
			easing: cubicOut,
			css: (t, u) => `transform: translate(${u * dx}px, ${u * dy}px) rotate(${t * 360}deg);`
		};
	}
</script>

{#each list as item, index (item)}
	<div animate:whizz>{item}</div>
{/each}

A custom animation function can also return a tick function, which is called during the animation with the same t and u arguments.

If it's possible to use css instead of tick, do so — CSS animations can run off the main thread, preventing jank on slower devices.

<script>
	import { cubicOut } from 'svelte/easing';

	/**
	 * @param {HTMLElement} node
	 * @param {{ from: DOMRect; to: DOMRect }} states
	 * @param {any} params
	 */
	function whizz(node, { from, to }, params) {
		const dx = from.left - to.left;
		const dy = from.top - to.top;

		const d = Math.sqrt(dx * dx + dy * dy);

		return {
			delay: 0,
			duration: Math.sqrt(d) * 120,
			easing: cubicOut,
			tick: (t, u) => Object.assign(node.style, { color: t > 0.5 ? 'Pink' : 'Blue' })
		};
	}
</script>

{#each list as item, index (item)}
	<div animate:whizz>{item}</div>
{/each}

<!--- copy: false  --->
{#key expression}...{/key}

Key blocks destroy and recreate their contents when the value of an expression changes.

This is useful if you want an element to play its transition whenever a value changes.

{#key value}
	<div transition:fade>{value}</div>
{/key}

When used around components, this will cause them to be reinstantiated and reinitialised.

{#key value}
	<Component />
{/key}