Sveltekit: different in|out FLY transition (vertical and horizontal) based on screen width

558 views Asked by At

I'm learning Sveltekit and i am trying to figure out how i can implent two differently oriented in:fly/out:fly based on the width of the screen (mobile and monitor for example).

Something like: IF on monitor screen (min-width: 640px) then in:fly from the left ELSE (if on mobile phone) in:fly from the top

This is an example of my code at the moment:

<script lang="ts">
    import { fade, fly } from 'svelte/transition';

    /* Navigation menu */
    const navigation = [
        {
            title: 'Home',
            href: '/'
        },
        {
            title: 'About',
            href: '/about'
        },
        {
            title: 'Projects',
            href: '/projects'
        },
        {
            title: 'Contact',
            href: '/contact'
        }
    ];
</script>
<nav class="navigation" in:fade={{ duration: 100 }} out:fade={{ duration: 100 }}>
    <ul
        class="navigation__content"
        in:fly={{ x: -1000, duration: 300 }}
        out:fly={{ x: -1000, duration: 300 }}
    >
        {#each navigation as navItem}
            <li class="navigation__page">
                <a href={navItem.href}>{navItem.title}</a>
            </li>
        {/each}
    </ul>
</nav>

I am not quite sure how i could implement something like this, i was thinking about a #if statement. I searched the web but couldn't find anything useful.

2

There are 2 answers

7
brunnerh On BEST ANSWER

You can create a store that programmatically matches a media query and switch out the transition arguments accordingly, e.g.

import { onMount } from 'svelte';
import { writable } from 'svelte/store';

export function mediaQueryStore(query) {
    const store = writable(false);
    
    onMount(() => {
        const mediaQueryList = window.matchMedia(query);

        const onChange = () => store.set(mediaQueryList.matches);
        mediaQueryList.addEventListener('change', onChange);
        onChange();

        return () => mediaQueryList.removeEventListener('change', onChange);
    });

    return { subscribe: store.subscribe };
}
const small = mediaQueryStore('(max-width: 600px)');
<div transition:fly={$small ?
  { x: -1000, duration: 300 } : 
  { y: -1000, duration: 300 }}>

(If in/out animations are the same, you can just use transition:...)

REPL

3
Corrl On

One way would be to watch the window width via the innerWidth binding (tutorial) and set x and y accordingly

REPL

<script>
    import {fly} from 'svelte/transition'

    let innerWidth
    const threshold = 640
    $: x = innerWidth >= threshold ? -100 : 0
    $: y = innerWidth < threshold ? -100 : 0

    let nrs = [0,1,2,3,4,5,6,7]

    function add() {
        nrs = [...nrs, nrs.length] 
    }
</script>

<svelte:window bind:innerWidth />

<div id="page">
    <button on:click={add}>add one</button>
    <button on:click={() => nrs = [0,1,2,3]}>reset</button>
    innerWidth: {innerWidth}
    <div id="nrs">
        {#each nrs as nr (nr)}
        <div class="nr"
                 in:fly="{{x, y}}"
                 >
            {nr}
        </div>
        {/each}
    </div>
</div>

<style>
    #page {
        width: 100%;
        height: 100%;
        padding: 2rem;
        background: lightgrey;
    }
    #nrs {
        display: flex;
        align-items: flex-start;
        flex-wrap: wrap;
        gap: .5rem;
    }
    .nr {
        width: 4rem;
        height: 4rem;
        display: grid;
        place-content: center;
        background: black;
        color: white;
    }
    :global(body) {
        padding: 0;
    }
    :global(*) {
        box-sizing: border-box;
    }
</style>