第十一屆鐵人賽 前端工程師的另一個選擇 svelte DAY27- 過場動畫 (四)

crossfade

昨天介紹完draw,就是透過svg的path繪出一個過場動畫,今天來跟大家分享一下crossfade

crossfade 是甚麼

之前有提到一個fade,不知道讀者們還記不記得,那個fade就是淡入淡出的過場動畫,而 crossfade 可以做到的是在兩種過場動畫之間呈現,而不像之前說的話消失。

crossfade 怎麼用

介紹完 crossfade 的概念後現在來說明要如何使用,這裡我們用一個 todolist 來介紹,請大家先看看下面的程式碼。

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

    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.which === 13 && 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}}"
                         >
                <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}}"
                         >
                <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>

這邊說明一下相關的內容,這裡要注意的是下面幾點。

send receive

在呼叫 crossfade 方法時會有回傳值send跟receive,然後在html上面也記得要有兩個元素分別有其send跟receive,這主要是當狀態變更時,物件會由A元素跑到B元素的位置。

fallback

呼叫 crossfade 時要傳入一個參數他的用途是當物件消失時要做的處置。

小結

今天跟大家介紹如何用svelte的crossfade製作一個todolist,雖然內容有點繁瑣,但是只要掌握了應該可以得心應手。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *