2020-04-25

Svelte Tutorial 14: Component Composition (English & Indonesia)

English

Slots


Just like elements can have children...

<div>
  <p>I'm a child of the div</p>
</div>

...so can components. Before a component can accept children, though, it needs to know where to put them. We do this with the <slot> element. Put this inside Box.svelte:

<style>
  .box {
    width: 300px;
    border: 1px solid #aaa;
    border-radius: 2px;
    box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
    padding: 1em;
    margin: 0 0 1em 0;
  }
</style>

<div class="box">
  <slot></slot>
</div>

You can now put things in the Box component in main app:

<script>
  import Box from './Box.svelte';
</script>

<Box>
  <h2>Hello!</h2>
  <p>This is a box. It can contain anything!</p>
</Box>

Slot Fallbacks


A component can specify fallbacks for any slots that are left empty, by putting content inside the <slot> element:

<div class="box">
  <slot>
    <em>no content was provided</em>
  </slot>
</div>

We can now create instances of <Box> without any children, you can try this from previous example:

<Box>
  <h2>Hello!</h2>
  <p>This is a box. It can contain anything.</p>
</Box>

<Box/>

Named Slots


The previous example contained a default slot, which renders the direct children of a component. Sometimes you will need more control over placement, such as with this <ContactCard>. In those cases, we can use named slots.

In ContactCard.svelte, add a name attribute to each slot:

<style>
  .contact-card {
    width: 300px;
    border: 1px solid #aaa;
    border-radius: 2px;
    box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
    padding: 1em;
  }

  h2 {
    padding: 0 0 0.2em 0;
    margin: 0 0 1em 0;
    border-bottom: 1px solid #ff3e00
  }

  .address, .email {
    padding: 0 0 0 1.5em;
    background:  0 0 no-repeat;
    background-size: 20px 20px;
    margin: 0 0 0.5em 0;
    line-height: 1.2;
  }

  .address { background-image: url(tutorial/icons/map-marker.svg) }
  .email   { background-image: url(tutorial/icons/email.svg) }
  .missing { color: #999 }
</style>

<article class="contact-card">
  <h2>
    <slot name="name">
      <span class="missing">Unknown name</span>
    </slot>
  </h2>

  <div class="address">
    <slot name="address">
      <span class="missing">Unknown address</span>
    </slot>
  </div>

  <div class="email">
    <slot name="email">
      <span class="missing">Unknown email</span>
    </slot>
  </div>
</article>

Then, add elements with corresponding slot="..." attributes inside the <ContactCard> component:

<script>
  import ContactCard from './ContactCard.svelte';
</script>

<ContactCard>
  <span slot="name"> P. Sherman </span>
  <span slot="address"> 42 Wallaby Way, Sydney </span>
</ContactCard>

Slot Props


In this app, we have a <Hoverable> component that tracks whether the mouse is currently over it. It needs to pass that data back to the parent component, so that we can update the slotted contents.

For this, we use slot props. In Hoverable.svelte, pass the hovering value into the slot by adding hovering={hovering} in slot.

<script> // this is Hoverable.svelte
  let hovering;
  function enter() { hovering = true; }
  function leave() { hovering = false; }
</script>

<div on:mouseenter={enter} on:mouseleave={leave}>
  <slot hovering={hovering}></slot>
</div>

Then, to expose hovering to the contents of the<Hoverable> component, we use the let directive:

<Hoverable let:hovering={hovering}>
  <div class:active={hovering}>
    {#if hovering} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

You can rename the variable, if you want — let's call it active in the parent component. You can also have as many of these components as you like, and the slotted props will remain local to the component where they're declared.

<script> // this is App.svelte
  import Hoverable from './Hoverable.svelte';
</script>

<style>
  div {
    padding: 1em;
    margin: 0 0 1em 0;
    background-color: #eee;
  }

  .active {
    background-color: #ff3e00;
    color: white;
  }
</style>

<Hoverable let:hovering={active}>
  <div class:active>
    {#if active} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

<Hoverable let:hovering={active}>

  <div class:active>
    {#if active} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

<Hoverable let:hovering={active}>

  <div class:active>
    {#if active} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

Named slots can also have props; use the let directive on an element with a slot="..." attribute, instead of on the component itself.




Indonesia

Slots


Sama halnya seperti elemen bisa mempunyai child...

<div>
  <p>I'm a child of the div</p>
</div>

...begitu juga komponen juga bisa. Sebelum komponen bisa menerima child, komponen perlu diberi tahu dulu di mana posisinya akan diletakkan. Kita bisa melakukan ini dengan elemen <slot>. Berikut contoh Box.svelte:

<style>
  .box {
    width: 300px;
    border: 1px solid #aaa;
    border-radius: 2px;
    box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
    padding: 1em;
    margin: 0 0 1em 0;
  }
</style>

<div class="box">
  <slot></slot>
</div>

Sekarang kita bisa meletakkan sesuatu pada komponen Box pada main app:

<script>
  import Box from './Box.svelte';
</script>

<Box>
  <h2>Hello!</h2>
  <p>This is a box. It can contain anything!</p>
</Box>

Slot Fallbacks


Sebuah komponen bisa menentukan fallback untuk slot apapun yang dibiarkan kosong, dengan meletakkan isian pada elemen <slot>:

<div class="box">
  <slot>
    <em>no content was provided</em>
  </slot>
</div>

Sekarang kita bisa membuat komponen <Box> tanpa diisi child, disini kita akan coba dari contoh sebelumnya:

<Box>
  <h2>Hello!</h2>
  <p>This is a box. It can contain anything.</p>
</Box>

<Box/>

Named Slots


Contoh sebelumnya kita melihat slot default, yang akan me-render child langsung dari komponen. Tapi terkadang kita akan membutuhkan pengaturan lebih dalam peletakan, contoh pada <ContactCard> berikut. Pada kasus seperti ini kita bisa gunakan named slot (=slot yang dinamai).

Pada ContactCard.svelte, tambahkan atribut name pada setiap slot:

<style>
  .contact-card {
    width: 300px;
    border: 1px solid #aaa;
    border-radius: 2px;
    box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
    padding: 1em;
  }

  h2 {
    padding: 0 0 0.2em 0;
    margin: 0 0 1em 0;
    border-bottom: 1px solid #ff3e00
  }

  .address, .email {
    padding: 0 0 0 1.5em;
    background:  0 0 no-repeat;
    background-size: 20px 20px;
    margin: 0 0 0.5em 0;
    line-height: 1.2;
  }

  .address { background-image: url(tutorial/icons/map-marker.svg) }
  .email   { background-image: url(tutorial/icons/email.svg) }
  .missing { color: #999 }
</style>

<article class="contact-card">
  <h2>
    <slot name="name">
      <span class="missing">Unknown name</span>
    </slot>
  </h2>

  <div class="address">
    <slot name="address">
      <span class="missing">Unknown address</span>
    </slot>
  </div>

  <div class="email">
    <slot name="email">
      <span class="missing">Unknown email</span>
    </slot>
  </div>
</article>

Kemudian tambahkan elemen dengan atribut slot="..." yang sesuai di dalam komponen <ContactCard>:

<script>
  import ContactCard from './ContactCard.svelte';
</script>

<ContactCard>
  <span slot="name"> P. Sherman </span>
  <span slot="address"> 42 Wallaby Way, Sydney </span>
</ContactCard>

Slot Props


Pada app berikut, kita mempunyai komponen <Hoverable> yang akan mendeteksi jika pointer mouse berada di atasnya. komponen ini perlu mem pass data ke komponen parent nya, supaya kita bisa meng update isi dari slot.

Di sini kita bisa menggunakan slot props. Di dalam Hoverable.svelte, pass nilai hovering ke dalam slot dengan menambahkan hovering={hovering} pada <slot>.

<script> // this is Hoverable.svelte
  let hovering;
  function enter() { hovering = true; }
  function leave() { hovering = false; }
</script>

<div on:mouseenter={enter} on:mouseleave={leave}>
  <slot hovering={hovering}></slot>
</div>

Kemudian, untuk mengekspos hovering pada isi dari komponen <Hoverable>, kita bisa menggunakan direktiv let:

<Hoverable let:hovering={hovering}>
  <div class:active={hovering}>
    {#if hovering} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

Kita juga bisa mengganti nama variabel jika mau — misal diganti jadi active pada komponen parent. Kita juga bisa mempunyai lebih dari satu komponen jika mau, dan slot prop akan tetap bersifat lokal pada komponen tempat dideklarasikan.

<script> // this is App.svelte
  import Hoverable from './Hoverable.svelte';
</script>

<style>
  div {
    padding: 1em;
    margin: 0 0 1em 0;
    background-color: #eee;
  }

  .active {
    background-color: #ff3e00;
    color: white;
  }
</style>

<Hoverable let:hovering={active}>
  <div class:active>
    {#if active} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

<Hoverable let:hovering={active}>

  <div class:active>
    {#if active} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

<Hoverable let:hovering={active}>

  <div class:active>
    {#if active} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

Named slot juga bisa memiliki properti, gunakan direktiv let pada elemen dengan atribut slot="...", dan bukan pada komponennya.

Svelte Tutorial 13: Classes (English & Indonesia)

English

The Class Directive


Like any other attribute, you can specify classes with a JavaScript attribute, seen here:

<button
  class="{current === 'foo' ? 'active' : ''}"
  on:click="{() => current = 'foo'}"
>foo</button>

This is such a common pattern in UI development that Svelte includes a special directive to simplify it:

<button
class:active="{current === 'foo'}"
on:click="{() => current = 'foo'}"
>foo</button>

The active class is added to the element whenever the value of the expression is truthy, and removed when it's falsy. So as you can see in the example below, once class is set to active, the button will change color according to what css is set to, in this case, to color orange.

<script>
  let current = 'foo';
</script>

<style>
  button {
    display: block;
  }

  .active {
    background-color: #ff3e00;
    color: white;
  }
</style>

<button
  class:active="{current === 'foo'}"
  on:click="{() => current = 'foo'}"
>foo</button>

<button
  class:active="{current === 'bar'}"
  on:click="{() => current = 'bar'}"
>bar</button>

<button
  class:active="{current === 'baz'}"
  on:click="{() => current = 'baz'}"
>baz</button>

Shorthand Class Directive


Often, the name of the class will be the same as the name of the value it depends on:

<div class:big={big}>
<!-- ... -->
</div>

In those cases we can use a shorthand form:

<div class:big>
<!-- ... -->
</div>

Here is full example when variable big is set to true as the checkbox is checked, the class of div also change to big, so the font also get bigger as it's set in the css.

<script>
  let big = false;
</script>

<style>
  .big {
    font-size: 4em;
  }
</style>

<label>
  <input type=checkbox bind:checked={big}> big
</label>

<div class:big>
  some {big ? 'big' : 'small'} text
</div>




Indonesia

The Class Directive


Seperti atribut yang lain, kita juga bisa menentukan class dengan atribut JavaScript, seperti berikut:

<button
  class="{current === 'foo' ? 'active' : ''}"
  on:click="{() => current = 'foo'}"
>foo</button>

Karena begitu seringnya digunakan dalam pembuatan UI, maka Svelte memasukkan direktiv spesial untuk membuat proses ini lebih mudah:

<button
class:active="{current === 'foo'}"
on:click="{() => current = 'foo'}"
>foo</button>

Class active akan ditambahkan pada elemen kapanpun hasil dari ekspresi adalah benar, dan dihilangkan jika hasilnya salah. Jadi seperti yang bisa dilihat pada contoh di atas, begitu class sudah diset menjadi active, tombol akan berubah warna sesuai dengan yang sudah diatur di dalam css, dalam hal ini menjadi warna oranye.

<script>
  let current = 'foo';
</script>

<style>
  button {
    display: block;
  }

  .active {
    background-color: #ff3e00;
    color: white;
  }
</style>

<button
  class:active="{current === 'foo'}"
  on:click="{() => current = 'foo'}"
>foo</button>

<button
  class:active="{current === 'bar'}"
  on:click="{() => current = 'bar'}"
>bar</button>

<button
  class:active="{current === 'baz'}"
  on:click="{() => current = 'baz'}"
>baz</button>

Shorthand Class Directive


Seringkali, nama dari class akan sama dengan nama variabel yang menentukan class tersebut:

<div class:big={big}>
<!-- ... -->
</div>

Pada kasus seperti itu kita bisa menggunakan bentuk singkat:

<div class:big>
<!-- ... -->
</div>

Berikut adalah contoh ketika variabel big diset true saat checkbox dicentang, maka class dari div juga akan berubah menjadi big, jadi ukuran font juga akan menjadi lebih besar seperti seting di css.

<script>
  let big = false;
</script>

<style>
  .big {
    font-size: 4em;
  }
</style>

<label>
  <input type=checkbox bind:checked={big}> big
</label>

<div class:big>
  some {big ? 'big' : 'small'} text
</div>

Svelte Tutorial 12: Actions (English & Indonesia)

English


The Use Directive


Actions are essentially element-level lifecycle functions. They're useful for things like:
  • interfacing with third-party libraries
  • lazy-loaded images
  • tooltips
  • adding custom event handlers
In this app, we want to make the orange box 'pannable'. It has event handlers for the panstartpanmove and panend events, but these aren't native DOM events. We have to dispatch them ourselves. First, import the pannable function and then use it with <div class="box"> element.

<script>
  import { spring } from 'svelte/motion';
  import { pannable } from './pannable.js';

  const coords = spring({ x: 0, y: 0 }, {
    stiffness: 0.2,
    damping: 0.4
  });

  function handlePanStart() {
    coords.stiffness = coords.damping = 1;
  }

  function handlePanMove(event) {
    coords.update($coords => ({
      x: $coords.x + event.detail.dx,
      y: $coords.y + event.detail.dy
    }));
  }

  function handlePanEnd(event) {
    coords.stiffness = 0.2;
    coords.damping = 0.4;
    coords.set({ x: 0, y: 0 });
  }
</script>

<style>
  .box {
    --width: 100px;
    --height: 100px;
    position: absolute;
    width: var(--width);
    height: var(--height);
    left: calc(50% - var(--width) / 2);
    top: calc(50% - var(--height) / 2);
    border-radius: 4px;
    background-color: #ff3e00;
    cursor: move;
  }
</style>

<div class="box"
  use:pannable
  on:panstart={handlePanStart}
  on:panmove={handlePanMove}
  on:panend={handlePanEnd}
  style="transform:
    translate({$coords.x}px,{$coords.y}px)
    rotate({$coords.x * 0.2}deg)"
></div>

Then on the pannable.js file. Like transition functions, an action function receives a node and some optional parameters, and returns an action object. That object can have a destroy function, which is called when the element is unmounted.

We want to fire panstart event when the user mouses down on the element, panmove events (with dx and dy properties showing how far the mouse moved) when they drag it, and panend events when they mouse up. One possible implementation looks like this, you can try to move box around.

export function pannable(node) {
  let x;
  let y;

  function handleMousedown(event) {
    x = event.clientX;
    y = event.clientY;

    node.dispatchEvent(new CustomEvent('panstart', {
      detail: { x, y }
    }));

    window.addEventListener('mousemove', handleMousemove);
    window.addEventListener('mouseup', handleMouseup);
  }

  function handleMousemove(event) {
    const dx = event.clientX - x;
    const dy = event.clientY - y;
    x = event.clientX;
    y = event.clientY;

    node.dispatchEvent(new CustomEvent('panmove', {
      detail: { x, y, dx, dy }
    }));
  }

  function handleMouseup(event) {
    x = event.clientX;
    y = event.clientY;

    node.dispatchEvent(new CustomEvent('panend', {
      detail: { x, y }
    }));

    window.removeEventListener('mousemove', handleMousemove);
    window.removeEventListener('mouseup', handleMouseup);
  }

  node.addEventListener('mousedown', handleMousedown);

  return {
    destroy() {
      node.removeEventListener('mousedown', handleMousedown);
    }
  };
}

Adding Parameters


Like transitions and animations, an action can take an argument, which the action function will be called with alongside the element it belongs to.

Here, we're using a longpress action that fires an event with the same name whenever the user presses and holds the button for a given duration. Here is the example of longpress.js. Important part is duration in second parameter of function declaration; pass duration value in setTimeout; and update(newDuration) in return value.

export function longpress(node, duration) {
  let timer;

  const handleMousedown = () => {
    timer = setTimeout(() => {
      node.dispatchEvent(
        new CustomEvent('longpress')
      );
    }, duration);
  };

  const handleMouseup = () => {
    clearTimeout(timer)
  };

  node.addEventListener('mousedown', handleMousedown);
  node.addEventListener('mouseup', handleMouseup);

  return {
    update(newDuration) {
      duration = newDuration;
    },
    destroy() {
      node.removeEventListener('mousedown', handleMousedown);
      node.removeEventListener('mouseup', handleMouseup);
    }
  };
}

And here is App.svelte, where we can pass the duration value to the action using this <button use:longpress={duration}:

<script>
  import { longpress } from './longpress.js';

  let pressed = false;
  let duration = 2000;
</script>

<label>
  <input type=range bind:value={duration} max={2000} step={100}>
  {duration}ms
</label>

<button use:longpress={duration}
  on:longpress="{() => pressed = true}"
  on:mouseenter="{() => pressed = false}"
>press and hold</button>

{#if pressed}
  <p>congratulations, you pressed and held for {duration}ms</p>
{/if}



Indonesia


The Use Directive


Pada dasarnya action adalah fungsi lifecycle pada tingkat elemen. Action bermanfaat untuk hal-hal seperti:
  • memberikan interface untuk library pihak ketiga
  • melakukan lazy-loaded pada gambar
  • tooltips
  • menambahkan event handlers custom
Pada aplikasi ini, kita ingin membuat kotak oranye menjadi bisa di pan. Kotak mempunyai even handler untuk even panstartpanmove dan panend, tapi even-even ini bukan even asli bawaan DOM. Kita harus melakukan dispatch even ini sendiri. Pertama import fungsi pannable kemudian gunakan di dalam elemen <div class="box">.

<script>
  import { spring } from 'svelte/motion';
  import { pannable } from './pannable.js';

  const coords = spring({ x: 0, y: 0 }, {
    stiffness: 0.2,
    damping: 0.4
  });

  function handlePanStart() {
    coords.stiffness = coords.damping = 1;
  }

  function handlePanMove(event) {
    coords.update($coords => ({
      x: $coords.x + event.detail.dx,
      y: $coords.y + event.detail.dy
    }));
  }

  function handlePanEnd(event) {
    coords.stiffness = 0.2;
    coords.damping = 0.4;
    coords.set({ x: 0, y: 0 });
  }
</script>

<style>
  .box {
    --width: 100px;
    --height: 100px;
    position: absolute;
    width: var(--width);
    height: var(--height);
    left: calc(50% - var(--width) / 2);
    top: calc(50% - var(--height) / 2);
    border-radius: 4px;
    background-color: #ff3e00;
    cursor: move;
  }
</style>

<div class="box"
  use:pannable
  on:panstart={handlePanStart}
  on:panmove={handlePanMove}
  on:panend={handlePanEnd}
  style="transform:
    translate({$coords.x}px,{$coords.y}px)
    rotate({$coords.x * 0.2}deg)"
></div>

Kemudian pada file pannable.js. Seperti pada fungsi transisi, sebuah fungsi action akan menerima node dan beberapa parameter optional, dan me return sebuah object action. Object tersebut bisa memiliki fungsi destroy, yang akan dipanggil ketika elemen di unmount.

Kita ingin menjalankan even panstart ketika terjadi mouse down pada elemen, even panmove  (dengan properti dx dan dy menunjukkan sejauh mana mouse sudah bergerak) ketika kotaknya di drag, dan even panend ketika mouse up. Satu contoh implementasi yang mungkin adalah sebagai berikut, kita bisa coba menggerak-gerakkan kotaknya.

export function pannable(node) {
  let x;
  let y;

  function handleMousedown(event) {
    x = event.clientX;
    y = event.clientY;

    node.dispatchEvent(new CustomEvent('panstart', {
      detail: { x, y }
    }));

    window.addEventListener('mousemove', handleMousemove);
    window.addEventListener('mouseup', handleMouseup);
  }

  function handleMousemove(event) {
    const dx = event.clientX - x;
    const dy = event.clientY - y;
    x = event.clientX;
    y = event.clientY;

    node.dispatchEvent(new CustomEvent('panmove', {
      detail: { x, y, dx, dy }
    }));
  }

  function handleMouseup(event) {
    x = event.clientX;
    y = event.clientY;

    node.dispatchEvent(new CustomEvent('panend', {
      detail: { x, y }
    }));

    window.removeEventListener('mousemove', handleMousemove);
    window.removeEventListener('mouseup', handleMouseup);
  }

  node.addEventListener('mousedown', handleMousedown);

  return {
    destroy() {
      node.removeEventListener('mousedown', handleMousedown);
    }
  };
}

Adding Parameters


Seperti pada transisi dan animasi, sebuah action bisa menerima argumen, yang akan digunakan untuk memanggil fungsi action pada elemen yang akan menjadi tempatnya.

Sekarang, kita akan menggunakan action longpress yang akan memicu even yang memiliki nama sama kapanpun user menekan dan menahan tombol selama waktu yang ditentukan. Di bawah ini adalah contoh dari longpress.js. Bagian penting adalah duration dalam parameter sekon pada deklarasi fungsi; pass nilai duration ke dalam setTimeout; dan update(newDuration) pada nilai return.

export function longpress(node, duration) {
  let timer;

  const handleMousedown = () => {
    timer = setTimeout(() => {
      node.dispatchEvent(
        new CustomEvent('longpress')
      );
    }, duration);
  };

  const handleMouseup = () => {
    clearTimeout(timer)
  };

  node.addEventListener('mousedown', handleMousedown);
  node.addEventListener('mouseup', handleMouseup);

  return {
    update(newDuration) {
      duration = newDuration;
    },
    destroy() {
      node.removeEventListener('mousedown', handleMousedown);
      node.removeEventListener('mouseup', handleMouseup);
    }
  };
}

Dan di sini adalah contoh App.svelte, di mana kita bisa pass nilai durasi ke action menggunakan ini <button use:longpress={duration}:

<script>
  import { longpress } from './longpress.js';

  let pressed = false;
  let duration = 2000;
</script>

<label>
  <input type=range bind:value={duration} max={2000} step={100}>
  {duration}ms
</label>

<button use:longpress={duration}
  on:longpress="{() => pressed = true}"
  on:mouseenter="{() => pressed = false}"
>press and hold</button>

{#if pressed}
  <p>congratulations, you pressed and held for {duration}ms</p>

{/if}

Svelte Tutorial 11: Animations (English & Indonesia)

English


The Animate Directive


In the previous example, we used deferred transitions to create the illusion of motion as elements move from one todo list to the other.

To complete the illusion, we also need to apply motion to the elements that aren't transitioning. For this, we use the animate directive.

First, import the flip function — flip stands for  'First, Last, Invert, Play' — from svelte/animate:

import { flip } from 'svelte/animate';

Then add it to the <label> elements:

<label
  in:receive="{{key: todo.id}}"
  out:send="{{key: todo.id}}"
  animate:flip
>

You'll see that if you check something and it moved to other side,remaining item will slowly going up, instead of immediately jump. If you want to change speed of movement, we can add a duration parameter:

<label
  in:receive="{{key: todo.id}}"
  out:send="{{key: todo.id}}"
  animate:flip="{{duration: 200}}"
>

duration can also be a d => miliseconds function, where d is the number of pixels the element has to travel.

Note that all the transitions and animations are being applied with CSS, rather than JavaScript, meaning they won't block (or be blocked by) the main thread.

Indonesia


The Animate Directive


Pada contoh sebelumnya, kita menggunakan transisi defer untuk membuat ilusi gerakan saat satu elemen pindah dari daftar todo ke daftar satunya.

Untuk melengkapi, kita juga bisa memberikan efek gerakan untuk elemen yang tidak pindah. Untuk keperluan ini kita bisa menggunakan direktiv animate.

Pertama, kita import fungsi flip — flip singkatan dari  'First, Last, Invert, Play' — dari svelte/animate:

import { flip } from 'svelte/animate';

Kemudian tinggal menambahkan ke elemen <label> :

<label
  in:receive="{{key: todo.id}}"
  out:send="{{key: todo.id}}"
  animate:flip
>

Kamu akan bisa lihat, jika kita memilih sesuatu kemudian pindah ke daftar sebelahnya, item yang tersisa akan pelan-pelan naik ke atas, dan tidak lompat seketika. Jika ingin mengganti kecepatan gerakan, kita bisa menambahkan parameter duration:

<label

  in:receive="{{key: todo.id}}"
  out:send="{{key: todo.id}}"
  animate:flip="{{duration: 200}}"
>

duration juga bisa berupa fungsi d => milisecondsd adalah jumlah pixel yang dilewati elemen.

Perhatikan bahwa semua transisi dan animasi akan dijalankan pada CSS, dan bukan JavaScript, jadi mereka tidak akan menghalangi (atau dihalangi) oleh main thread.