Wer schon einmal eine App mit vanilla JS gebaut hat, weiß, wie kompliziert es sein kann, Elemente nicht nur weich einzublenden, sonder auch weich verschwinden zu lassen. Klar kann man einfach die Transparenz animieren und die Pointer-Events ein und ausschalten, aber manchmal möchte man ein Element auch komplett aus dem DOM nehmen. Das ist mit nativen Technologien unverhältnismäßig kompliziert.

Bei Svelte sieht die Welt ganz anders aus. Hier sind butterweiche Transitions ein Standard-Feature. So kannst du dich auf die wichtigen Aspekte deiner App konzentrieren und Kopfschmerzen vermeiden!

Wir schauen uns heute sogenannte “Svelte-Transitions” genauer an, während wir eine einfache Sidebar bauen.

Die Sidebar soll eine eigene Komponente sein, welche wir unter src/lib/components/ anlegen. Falls du es noch nicht wusstest: Der lib Ordner kommt mit einem gratis Alias “$lib”, sodass du die Sidebar später von überall aus mit “$lib/components/Sidebar.svelte” importieren kannst.

<!-- Sidebar.svelte -->
<script>
  let isOpen = true;
</script>

{#if isOpen}
  <aside>
    <p>Hello from sidebar</p>
  </aside>
{/if}

Damit wir auch sehen, was wir tun, importieren wir uns die Sidebar in unserer +page.svelte:

<!-- +page.svelte -->
<script>
  import Sidebar from "$lib/components/Sidebar.svelte"
</script>

<Sidebar />

Screenshot_2024-08-16_at_14.25.32.png

Als nächstes stylen wir unsere Sidebar so, dass sie an der linken Seite fixiert ist und einen Schatten wirft. Außerdem geben wir ihr eine Hintergrundfarbe und ein bisschen Padding:

<!-- Sidebar.svelte -->
<script>
  let isOpen = true;
</script>

{#if isOpen}
  <aside>
    <p>Hello from sidebar</p>
  </aside>
{/if}

<style>
  aside {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    background: white;
    padding: 1rem;
    box-shadow: 0 0 1rem rgba(0, 0, 0, 0.25);
  }
</style>

Screenshot_2024-08-16_at_14.30.21.png

Okay, nun kümmern wir uns darum, dass man die Sidebar auch tatsächlich öffnen und schließen kann. Dazu erstellen und exportieren wir zwei Funktionen:

<!-- Sidebar.svelte -->
<script>
    let isOpen = true;
      
    export function open() {
      isOpen = true;
    }
    
    export function close() {
        isOpen = false;
    }
</script>

...

Jetzt brauchen wir noch einen Button rechts oben, mit dem wir die Sidebar schließen können.

<!-- Sidebar.svelte -->
<script>
  ...
</script>

{#if isOpen}
  <aside>
    <button on:click={close}>X</button>
    <p>Hello from sidebar</p>
  </aside>
{/if}

<style>
  aside {
    ...
    display: flex; /* Wichtig, damit wir den Button mit margin positionieren können */
    flex-direction: column;
  }

  button {
    margin-left: auto;
  }
</style>

Screenshot_2024-08-16_at_14.41.12.png

Wenn wir jetzt auf das X klicken, verschwindet die Sidebar.

Was nun kommt, mag zunächst verwirren, ist aber ganz simpel:

Wir binden die Sidebar-Komponente an eine Variable, sodass wir auf ihre Methoden (die exportierten Funktionen) zugreifen können. Das ist deshalb notwendig, damit Svelte weiß, welche Instanz unserer Komponente wir ansprechen, denn theoretisch könnten wir sie auch mehrfach einbinden.

<!-- +page.svelte -->
<script>
  import Sidebar from "$lib/components/Sidebar.svelte"
  
  let sidebar;
</script>

<Sidebar bind:this={sidebar} />

Ich nenne die Variable gerne so wie die Komponente, nur eben klein geschrieben. Allerdings ist der Name an sich unbedeutend.

Jetzt können wir einen Button hinzufügen, der die open-Funktion der sidebar aufruft:

<!-- +page.svelte -->
<script>
  import Sidebar from "$lib/components/Sidebar.svelte"
  
  let sidebar;
</script>

<button on:click={sidebar.open}>Open Sidebar</button>

<Sidebar bind:this={sidebar} />

Jetzt kannst du die Sidebar öffnen und schließen. Und bevor wir es vergessen, sollten wir die isOpen-Variable in der Komponente auf false setzen.

<!-- Sidebar.svelte -->
<script>
  let isOpen = false;
</script>

...

So, jetzt geht es ENDLICH an die Transitions. Jetzt werden wir dafür sorgen, dass die Sidebar superweich öffnet und schließt.

Wir importieren dazu die fly transition:

<!-- Sidebar.svelte -->
<script>
  import { fly } from 'svelte/transition';
  
  ...
</script>

...

Svelte bietet uns sieben Arten von Transitions: fadeblurflyslidescaledraw und crossfade.

Tatsächlich gibt es die Möglichkeit, selbst custom Transitions zu schreiben, was nichtmal schwierig ist. Doch hat die Erfahrung gezeigt, dass man das so gut wie nie machen muss, da die sieben Funktionen bereits alles abdecken. In 90% aller Fälle wirst du fly, fade, scale und seltener auch slide verwenden.

Fly bedeutet, dass das Element mit “transform: translate” quasi in das Bild “fliegt”. In der Startposition ist es per default transparent und wird dann sichtbar (beim Verschwinden ist es exakt umgekehrt).

Und so wenden wir die Transition auf unsere Sidebar an:

<!-- Sidebar.svelte -->
<script>
  ...
</script>

{#if isOpen}
  <aside transition:fly={{ x: "-100%" }}>
    <button on:click={close}>X</button>
    <p>Hello from sidebar</p>
  </aside>
{/if}

<style>
    ...
</style>

Wir schreiben also in das Start-Tag unseres zu animierenden Elements “transition:” und dann den Namen der gewünschten Transition.

Als Wert legen wir ein Objekt fest und damit Svelte es als JavaScript erkennt, umgeben wir es mit weiteren geschweiften Klammern. Bei fly können wir neben der x-Position folgende attribute festlegen: delay, duration, easing, y und opacity.

Opacity ist voreingestellt auf 0, aber für unsere Zwecke legen wir hier eine 1 fest, damit sich die Sidebar wirklich nur von der Seite herein bewegt und nicht erst auf dem Weg opak wird. Außerdem laden wir uns noch eine schönere Easing-Funktion:

<!-- Sidebar.svelte -->
<script>
  import { cubicOut } from 'svelte/easing';
  ...
</script>

{#if isOpen}
  <aside transition:fly={{ x: "-100%", opacity: 1, easing: cubicOut }}>
    <button on:click={close}>X</button>
    <p>Hello from sidebar</p>
  </aside>
{/if}

<style>
    ...
</style>

ezgif-3-d06113f00e.gif

Momentan wird für das Erscheinen und Verschwinden die selbe Transition verwendet, doch kannst du statt “transition:” auch “in:” und “out:” verwenden. Nehmen wir an, dass du für das Erscheinen eine langsame Transition möchtest, während das Verschwinden lediglich 100ms dauern soll. Dann würdest du folgendes schreiben:

<!-- Sidebar.svelte -->
<script>
  ...
</script>

{#if isOpen}
  <aside
   in:fly={{ x: "-100%", opacity: 1, easing: cubicOut, duration: 500 }}
   out:fly={{ x: "-100%", opacity: 1, easing: cubicOut, duration: 100 }}
  >
    <button on:click={close}>X</button>
    <p>Hello from sidebar</p>
  </aside>
{/if}

<style>
    ...
</style>

Oh, es wäre natürlich noch schön, wenn sich die Sidebar durch einen verdunkelten Hintergrund abheben würde und ein Klick auf diesen die Sidebar schließen würde.

Dazu erstellen und stylen wir den Hintergrund und fügen auch ihm eine Transition hinzu. Dazu nehmen wir diesmal fade.

<!-- Sidebar.svelte -->
<script>
  import { fly, fade } from 'svelte/transition';
    ...
</script>

{#if isOpen}
  <button
     in:fade={{ easing: cubicOut, duration: 500 }}
     out:fade={{ easing: cubicOut, duration: 100 }}
    class="backdrop"
    on:click={close}
  ></button>
  <aside
   in:fly={{ x: "-100%", opacity: 1, easing: cubicOut, duration: 500 }}
   out:fly={{ x: "-100%", opacity: 1, easing: cubicOut, duration: 100 }}
  >
    <button on:click={close}>X</button>
    <p>Hello from sidebar</p>
  </aside>
{/if}

<style>
    ...
    
  .backdrop {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.1);
    border: none;
  }
</style>

ezgif-2-2ca6680dc9.gif

Ist das nicht genial einfach? Das hat uns jetzt effektiv 10 Minuten Zeit gekostet und der Code ist übersichtlich und sehr einfach anzupassen.

Wenn du mehr Einzelheiten über Svelte-Transitions erfahren möchtest, kommst du wohl nicht drumherum, die Dokumentation zu lesen: https://svelte.dev/docs/svelte-transition

Ansonsten probier dich aus und versuche auf jeden Fall einmal, eine eigene Transition zu erstellen. Das ist, wie gesagt, in der Praxis kaum notwendig, aber wichtig, um ein tieferes Verständnis dafür zu bekommen, was hinter den Kulissen abgeht. Dieser Blog-Post erklärt dir alles im Detail: https://css-tricks.com/making-your-first-custom-svelte-transition/

Hier findest du den gesamten Code, den wir zusammen geschrieben haben: https://tinyurl.com/svelte-transitions