Skip to main content

transition:

当元素由于状态变化而进入或离开 DOM 时,会触发*transition(过渡)*效果。

当一个块(比如 {#if ...} 块)正在过渡退出时,其中的所有元素,包括那些没有自己的过渡效果的元素,都会保留在 DOM 中,直到该块中的所有过渡都完成。

transition: 指令表示 双向 过渡,这意味着在过渡进行过程中可以平滑地反向执行。

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

	let visible = $state(false);
</script>

<button onclick={() => visible = !visible}>toggle</button>

{#if visible}
	<div transition:fade>淡入淡出效果</div>
{/if}

内置过渡

可以从 svelte/transition 模块导入一系列内置的过渡效果。

局部 vs 全局

过渡默认是局部的。局部过渡只在它们所属的块被创建或销毁时播放,而 不会 在父块被创建或销毁时播放。

{#if x}
	{#if y}
		<p transition:fade>仅在 y 变化时淡入淡出</p>

		<p transition:fade|global>在 x 或 y 变化时淡入淡出</p>
	{/if}
{/if}

过渡参数

过渡可以有参数。

(双 {{大括号}} 不是特殊语法;这是表达式标签内的对象字面量。)

{#if visible}
	<div transition:fade={{ duration: 2000 }}>在两秒内淡入淡出</div>
{/if}

自定义过渡函数

transition = (node: HTMLElementnode: HTMLElement, params: anyparams: any, 
options: {
    direction: "in" | "out" | "both";
}
options
: { direction: "in" | "out" | "both"direction: 'in' | 'out' | 'both' }) => {
delay?: number, duration?: number, easing?: (t: numbert: number) => number, css?: (t: numbert: number, u: numberu: number) => string, tick?: (t: numbert: number, u: numberu: number) => void }

过渡可以使用自定义函数。如果返回的对象有 css 函数,Svelte 将为 Web 动画 生成关键帧。

传递给 css 的参数 t 是一个介于 01 之间的值,这个值已经经过 easing 函数处理。入场 过渡从 01退场 过渡从 10 —— 换句话说,1 是元素的自然状态,就像没有应用过渡一样。参数 u 等于 1 - t

该函数会在过渡开始 之前 被反复调用,每次调用时都会传入不同的 tu 参数。

App
<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>呼啸而入</div>
{/if}
<script lang="ts">
	import { elasticOut } from 'svelte/easing';

	export let visible: boolean;

	function whoosh(node: HTMLElement, params: { delay?: number, duration?: number, easing?: (t: number) => number }) {
		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>呼啸而入</div>
{/if}

自定义过渡函数还可以返回一个 tick 函数,该函数会在过渡 过程中 被调用,同样会传入 tu 参数。

如果可以使用 css 而不是 tick,那就尽可能使用 css —— Web 动画可以在主线程之外运行,防止在性能较低的设备上出现卡顿。

App
<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(`此过渡效果只适用于具有单个文本节点子元素的元素`);
		}

		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 }}>敏捷的棕色狐狸跳过了懒狗</p>
{/if}
<script lang="ts">
	export let visible = false;

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

		if (!valid) {
			throw new Error(`此过渡效果只适用于具有单个文本节点子元素的元素`);
		}

		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 }}>敏捷的棕色狐狸跳过了懒狗</p>
{/if}

如果过渡返回一个函数而不是过渡对象,该函数将在下一个微任务中被调用。这允许多个过渡进行协调,使 交叉淡入淡出效果 成为可能。

过渡函数还会接收第三个参数 options,其中包含有关过渡的信息。

options 对象中可用的值为:

  • direction - 过渡的类型,可以是 inoutboth 之一

过渡事件

具有过渡效果的元素除了标准的 DOM 事件外,还会触发以下事件:

  • introstart(入场开始)
  • introend(入场结束)
  • outrostart(退场开始)
  • outroend(退场结束)
{#if visible}
	<p
		transition:fly={{ y: 200, duration: 2000 }}
		onintrostart={() => (status = '入场开始')}
		onoutrostart={() => (status = '退场开始')}
		onintroend={() => (status = '入场结束')}
		onoutroend={() => (status = '退场结束')}
	>
		飞入飞出
	</p>
{/if}

在 GitHub 编辑此页面

上一页 下一页