본문 바로가기
프로그래밍/Web

svelte 번역 - 6 - Template Syntax / Element directives

by 사악신 2024. 10. 17.

on:eventname

on: 지시어는 DOM 이벤트를 수신하는 데 사용됩니다.

<script lang="ts">
    let count = 0;

    function handleClick(event: MouseEvent) {
        count += 1;
    }
</script>

<button on:click={handleClick}>
    count: {count}
</button>

핸들러는 인라인으로 선언할 수 있으며, 성능에 영향을 주지 않습니다.

<button on:click={() => (count += 1)}>
    count: {count}
</button>

이벤트에 수정자를 추가할 수 있으며, | 문자로 구분합니다.

<form on:submit|preventDefault={handleSubmit}>
    <!-- 제출 이벤트의 기본 동작을 방지하여 페이지가 새로고침되지 않도록 함 -->
</form>

사용할 수 있는 수정자는 다음과 같습니다:

  • preventDefaultevent.preventDefault()를 호출하여 기본 동작을 막음
  • stopPropagationevent.stopPropagation()을 호출하여 이벤트가 다음 요소에 도달하는 것을 방지
  • stopImmediatePropagation — 다른 리스너가 실행되지 않도록 방지
  • passive — 터치/휠 이벤트에서 스크롤 성능을 향상
  • capture — 버블링 단계 대신 캡처 단계에서 핸들러 실행
  • once — 핸들러가 한 번 실행된 후 제거됨
  • self — 이벤트 타겟이 해당 요소일 때만 핸들러 실행
  • trustedevent.isTrusted가 참일 때만 핸들러 실행

이벤트 지시어는 값 없이 사용할 수도 있습니다. 이 경우 컴포넌트는 이벤트를 전달하게 됩니다.

<button on:click>컴포넌트가 클릭 이벤트를 발생시킴</button>

같은 이벤트에 대해 여러 리스너를 등록할 수도 있습니다.

<script lang="ts">
    let counter = 0;

    function increment() {
        counter += 1;
    }

    function track(event: MouseEvent) {
        trackEvent(event);
    }
</script>

<button on:click={increment} on:click={track}>Click me!</button>

bind:property

bind: 지시어는 데이터가 자식에서 부모로 흐르도록 허용합니다. 대부분의 바인딩은 특정 요소에 적용됩니다.

<input bind:value={name} />
<textarea bind:value={text} />
<input type="checkbox" bind:checked={yes} />

숫자 입력값은 자동으로 숫자로 변환됩니다. 예를 들어, type="number"가 있을 때 값이 비어 있거나 유효하지 않으면 null이 됩니다.

<input type="number" bind:value={num} />
<input type="range" bind:value={num} />

<input> 요소에서 파일 선택을 위해 bind:files를 사용할 수 있습니다. 이 속성은 읽기 전용입니다.

<label for="avatar">사진 업로드:</label>
<input accept="image/png, image/jpeg" bind:files id="avatar" name="avatar" type="file" />

이벤트와 함께 바인딩을 사용할 때, 정의된 순서에 따라 이벤트 핸들러가 호출될 때 바인딩된 변수 값에 영향을 미칩니다.

<script>
    let value = 'Hello World';
</script>

<input
    on:input={() => console.log('Old value:', value)}
    bind:value
    on:input={() => console.log('New value:', value)}
/>

<select> 요소에서도 바인딩할 수 있습니다.

<select bind:value={selected}>
    <option value={a}>a</option>
    <option value={b}>b</option>
    <option value={c}>c</option>
</select>

bind:this

DOM 노드에 대한 참조를 얻으려면 bind:this를 사용합니다.

<script lang="ts">
    import { onMount } from 'svelte';

    let canvasElement: HTMLCanvasElement;

    onMount(() => {
        const ctx = canvasElement.getContext('2d');
        drawStuff(ctx);
    });
</script>

<canvas bind:this={canvasElement} />

class:name

class: 지시어는 조건에 따라 클래스를 토글하는 짧은 문법을 제공합니다.

<!-- 두 가지 방식은 동일 -->
<div class={isActive ? 'active' : ''}>...</div>
<div class:active={isActive}>...</div>

여러 클래스를 토글할 수도 있습니다.

<div class:active class:inactive={!active} class:isAdmin>...</div>

style:property

style: 지시어는 여러 스타일을 요소에 설정하는 짧은 문법을 제공합니다.

<!-- 두 가지 방식은 동일 -->
<div style:color="red">...</div>
<div style="color: red;">...</div>

<!-- 변수 사용 가능 -->
<div style:color={myColor}>...</div>

<!-- 스타일 여러 개 설정 가능 -->
<div style:color style:width="12rem" style:background-color={darkMode ? 'black' : 'white'}>...</div>

important로 스타일을 표시할 수도 있습니다.

<div style:color|important="red">...</div>

use:action

use: 지시어는 요소가 생성될 때 호출되는 함수를 정의하는 데 사용됩니다.

<script lang="ts">
    import type { Action } from 'svelte/action';

    const foo: Action = (node) => {
        return {
            destroy() {
                // 노드가 DOM에서 제거되었을 때 호출됨
            },
        };
    };
</script>

<div use:foo />

액션은 매개변수를 가질 수 있으며, update 메서드가 반환되면 매개변수가 변경될 때마다 호출됩니다.

<script lang="ts">
    export let bar;

    const foo: Action = (node, bar) => {
        return {
            update(bar) {
                // bar 값이 변경되었을 때 호출됨
            },

            destroy() {
                // 노드가 DOM에서 제거되었을 때 호출됨
            },
        };
    };
</script>

<div use:foo={bar} />

transition:fn

transition: 지시어는 요소가 DOM에 들어가거나 나갈 때 트랜지션 애니메이션을 적용하는 데 사용됩니다. 트랜지션은 상태 변화에 따라 요소가 추가되거나 제거될 때 트리거됩니다.

{#if visible}
    <div transition:fade>페이드 인/아웃 애니메이션</div>
{/if}

트랜지션은 기본적으로 로컬입니다. 즉, 해당 트랜지션이 속한 블록이 생성되거나 파괴될 때만 실행됩니다.

{#if x}
    {#if y}
        <p transition:fade>y가 변경될 때만 페이드 인/아웃</p>
        <p transition:fade|global>x 또는 y가 변경될 때 페이드 인/아웃</p>
    {/if}
{/if}

트랜지션 매개변수

트랜지션도 액션과 마찬가지로 매개변수를 가질 수 있습니다.

{#if visible}
    <div transition:fade={{ duration: 2000 }}>2초간 페이드 인/아웃</div>
{/if}

커스텀 트랜지션 함수

커스텀 트랜지션 함수도 사용할 수 있습니다. 트랜지션 함수는 CSS 애니메이션을 생성하거나 매 프레임마다 tick 함수를 호출할 수 있습니다.

<script lang="ts">
    import { elasticOut } from 'svelte/easing';

    function whoosh(
        node: HTMLElement,
        params: { delay?: number; duration?: number; easing?: (t: number) => number },
    ) {
        const existingTransform = getComputedStyle(node).transform.replace('none', '');

        return {
            delay: params.delay || 0,
            duration: params.duration || 400,
            easing: params.easing || elasticOut,
            css: (t, u) => `transform: ${existingTransform} scale(${t})`,
        };
    }
</script>

{#if visible}
    <div in:whoosh>커스텀 트랜지션</div>
{/if}

css 함수는 애니메이션을 위해 0에서 1 사이의 값인 t를 사용하고, u1 - t 값으로 변환됩니다. CSS 애니메이션을 사용할 수 있다면, tick 대신 CSS 애니메이션을 사용하는 것이 성능 면에서 더 좋습니다.

transition 이벤트

트랜지션을 사용하는 요소는 표준 DOM 이벤트 외에도 추가적인 이벤트를 디스패치합니다. 트랜지션 관련 이벤트는 다음과 같습니다:

  • introstart: 요소가 DOM에 들어갈 때 트랜지션이 시작됨
  • introend: 요소가 DOM에 들어갈 때 트랜지션이 끝남
  • outrostart: 요소가 DOM에서 나갈 때 트랜지션이 시작됨
  • outroend: 요소가 DOM에서 나갈 때 트랜지션이 끝남

이벤트를 수신할 수 있으며, 이를 통해 트랜지션 상태를 추적할 수 있습니다.

{#if visible}
    <p
        transition:fly={{ y: 200, duration: 2000 }}
        on:introstart={() => (status = 'intro started')}
        on:outrostart={() => (status = 'outro started')}
        on:introend={() => (status = 'intro ended')}
        on:outroend={() => (status = 'outro ended')}
    >
        Flies in and out
    </p>
{/if}

in:fn / out:fn

in:out: 지시어는 특정 상태에 따라 요소가 DOM에 들어가거나 나가는 트랜지션을 설정할 수 있습니다.

  • in:은 요소가 DOM에 추가될 때 실행되는 트랜지션입니다.
  • out:은 요소가 DOM에서 제거될 때 실행되는 트랜지션입니다.

이 트랜지션들은 양방향 트랜지션이 아닌 단일 방향 트랜지션을 적용하며, 트랜지션 중단 시 트랜지션이 처음부터 다시 시작됩니다.

{#if visible}
    <div in:fly out:fade>플라이 인, 페이드 아웃 트랜지션</div>
{/if}

animate:fn

animate: 지시어는 keyed each 블록에서 데이터가 재정렬될 때 요소에 애니메이션을 적용하는 데 사용됩니다. 이 애니메이션은 요소가 추가되거나 제거될 때가 아니라, 기존 데이터 항목이 재배치될 때 트리거됩니다.

{#each list as item, index (item)}
    <li animate:flip>{{ item }}</li>
{/each}

애니메이션 함수는 커스텀 애니메이션을 만들 수 있으며, DOMRect 객체를 기반으로 요소의 시작 위치와 종료 위치를 처리합니다.

<script lang="ts">
    import { cubicOut } from 'svelte/easing';

    function whizz(node, { from, to }, params) {
        const dx = from.left - to.left;
        const dy = from.top - to.top;

        const d = Math.sqrt(dx * dx + dy * dy);

        return {
            delay: 0,
            duration: Math.sqrt(d) * 120,
            easing: cubicOut,
            css: (t, u) => `transform: translate(${u * dx}px, ${u * dy}px) rotate(${t * 360}deg);`
        };
    }
</script>

{#each list as item, index (item)}
    <div animate:whizz>{{ item }}</div>
{/each}

애니메이션 함수는 fromto 값을 사용하여 요소의 이동과 변화를 계산합니다. css 함수는 CSS 애니메이션을 생성하고, tick 함수는 애니메이션 동안 매 프레임마다 호출됩니다.


tick 함수

tick 함수는 애니메이션 동안 호출되며, 애니메이션의 현재 진행 상태(tu)를 기반으로 요소의 상태를 업데이트할 수 있습니다.

<script lang="ts">
    function typewriter(node: HTMLElement, { speed = 1 }: { speed?: number }) {
        const valid = node.childNodes.length === 1 && node.childNodes[0].nodeType === Node.TEXT_NODE;

        if (!valid) {
            throw new Error(`이 트랜지션은 텍스트 노드가 하나인 요소에서만 작동합니다`);
        }

        const text = node.textContent;
        const duration = text.length / (speed * 0.01);

        return {
            duration,
            tick: (t) => {
                const i = ~~(text.length * t);
                node.textContent = text.slice(0, i);
            },
        };
    }
</script>

{#if visible}
    <p in:typewriter={{ speed: 1 }}>빠르게 타자 치는 효과</p>
{/if}

이 함수는 텍스트가 서서히 입력되는 효과를 생성하는 데 유용하며, tick 함수를 사용해 애니메이션 진행 상태를 기반으로 텍스트를 업데이트합니다.


Svelte의 트랜지션, 애니메이션 및 이벤트 핸들링은 웹 페이지와 애플리케이션에서 동적인 인터페이스를 구현하는 데 매우 유용합니다. 이를 통해 다양한 사용자 인터페이스 효과를 쉽게 적용하고, 애니메이션 및 상호작용을 손쉽게 추가할 수 있습니다.

반응형

댓글