过渡
过渡是由元素由于状态更改而进入或离开 DOM 触发的。
当一个块(例如 {#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>fades in and out</div>
{/if}
内置过渡
可以选择从 svelte/transition
模块导入内置过渡。
局部与全局
默认情况下,过渡是局部的。局部过渡仅在其所属的块创建或销毁时播放,而不是在父块创建或销毁时播放。
{#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}
过渡参数
过渡可以具有参数。
(双 {{curlies}}
不是特殊的语法;这是表达式标签内的对象文字。)
{#if visible}
<div transition:fade={{ duration: 2000 }}>fades in and out over two seconds</div>
{/if}
自定义过渡函数
transition = (node: HTMLElement
node: HTMLElement, params: any
params: any, options: {
direction: "in" | "out" | "both";
}
options: { direction: "in" | "out" | "both"
direction: 'in' | 'out' | 'both' }) => {
delay?: number,
duration?: number,
easing?: (t: number
t: number) => number,
css?: (t: number
t: number, u: number
u: number) => string,
tick?: (t: number
t: number, u: number
u: number) => void
}
过渡可以使用自定义函数。如果返回的对象具有 css
函数,则 Svelte 将为 web 动画 生成关键帧。
传递给 css
的 t
参数是在应用 easing
函数后介于 0
和 1
之间的值。进入过渡从 0
运行到 1
,离开过渡从 1
运行到 0
— 换句话说,1
是元素的自然状态,就像没有应用过渡一样。u
参数等于 1 - t
。
该函数在过渡开始之前重复调用,并使用不同的 t
和 u
参数。
应用
<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}
<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>whooshes in</div>
{/if}
自定义过渡函数还可以返回一个 tick
函数,该函数在过渡期间使用相同的 t
和 u
参数调用。
如果可以使用
css
而不是tick
,请这样做 — web 动画可以在主线程之外运行,从而防止较慢设备上的卡顿。
应用
<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}
<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(`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}
如果过渡返回一个函数而不是一个过渡对象,则该函数将在下一个微任务中被调用。这允许多个过渡进行协调,从而实现 交叉淡入淡出效果。
过渡函数还会接收第三个参数 options
,其中包含有关过渡的信息。
options
对象中的可用值是
direction
- 根据过渡类型,为in
、out
或both
之一
过渡事件
具有过渡的元素除了任何标准 DOM 事件外,还将分派以下事件
introstart
introend
outrostart
outroend
{#if visible}
<p
transition:fly={{ y: 200, duration: 2000 }}
onintrostart={() => (status = 'intro started')}
onoutrostart={() => (status = 'outro started')}
onintroend={() => (status = 'intro ended')}
onoutroend={() => (status = 'outro ended')}
>
Flies in and out
</p>
{/if}