본문 바로가기
Web과 프로그래밍 언어/JavaScript

[SVELTE] Animations - The animate directive

by cosmicgy 2023. 3. 11.

Animations

 

1. The animate directive

 

- flip 함수

first, last, invert, play 기능을 제공한다. deferred transitions 예제 나왔던 todo list를 예로 들자면, 이동하지 않고 그자리에 남아있는 개별의 list 가 동작하는 방법을 통제할 수 있다.

 

<script>
    import { quintOut } from 'svelte/easing';
    import { crossfade } from 'svelte/transition';
    import { flip } from 'svelte/animate';

    const [send, receive] = crossfade({
        duration: d => Math.sqrt(d * 200),

        fallback(node, params) {
            const style = getComputedStyle(node);
            const transform = style.transform === 'none' ? '' : style.transform;

            return {
                duration: 600,
                easing: quintOut,
                css: t => `
                    transform: ${transform} scale(${t});
                    opacity: ${t}
                `
            };
        }
    });

    let uid = 1;

    let todos = [
        { id: uid++, done: false, description: 'write some docs' },
        { id: uid++, done: false, description: 'start writing blog post' },
        { id: uid++, done: true,  description: 'buy some milk' },
        { id: uid++, done: false, description: 'mow the lawn' },
        { id: uid++, done: false, description: 'feed the turtle' },
        { id: uid++, done: false, description: 'fix some bugs' },
    ];

    function add(input) {
        const todo = {
            id: uid++,
            done: false,
            description: input.value
        };

        todos = [todo, ...todos];
        input.value = '';
    }

    function remove(todo) {
        todos = todos.filter(t => t !== todo);
    }

    function mark(todo, done) {
        todo.done = done;
        remove(todo);
        todos = todos.concat(todo);
    }
</script>

<div class='board'>
    <input
        placeholder="what needs to be done?"
        on:keydown={e => e.key === 'Enter' && add(e.target)}
    >

    <div class='left'>
        <h2>todo</h2>
        {#each todos.filter(t => !t.done) as todo (todo.id)}
            <label
                in:receive="{{key: todo.id}}"
                out:send="{{key: todo.id}}"
                animate:flip
            >
                <input type=checkbox on:change={() => mark(todo, true)}>
                {todo.description}
                <button on:click="{() => remove(todo)}">remove</button>
            </label>
        {/each}
    </div>

    <div class='right'>
        <h2>done</h2>
        {#each todos.filter(t => t.done) as todo (todo.id)}
            <label
                class="done"
                in:receive="{{key: todo.id}}"
                out:send="{{key: todo.id}}"
                animate:flip
            >
                <input type=checkbox checked on:change={() => mark(todo, false)}>
                {todo.description}
                <button on:click="{() => remove(todo)}">remove</button>
            </label>
        {/each}
    </div>
</div>

<style>
    .board {
        display: grid;
        grid-template-columns: 1fr 1fr;
        grid-gap: 1em;
        max-width: 36em;
        margin: 0 auto;
    }

    .board > input {
        font-size: 1.4em;
        grid-column: 1/3;
    }

    h2 {
        font-size: 2em;
        font-weight: 200;
        user-select: none;
        margin: 0 0 0.5em 0;
    }

    label {
        position: relative;
        line-height: 1.2;
        padding: 0.5em 2.5em 0.5em 2em;
        margin: 0 0 0.5em 0;
        border-radius: 2px;
        user-select: none;
        border: 1px solid hsl(240, 8%, 70%);
        background-color:hsl(240, 8%, 93%);
        color: #333;
    }

    input[type="checkbox"] {
        position: absolute;
        left: 0.5em;
        top: 0.6em;
        margin: 0;
    }

    .done {
        border: 1px solid hsl(240, 8%, 90%);
        background-color:hsl(240, 8%, 98%);
    }

    button {
        position: absolute;
        top: 0;
        right: 0.2em;
        width: 2em;
        height: 100%;
        background: no-repeat 50% 50% url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23676778' d='M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M17,7H14.5L13.5,6H10.5L9.5,7H7V9H17V7M9,18H15A1,1 0 0,0 16,17V10H8V17A1,1 0 0,0 9,18Z'%3E%3C/path%3E%3C/svg%3E");
        background-size: 1.4em 1.4em;
        border: none;
        opacity: 0;
        transition: opacity 0.2s;
        text-indent: -9999px;
        cursor: pointer;
    }

    label:hover button {
        opacity: 1;
    }
</style>

 

  • 애니메이션 사용 시 주의사항
  1. key가 있는 each 블록 안에서 사용
  2. 배열이 재정렬 될 때 트리거 된다
    ex) list가 재정렬 될 때 애니메이션이 동작!
<!-- When `list` is reordered the animation will run-->
{#each list as item, index (item)}
  <li animate:flip>{item}</li>
{/each}
  1. each 블록 직계 요소에 사용
    : 애니메이션 디렉티브는 key가 정의된 each 블록의 직계 자식 요소에 정의되어야 한다