前言
Svelte,一个非常“有趣”、用起来“很爽”的前端框架。从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1:
Svelte 以其独特的编译时优化机制著称,具有轻量级、高性能、易上手等特性,非常适合构建轻量级 Web 项目,也是我做个人项目的首选技术栈。
目前 Svelte 基于 Svelte 5 发布了最新的官方文档,但却缺少对应的中文文档。为了帮助大家学习 Svelte,为爱发电翻译了官方文档。
我同时搭建了 Svelte 最新的中文文档站点:https://svelte.yayujs.com ,如果需要辅助学习,也可以入手我的小册《Svelte 开发指南》,语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!
虽说是翻译,但个人并不喜欢严格遵守原文,为了保证中文阅读流畅,会删减部分语句,对难懂的部分也会另做补充解释,希望能给大家带来一个好的中文学习体验。
欢迎围观我的“网页版朋友圈”、加入“低调务实优秀中国好青年”前端社群,分享技术,带你成长。
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}
   复制代码
 自定义过渡函数
 /// copy: false// @noErrorstransition = (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}
   复制代码
 
过渡可以使用自定义函数。如果返回的对象有 css 函数,Svelte 将为 Web 动画 生成关键帧。
传递给 css 的参数 t 是一个介于 0 和 1 之间的值,这个值已经经过 easing 函数处理。入场 过渡从 0 到 1,退场 过渡从 1 到 0 —— 换句话说,1 是元素的自然状态,就像没有应用过渡一样。参数 u 等于 1 - t。
该函数会在过渡开始 之前 被反复调用,每次调用时都会传入不同的 t 和 u 参数。
 <!--- file: App.svelte ---><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}
   复制代码
 
自定义过渡函数还可以返回一个 tick 函数,该函数会在过渡 过程中 被调用,同样会传入 t 和 u 参数。
[!NOTE] 如果可以使用 css 而不是 tick,那就尽可能使用 css —— Web 动画可以在主线程之外运行,防止在性能较低的设备上出现卡顿。
 <!--- 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(`此过渡效果只适用于具有单个文本节点子元素的元素`);    }
    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 对象中可用的值为:
过渡事件
具有过渡效果的元素除了标准的 DOM 事件外,还会触发以下事件:
- introstart(入场开始)
 
- introend(入场结束)
 
- outrostart(退场开始)
 
- outroend(退场结束)
 
 {#if visible}  <p    transition:fly={{ y: 200, duration: 2000 }}    onintrostart={() => (status = '入场开始')}    onoutrostart={() => (status = '退场开始')}    onintroend={() => (status = '入场结束')}    onoutroend={() => (status = '退场结束')}  >    飞入飞出  </p>{/if}
   复制代码
 in: 和 out:
in: 和 out: 指令与 transition: 相同,只是产生的过渡不是双向的 — 如果在过渡进行过程中块被移出,in 过渡将继续与 out 过渡一起"播放",而不是反向运行。如果 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}
   复制代码
 animate:
当带键的 each 块的内容重新排序时,会触发动画。动画不会在元素添加或删除时运行,只会在 each 块内现有数据项的索引发生变化时运行。animate 指令必须在带键的 each 块的直接子元素上使用。
动画可以与 Svelte 的内置动画函数或自定义动画函数一起使用。
 <!-- 当 `list` 重新排序时动画将会运行 -->{#each list as item, index (item)}  <li animate:flip>{item}</li>{/each}
   复制代码
 动画参数
与 actions 和过渡一样,动画也可以有参数。
(双重 {{大括号}} 不是特殊语法;这是表达式标签内的对象字面量。)
 {#each list as item, index (item)}  <li animate:flip={{ delay: 500 }}>{item}</li>{/each}
   复制代码
 自定义动画函数
 /// copy: false// @noErrorsanimation = (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}
   复制代码
 
动画可以使用自定义函数,这些函数接收 node、animation 对象和任何 parameters 作为参数。
animation 参数是一个包含 from 和 to 属性的对象,每个属性都包含一个 DOMRect,描述元素在其 开始 和 结束 位置的几何形状。
from 属性是元素在其起始位置的 DOMRect,而 to 属性是列表重新排序且 DOM 更新后元素在其最终位置的 DOMRect。
如果返回的对象有 css 方法,Svelte 将创建一个在元素上播放的web 动画。
传递给 css 的 t 参数是一个在应用 easing 函数后从 0 到 1 的值。u 参数等于 1 - t。
该函数会在动画开始之前被反复调用,并传入不同的 t 和 u 参数。
 <!--- file: App.svelte ---><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}
   复制代码
 
自定义动画函数还可以返回一个 tick 函数,该函数会在动画期间被调用,并带有相同的 t 和 u 参数。
[!NOTE] 如果可以使用 css 而不是 tick,那就尽可能使用 css — web 动画可以在主线程之外运行,防止在较慢的设备上出现卡顿。
 <!--- file: App.svelte ---><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}
   复制代码
 Svelte 中文文档
本篇已收录在掘金专栏 《Svelte 中文文档》,该系列预计 40 篇。
系统学习 Svelte,欢迎入手小册《Svelte 开发指南》。语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!
此外我还写过 JavaScript 系列、TypeScript 系列、React 系列、Next.js 系列、冴羽答读者问等 14 个系列文章, 全系列文章目录:https://github.com/mqyqingfeng/Blog
通过文字建立交流本身就是一种缘分,欢迎围观我的“网页版朋友圈”、加入“低调务实优秀中国好青年”前端社群,分享技术,带你成长。
评论