🤸‍♀️

Svelte5の各状況でのアニメーション実装方法メモ

2025/01/21に公開


Svelte5で色々作成している中で要素にアニメーションを付与したいと思った時、この状況だと何を使えばよかったっけ、となることがまぁまぁあるのでメモとして纏めました。記載以外のHTML/CSSネイティブ機能を使用する等、これ以外にも方法はあると思うので参考です。easing関数やtransitionの種類,引数については別の場所にリファレンスや記事等あるため記載せず参考文献にそれらへのリンクを載せています。

まとめ

分類 トリガー 指定方法
1要素表示 要素存在 transitionディレクティブ
1要素表示 CSS display CSStransition@starting-style
1要素表示 CSS visibility CSStransition
1要素表示 CSS 疑似クラス CSStransition
2要素表示 要素存在 transitionディレクティブのdurationdelayを同期
2要素表示 CSS display,visibility setTimeouttransition-durationと同期
移動 同親内の位置変更 CSStransition
移動 別親内へ要素移動 in,outディレクティブとcrossfade関数
移動 同親内の子要素増減時 Keyed each blockとanimateディレクティブとflip関数

1要素の表示切替アニメーション

作成,削除時

transitionディレクティブを使用する。

https://svelte.dev/playground/untitled#H4sIAAAAAAAACm2QTU7EMAxGr2IMixmpovu2U4kdF2CFWfTHQRGpEzUuAkW5O8qUCoFYRX6f_RInoQwLY4NPolYdz1ihsY4jNs8J9TOUrACsjs6HEO7jOzstbBwi_8cnL8qiERvs4rTaoLD4eXMMbpDXC6FGwp4EwC7BrwoJzDAzZDCrX4BwN9W6DhKtWi-ELUlX77Ke5ND-9TlWGL13cIG7qIPySdeNzy1JSc0mU7GBl8nZ6e10hlQ4HDM35WwLyr-uI-nGTdULlE-5EO4VIaRvVe691N6Yrt6j61C6teaqzkXZBfhZqCkL94_snO_q0JOk2ppMghUqfyg25d35JX8BRAqvkaMBAAA=

code
App.svelte
<script module lang="ts">
  import { fade } from "svelte/transition";
</script>
<script lang="ts">
  let bool = $state(false);

  function onclick() {
    bool = !bool;
  }
</script>

<button type="button" {onclick}>on/off</button>

{#if bool}
  <p transition:fade>Hello</p>
{/if}

作成時と削除時で異なるアニメーションを設定する場合はin,outディレクティブを使用する。

https://svelte.dev/playground/untitled#H4sIAAAAAAAACm2QsU7EMAyGX8UYhjuponuvV-k2XoCJMORaB0WkdtS4CBTl3VGudAAxRf5--7OcjGxnwg6fWb0GmrBB5wMl7F4y6lesWQXY7J2XGB_TBwWt7GoT_cdHYSXWhB32aVx8VJhlWgNBsPx2NqjJ4GAYwM9RFoUMKfiJGnB2IijgFpnB4CZsdbGcvHphgyfDfbs5B8O7_a82kMJVJMAZHpJapYOzIdHxZLjGbuWx6kB4DH58PxwhVw770F19TxWVX_sM99dVVRjq55wNbpVByD-qMgi34lzfbtFtKN97d1OXquwjeO5uB4Os2tWbhycKQfo2DoZz610xjA0qfSp2uqxUXss3zu4VVa0BAAA=

code
App.svelte
<script module lang="ts">
  import { slide, fade } from "svelte/transition";
</script>
<script lang="ts">
  let bool = $state(false);

  function onclick() {
    bool = !bool;
  }
</script>

<button type="button" {onclick}>on/off</button>

{#if bool}
  <p in:slide out:fade>Hello</p>
{/if}

CSSのdisplay切替時

CSSのtransitionプロパティ等を使用する。

https://svelte.dev/playground/untitled#H4sIAAAAAAAACm1Ry27CMBD8le2qB5CgUKknE1B76w_0hDk4YUMtzNqyF9ooyr9XJon64mR7dnZm1tsimxOhwjcWK472OMPaOkqoti1KE3ItAzgbmS8hPKQLOclYaRLdwivPQiwJFRapijYIOMOHtUZJGjeaARwJlN47WMN9EiM0qY1LNF1pzuX6zJVYz-C5crY6TqbQZhzGprt8rjLUaS4WvcsmNxflWcQz5Phrjf1LI7SDVLfxvPB1XSz6Ut8UYG_EzPc2BWeadZvlu80rOeeLReg5SRpH1_RhTCPRcLI56TxEHyhKo2AQmYEPprLSrP5xS3o3F-ujAuOc_8i2VSSh_8z9OZp8UfC0XJ7SODFA2P4KrFHimTTuxmRDQUHpfHUchIdACh4H4DmJiWL5ML8OB-03ZbnqjW67Xbd1y44901-35Y9FDX-IMxT6FFQ5drfrvgDOmf-riwIAAA==

code
App.svelte
<script lang="ts">
  let bool = $state(false);

  function onclick() {
    bool = !bool;
  }
</script>

<button type="button" {onclick}>on/off</button>

<p data-display={bool}>Hello</p>

<style>
  p {
    transition-property: display, opacity;
    transition-behavior: allow-discrete;
    transition-duration: 400ms;
  }
  p[data-display="true"] {
    display: block;
    opacity: 1;
    @starting-style { opacity: 0; }
  }
  p[data-display="false"] {
    display: none;
    opacity: 0;
  }
</style>

CSSのvisibility切替時

CSSのtransitionプロパティを使用する。

https://svelte.dev/playground/untitled#H4sIAAAAAAAACn2Qz26DMAzGX8WzdugkOjppJwpIu-0Fdmp6CGC2aKkTEVMNId59SgHt_05JPtu_74tHZH0izPCJxYilBhNsjaWA2WFEGXysRQGTtfPB-9twJitRq3Sg3_TasRBLwAzzUHfGC1jNz4VCCQpLxQCWBCrnLBRwHUQLbVptA93sFcdy23MtxjE4rq2pXzc3MEYd1qGreO6jNCnO09mljMN51Ys4hhi_UDi_FMK4oKbSceraNk_n0jzkodGit40J3uqhGCN-Kh_JWpenfu4JMli6pPdrGuk0BxOTbn3nPHUyZHA2wVTGGhkScF7XRob9j_am73S8ZHC_253C-hUAf_iSRKF0PSk8rpYf9MXJ0kJfvDK4-4d2WfMfuBfTNMTfabtPa142gAkKvQlmMdt0nN4BTbAtbUkCAAA=

code
App.svelte
<script lang="ts">
  let bool = $state(false);

  function onclick() {
    bool = !bool;
  }
</script>

<button type="button" {onclick}>on/off</button>

<p data-display={bool}>Hello</p>

<style>
  p {
    transition-property: visibility, opacity;
    transition-duration: 400ms;
  }
  p[data-display="true"] {
    visibility: visible;
    opacity: 1;
  }
  p[data-display="false"] {
    visibility: hidden;
    opacity: 0;
  }
</style>

CSSの疑似要素トグル時

CSSのtransitionプロパティを使用する。

https://svelte.dev/playground/untitled#H4sIAAAAAAAACm2OzQrCMAyAX6XkvD9BL3UMvPkCnqyH6TIsdGlps-Ece3fpZHhwp4QvH3yZgOoOQcKFWLPBBhJotcEA8joBjy7eIoBkNU_OZWFAw5Hd64Bb_GGJkTiAhNJVZzTGlrmrFCkqA48GK0VCODHFIURridOg3yjFzmN3_FL2NQXN2lLqvHXoeZQ_9V9qel_HRYp9UXRhEealI592QL9Vyw5rb1ZU5utvkADji0Gy73G-zR874wGAKAEAAA==

code
App.svelte
<p>Hello</p>

<style>
  p {
    font-size: 1rem;
    transition-property: font-size;
    transition-duration: 400ms;
  }
  p:hover {
    font-size: 1.5rem;
  }
</style>

2要素の表示切替アニメーション

作成,削除時

シーケンシャルな切替

transitionディレクティブのdurationdelayを同期するよう設定する。

  • {#if}を使用する場合

https://svelte.dev/playground/untitled#H4sIAAAAAAAACq2Qv07EMAzGX8UYhjupojcw9dpKTDwBE2HItQ6KSO2qcRGnKO-Ocn8G0I1Mcb7P_n2JE7KdCBt8ZfUaaMQKnQ8UsXlLqMe5eEXA6tr5PM-P8YuCFu1gI93SB2El1ogNtnFY_KwwybgGgmD5ozOo0WBvGMBPsywKCZwdCTK4RSYweCbVuliOXr2wwb3htj7DesNX7F9eIIWDSIAOHqJapY2zIdJ2b7jYbuWh4EB4CH743GwhFR2uQ3fl3Bcp_8oz3B5WVWEoW-kMnm8GIV1QuReuxbm2PlunoXTv3QmdC7KdwXNTftqlNK6LLU9p4Gm3q2CkYI-nOmeQVW-25dy_iIwwycKeP9p67g2nhkKk_w2wTmlhEb5E1N5lw1ih0rdio8tK-T3_AP0VC20_AgAA

code
App.svelte
<script module lang="ts">
  import { fade } from "svelte/transition";
</script>
<script lang="ts">
  let bool = $state(false);

  function onclick() {
    bool = !bool;
  }
</script>

<button type="button" {onclick}>on/off</button>

{#if bool}
  <p in:fade={{duration: 400, delay: 400}} out:fade={{duration: 400}}>Good morning</p>
{:else}
  <p in:fade={{duration: 400, delay: 400}} out:fade={{duration: 400}}>Good afternoon</p>
{/if}
  • {#key}を使用する場合

https://svelte.dev/playground/untitled#H4sIAAAAAAAACm2QsU7EMAyGX8UYhkOq6A1MvbaIiSdgIgy51j1FlzpV4iKqKO-O0tIBdFOS_7c__05E1iNhhe8sRiz1WOBgLAWsPiLKMmUvC1jsla_T9BS-yErWzjrQLb1zLMQSsMI6dN5MAqPrZ0tgNV8ahRIUtooBzDg5LxBh0D1BgsG7ERRupFK85mDEOFZ4UlyXG6xVvGP_8ywJnJ2z0MBDEC10GLQN9HhSnO1h5i7jwHFnTXc9PELMOuxNd_k8ZSn9mae4Ps8ijiH_SqNweymE-ItKrePSDUNdbtbaFO-vtKzslJn1BIarvGoTYz97nbNU8Hw8FtCT1ct6TwncLDfLUmq3uHHN-wIK35zrYXSeDV8UQrVLehDy7HLGbXY5tYpjeaUlKcYChb4FK_Ezpc_0A-eBmf0KAgAA

code
App.svelte
<script module lang="ts">
  import { fade } from "svelte/transition";
</script>
<script lang="ts">
  let bool = $state(false);

  function onclick() {
    bool = !bool;
  }
</script>

<button type="button" {onclick}>on/off</button>

{#key bool}
  <p in:fade={{duration: 400, delay: 400}} out:fade={{duration: 400}}>
    {bool ? "Good morning" : "Good afternoon"}
  </p>
{/key}

パラレルな切替

CSSで位置を揃える。

https://svelte.dev/playground/untitled#H4sIAAAAAAAACoVQQU7EMAz8ijEcQFpRzm13JU68gBPhkG0dFJHaUeOuWFX9O0qzBYGQOEWeyczYMyPbgbDGZ1avgXrcofOBEtYvM-o5Zi4DuNt-PsZ4n04UNGNHm-gvvBNWYk1YY5u60UeFQfopEATLb3uDmgweDAP4IcqoMIOzPcECbpQBDBanSkfLyasXNtgYbqtidjC82f72C6RwFAmwh5ukVunW2ZDorjGcaTdxl-1AuAu-e7-9gznjsImu8ttkaPmRZ7g9TqrCkFvZGyyTQZgvVstBuBLn2qpQRdT707rXfO3dmrGUuDbC93F1Pv7wJNLDICN7fmurWGQ1hUT_aKxTGlmEv1SVd-v6JT23pedAK9f7E8wQ5eICIwWr_kQNrClx6-P7hz0mCZNSU4hATmt4uEwqcRtKYZcg3KHSh2Kt40TL6_IJKexhzGoCAAA=

code
App.svelte
<script module lang="ts">
  import { fade } from "svelte/transition";
</script>
<script lang="ts">
  let bool = $state(false);

  function onclick() {
    bool = !bool;
  }
</script>

<button type="button" {onclick}>on/off</button>

<div>
  {#if bool}
    <p transition:fade>Good morning</p>
  {:else}
    <p transition:fade>Good afternoon</p>
  {/if}
</div>

<style>
  div { position: relative; }
  p {
    position: absolute;
    left: 0;
    top: 0;
  }
</style>

CSSのdisplay切替時

シーケンシャルな切替

setTimeouttransition-durationと切替タイミングを同期させる。

https://svelte.dev/playground/untitled#H4sIAAAAAAAACn1Sy5LTMBD8lUHFIalySEhx8qvgxA_Aab0H2RkH1SozKmmy4HL53yn5ERM2cLLc3eqeh3pF-oIqVd9JjFg8qUS1xmJQ6VOvpHORi4BKFuUX5z6EV7QSsVoHfIQ3TIIkQaUqD403TsBqOheVklCpsiIAiwI1s_0IBbwPogU3rbYBt9mf7HFlxV9HMtLtlRoxTMDUWNO8bLbQRxzAtLAZbW8I3GLejYdsgQPKN3NBvspms4WivAWOumMCnw6H7aweAG3Ae8tV-n_LNfreMn6GivL9NKEytpbXVxEmiKMvKjX9VQr6udGhZNpz2-b7iZouOThp0buTCc7qrujHsKH8ynyCC3sydM73rvyH8jgrdSvoiZkmbUV5kM7iuC23tC5eUzBx9jvn2aGXLoXZLgF2ujHSZW-0Nf7Qr4Z9Ctpa_hkLaDwKvlWerl7HQxpndQnZPCUA93RXeqXig6jU81LZTKRQW25eZuO5oBSWtX8Oor0YOu_G5qBfJYfstpMHaePjfBRHTPh32iFblzvOUCVK8JeoNBY9PA-_AQxG9mV5AwAA

code
App.svelte
<script lang="ts">
  let bool1 = $state(false);
  let bool2 = $state(true);

  function onclick() {
    if (bool1) {
      bool1 = !bool1;
      setTimeout(() => bool2 = !bool2, 400);
    } else {
      bool2 = !bool2;
      setTimeout(() => bool1 = !bool1, 400);
    }
  }
</script>

<button type="button" {onclick}>on/off</button>

<p data-display={bool1}>Good morning</p>
<p data-display={bool2}>Good afternoon</p>

<style>
  p {
    transition-property: display, opacity;
    transition-behavior: allow-discrete;
    transition-duration: 400ms;
  }
  p[data-display="true"] {
    display: block;
    opacity: 1;
    @starting-style { opacity: 0; }
  }
  p[data-display="false"] {
    display: none;
    opacity: 0;
  }
</style>

パラレルな切替

CSSで位置を揃える。

https://svelte.dev/playground/untitled#H4sIAAAAAAAACm1SwW7bMAz9FVbooQWS2gN2UpxgO-0Ldqp6kG06E6qQgkR7Mwz_-6DYXrMtJ4GPj3yPpCZF9oJKq-8kTjy2aqc65zEp_TopGUPOZUDtNubXEF7SgF4yVtuE9_CGSZAkKa2q1EQXBLyl89EoSUadDAF4FKiZPRzhMYkVfOqsT_h8MJTTXU-NOCZgarxr3p-eYco4bEUP-T1kaDZUFYvKKRdXdS_CBNn-0aglMgqmtdV8Yiq466piSS1FrRuuvqoArRW7b10K3o7HKevMp2_MLVw4kqNzVYT71Icbru0EIzHTwq6KRcBQlWT0eG3QugEmCJxcHlVDRG_FDXjIMwGEbeQPhq0T-17wsCQ8dqKhXCPhcBNES0vVPkQOGGXUsDrdAQfbOBn_59b4ww6OowbrPf_MszUR_wjeMNs-2sXU57K8pO0UAOH1r60YJbFHo962adaEhtpz8742Xg1p-LQCX5LYKI7O--u-YPqglOt-7qtdv9E9OWLCf9XKmx-0nkXtlOAvUTrbnt_m3zwWkx4kAwAA

code
App.svelte
<script lang="ts">
  let bool = $state(false);

  function onclick() {
    bool = !bool;
  }
</script>

<button type="button" {onclick}>on/off</button>

<div>
  <p data-display={bool}>Good morning</p>
  <p data-display={!bool}>Good afternoon</p>
</div>

<style>
  div { position: relative; }
  p {
    position: absolute;
    left: 0;
    top: 0;
    transition-property: display, opacity;
    transition-behavior: allow-discrete;
    transition-duration: 400ms;
  }
  p[data-display="true"] {
    display: block;
    opacity: 1;
    @starting-style { opacity: 0; }
  }
  p[data-display="false"] {
    display: none;
    opacity: 0;
  }
</style>

View Transition APIで切替

document.startViewTransition$state変数を変化させる。

https://svelte.dev/playground/untitled#H4sIAAAAAAAACr1TTWvcMBD9K5MhBxs2uyS5eS2XQlt6WAiUfhyiQmRbuxGrjIw09nYx_u_F8jqQ0q9AyElo5unNe49Rj6QeNGb4hdiw1TUucGusDpjd9sjHZuyNBVzMyLdNswydtjzWShX07-qVI9bEATPMQ-VNw2AV7YREDhILSQBWMzRqp0HAeWDFOpF4KTFdj81tSxUbR-Cosqbaf0iaDAJ7Q7s0gyQVRedMDf2IBfCaW0-QpCCKuQZQu6p90MTLwMrzV6MPn72iYEbe5ISdFTRrGKbJAEM8B0n5apJeSJKU16aDwEerRX9Xm9BYdczgvJ8IhICoHt6AxNK6ai8RMpBIjrTEYX03RNN52TI7gjFZIXG6SZxtiv7Rr8QrielQ3GtrXb6akM_muI4cB-dt_ZTj3k9nA5VVIQiJcZDE4uP7zebm7CxfNb8iIo3E4tvNp827GZGvatMV_5XP1cvmcxm9laraP7X2D0vPEHz92oL_mrCkPMqNj5bRG_TQGX244MfFvhh_YwZb59bjCgMsI-cfgaXy69Oun7hxgax_MGbsWz18H34CLr01mSMEAAA=

code
App.svelte
<script lang="ts">
  let page = $state("1");
  function onclickF(p: string): ()=>void {
    return () => {
      document.startViewTransition(() => { page = p; });
    };
  }
</script>

<div style={`display: ${page === "1" ? "block" : "none"};`}>
  <button type="button" onclick={onclickF("2")}>hello</button>
  <button type="button" onclick={onclickF("3")}>world</button>
  <hr>
  <p class="hello">HELLO!!</p>
  <p class="world">WORLD!!</p>
</div>
<div style={`display: ${page === "2" ? "block" : "none"};`}>
  <button type="button" onclick={onclickF("1")}>back</button>
  <p class="hello">HELLO!!</p>
</div>
<div style={`display: ${page === "3" ? "block" : "none"};`}>
  <button type="button" onclick={onclickF("1")}>back</button>
  <p class="world">WORLD!!</p>
</div>

<style>
  .hello { view-transition-name: foo; }
  .world { view-transition-name: bar; }
</style>

CSSのvisibility切替時

同上

移動アニメーション

親要素が変わらない移動

CSSのtransitionプロパティを使用する。

https://svelte.dev/playground/untitled#H4sIAAAAAAAACp2SwU7DMAyGX8VYHIa0MRBwybpKPASnhUPauluEl0SNO4aqvjvKulIYnLikym_7_78m6dCZPaHCFydWmCqcY22ZIqpNh_IRUi0JOB87n0O4jQdiSVphIv2ll94JOYmoMItlY4MAG7dda5SoMdcOgEmg8J5hDddRjNCsNhzpZqVdKtetK8V6B96VbMu32Q10SYdx6Cp9V0nqtcuWQ0qehrOiFfEOEv5a47DTCN3Zqs-9W_q6zpZDaRiq7OHElQWojJhFsl93ae3zHTH7bBnylDQ0apdF-WA6zVT2MNIFH23iVtAQG7EHWg2Fd1vJTsHj3V04nqUd2e1OfmqFbypqFETPtvqhLc4O9xe9i9KzbxQU3NJ4HgBhBNqbZmudgiliQjRF9NzKBeLTb8Kn_wFKY9xXFnP60X38xriZTlrj6fo1vo7gTLUouJ-SxYdv-z8cpGl_Gzw8XjhMwvBwzreIcxQ6Cqrk0r_2n3cv4XUbAwAA

code
App.svelte
<script lang="ts">
  let bool = $state(false);

  function onclick() {
    bool = !bool;
  }
</script>

<button type="button" {onclick}>on/off</button>

<div>
  <p data-bool={bool}>hello</p>
</div>

<style>
  div {
    position: relative;
    width: 400px;
    height: 400px;
    border: solid;
    border-width: 1px;
    border-color: blue;
  }
  p {
    margin: 0px;
    position: absolute;
    width: 50px;
    height: 50px;
    border: solid;
    border-width: 1px;
    transition: all 400ms;
  }
  p[data-bool="false"] {
    left: 10px;
    top: 10px;
  }
  p[data-bool="true"] {
    left: 340px;
    top: 340px;
  }
</style>

親要素が変わる移動

in,outディレクティブとcrossfadeを使用する。

https://svelte.dev/playground/untitled#H4sIAAAAAAAACq1Sy27bMBD8lc22BxtwqgRoLrRkoB_RU5iDLK5swtRSIFduBEH_XlAPxE57KnoiOLM7MxxwQC4bQoU_Waw4MrjD2jqKqF4HlL5NXAJwt07-aNtv8UpOEnYsI_0NrzwLsURUmMcq2Fag8aZzBK7kU6FRosaDZgDbtD4IDFAFH2NdGoIR6uAb0DjLZRJKjlasZ437tFN5jgKvkdjsIFBF9kpvUHxIbIZxu9ecZ7P3QfOa4rO9I4Gj9w4K-BqlFNpI6Gh7Y3OhHgrQeKF-sk9M3XGV8oDnytnqstnCkHBYtR7SOYmMdzE058dOxDOkbguN800jDIvUePCc-brOs5mal4y9TnGHL7aePMbZLm_BsloqKIYL9SP4TlSqZr4e8qydVzNbT2FmrXvJh_-imWqW3tHEGXtdS2lLYyyfFDw_te_7tahgKCiI3llzhz3-skbOCp4_zT5W3vmg4Og6Wohl8vvTh-6Z7Okst9j0rHbN0pThZFnBvyVZgJc__V5u7PJsrQF3KPQuqNKvGt_G3yr0uGRwAwAA

code
App.svelte
<script module lang="ts">
  import { crossfade } from "svelte/transition";
  const [send, receive] = crossfade({});
</script>
<script lang="ts">
  let bool = $state(true);
  const key = "key";

  function onclick() {
    bool = !bool;
  }
</script>

<button type="button" {onclick}>on/off</button>

<div>
  {#if bool}
    <p in:receive={key} out:send={key}></p>
  {/if}
</div>
<div>
  {#if !bool}
    <p in:receive={key} out:send={key}></p>
  {/if}
</div>

<style>
  div {
    padding: 10px;
    border: solid;
    border-width: 1px;
    border-color: blue;
    width: 400px;
    height: 400px;
  }
  p {
    margin: 0px;
    border: solid;
    border-width: 1px;
    width: 50px;
    height: 50px;
  }
</style>

子要素増減による移動

{#each items as item (item.key)}を用いて子要素にanimate:flipディレクティブを使用する。

https://svelte.dev/playground/untitled#H4sIAAAAAAAACsVSy27bMBD8lQXbgwzIlu3GPdCSgf5De4pyoMWVRZgiBXLl2BD07wX1SOIkh6CXHjmcnRnOsmNG1Mg4-2NIkUbJYlYqjZ7xx47RrQl3AWDxzPzVNCt_QU0BOwqPn-GFNYSGPOMs9YVTDUFtZasRtDCnLGfkc3bIDYCqG-sIOiic9b4UEqGH0tkacjbKJeSE8YqUNTnb382UWjUf6MKoWhBO3MIaT_Do0cgYHBaoLvgE2atd1PWLfW7SZMx5yM2c-H3UUeqMN7-BDL57EoTR4ybexj-eVrVooitkB-jAIbXOQHfGG4drv4d-sdjfC2zfCDzEu_jnVwSCRNmaIjQB1hRaFedoAV3AYZRdNa2voiHiyleqpGiyBvBIv1WNtqUoWgSfkfUysH0ZiGGzXo9j_V0xuUmPLZE1EH5GlrPxlDPopjj9wZrElmWajFfjUKuH_rpvKIpqKlD44X09BPNFP2ZMtQJl-LSmbCTYlnhY33Sc1svD7rOuk60ToRAOD-t13x8GUppoNVomwXJ4xRDikyjb_xQl_DO6aRwuWz2vUStPy-FiGUrmYKzBaYVS-UaLG4dS43XC7AVdqe0zh0pJiWaCGyGlMicOm3UzU4_WSXQcvNVK3mHLZyWp4rB5x10WVlvH4ajbOcPEfFi_6laoThW9xYYOtZrfVAt3UobDv0WZgN1Hw90bvzSZ22QxI7wS4-Ra7J_6vyU0r1vkBAAA

code
App.svelte
<script module lang="ts">
  import { crossfade } from "svelte/transition";
  import { flip } from "svelte/animate";
  const [send, receive] = crossfade({});
</script>
<script lang="ts">
  const keys1 = $state([1,2,3].map(x => { return {key: x}; }));
  const keys2 = $state([4,5,6].map(x => { return {key: x}; }));

  function onclick() {
    keys2.push(keys1.shift());
    setTimeout(() => keys1.push(keys2.shift()), 100);
  }
</script>

<button type="button" {onclick}>on/off</button>

<ul>
  {#each keys1 as {key} (key)}
    <li in:receive={key} out:send={key} animate:flip={{duration: 400}}>{key}</li>
  {/each}
</ul>
<ul>
  {#each keys2 as {key} (key)}
    <li in:receive={key} out:send={key} animate:flip={{duration: 400}}>{key}</li>
  {/each}
</ul>

<style>
  ul {
    list-style-type: none;
    display: flex;
    overflow: hidden;
    padding: 10px;
    border: solid;
    border-width: 1px;
    border-color: blue;
    width: 400px;
    height: 400px;
  }
  li {
    margin: 0px;
    border: solid;
    border-width: 1px;
    width: 50px;
    height: 50px;
  }
</style>

アニメーション中の値を動的に取得

svelte/motionTweenもしくはSpringを使用する。

雑記

SvelteのPlaygroundのデフォルトタイトル"Hello world"の左にあるメニューアイコンを開くと色々なサンプルが見れることに今更気が付きました。Recursive componentsなどできることが広がりそうなサンプルもあるので、また時間がある時に眺めたいと思いました。

参考文献

Discussion