in:/out: 过渡
本教程共 50 篇 · 第 24 篇 · 更新于 2026-08-05 · 约 6 分钟阅读
本节目标:学会用 in: 和 out: 指令为元素设置不同的进入和离开动画,理解它与 transition: 的区别,掌握 local 修饰符和全局过渡的用法。
in: 和 out: 的区别
上一章学的 transition: 是双向的——进入和离开用同一种动画,而且可以中途逆转。in: 和 out: 则把进入和离开拆开了,可以分别设置不同的效果。
打个比方,transition: 像自动门——进出都是同一扇门缓缓打开。in: 和 out: 像旋转门配滑梯——进门用旋转门,出门直接滑走,两套不同的效果。
<script>
import { fade, fly } from 'svelte/transition';
let visible = $state(false);
</script>
<label>
<input type="checkbox" bind:checked={visible} />
显示/隐藏
</label>
{#if visible}
<!-- 进入时飞入,离开时淡出 -->
<div in:fly={{ y: 200 }} out:fade>
飞入,淡出
</div>
{/if}
transition: vs in:/out:
关键区别在于”中途逆转”的行为。
当你用 transition: 时,如果元素正在进入过程中你突然让它离开,动画会逆转方向——原本淡入到一半,会从当前位置淡出。
用 in: 和 out: 时则不会逆转。如果进入动画还没播完就被打断,离开动画会从头开始播放。
| 对比项 | transition: | in: + out: |
|---|---|---|
| 方向 | 双向,可逆转 | 单向,不可逆转 |
| 灵活性 | 进出相同效果 | 进出可不同 |
| 中途打断 | 逆转动画 | 重新开始 |
搭配不同效果
in: 和 out: 最大的价值就是进出可以用完全不同的动画。
<script>
import { fly, scale, slide } from 'svelte/transition';
let visible = $state(false);
</script>
<button onclick={() => visible = !visible}>切换</button>
{#if visible}
<div
in:fly={{ x: -100, duration: 500 }}
out:scale={{ start: 0.5, duration: 300 }}
style="padding: 20px; background: #eee;"
>
从左边飞入,缩放着离开
</div>
{/if}
只用 in: 或只用 out:
你可以只写 in: 不写 out:,或者反过来。没写的那一方就没有过渡效果。
<script>
import { fade } from 'svelte/transition';
let visible = $state(false);
</script>
{#if visible}
<!-- 只有进入动画,离开时瞬间消失 -->
<div in:fade={{ duration: 1000 }}>
只有淡入,没有淡出
</div>
{/if}
自定义过渡函数签名
无论是 transition:、in: 还是 out:,自定义过渡函数的签名是一样的。
type 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;
};
第三个参数 options 包含 direction 字段,告诉你当前是进入(in)、离开(out)还是双向(both)。你可以根据方向返回不同的配置。
<script>
function smartTransition(node, params, { direction }) {
if (direction === 'in') {
return {
duration: 800,
css: (t) => `opacity: ${t}; transform: translateY(${(1 - t) * 50}px)`
};
} else {
return {
duration: 300,
css: (t) => `opacity: ${t}; transform: scale(${t})`
};
}
}
let visible = $state(false);
</script>
{#if visible}
<div in:smartTransition out:smartTransition>
进入和离开用不同效果
</div>
{/if}
Note虽然
in:和out:可以分别指定同一个函数,但如果函数内部已经根据direction区分了行为,用transition:smartTransition更简洁。
local 修饰符
过渡默认是”本地”的。什么意思呢?看这个嵌套结构:
{#if x}
{#if y}
<p transition:fade>只在 y 变化时播放</p>
{/if}
{/if}
当 x 从 true 变成 false 时,内层 {#if y} 整体被移除。但 <p> 的过渡不会播放,因为它只在自己所属的 y 块创建/销毁时播放。
如果你希望父块销毁时也播放过渡,加 |global 修饰符:
{#if x}
{#if y}
<!-- 本地:只在 y 变化时播放 -->
<p transition:fade>本地过渡</p>
<!-- 全局:x 或 y 变化时都播放 -->
<p transition:fade|global>全局过渡</p>
{/if}
{/if}
Tip
local是默认行为,不需要显式写。大多数场景用默认的本地过渡就够了。只有当嵌套的{#if}导致父块销毁时子元素没有过渡,才需要考虑|global。
过渡事件
in: 和 out: 同样会触发过渡事件,跟 transition: 一致。
onintrostart:进入过渡开始onintroend:进入过渡结束onoutrostart:离开过渡开始onoutroend:离开过渡结束
<script>
import { fly, fade } from 'svelte/transition';
let visible = $state(false);
let status = $state('');
</script>
{#if visible}
<div
in:fly={{ y: 100 }}
out:fade
onintrostart={() => status = '进入开始'}
onintroend={() => status = '进入完成'}
onoutrostart={() => status = '离开开始'}
onoutroend={() => status = '离开完成'}
>
{status}
</div>
{/if}
<button onclick={() => visible = !visible}>切换</button>
实战:交错过渡
利用 delay 参数可以实现交错出场效果——多个元素依次出现。
<script>
import { fly } from 'svelte/transition';
let items = ['苹果', '香蕉', '橙子', '葡萄', '西瓜'];
let visible = $state(false);
</script>
<button onclick={() => visible = !visible}>切换</button>
{#if visible}
{#each items as item, i}
<div
in:fly={{ y: 20, delay: i * 100, duration: 400 }}
out:fly={{ y: -20, delay: (items.length - i - 1) * 100, duration: 400 }}
style="padding: 8px; margin: 4px 0; background: #f0f0f0;"
>
{item}
</div>
{/each}
{/if}
进入时每个元素延迟 i * 100 毫秒出场,离开时反过来——最后一个先消失,制造出波浪般的效果。
本节回顾
in:和out:分别控制进入和离开动画,可以设置不同效果- 与
transition:不同,in:/out:不会中途逆转,被打断后从头开始 - 自定义过渡函数的第三个参数包含
direction,可据此返回不同配置 - 默认是本地过渡,加
|global修饰符可让父块变化时也触发 - 利用
delay参数可以实现交错出场效果 - 过渡事件(
introstart/introend/outrostart/outroend)同样适用