Post

LUVIT 새로운 웹 개발의 시작 스벨트3

LUVIT 새로운 웹 개발의 시작 스벨트3

들어가며

이 포스트는 오시내님의 「LUVIT 새로운 웹 개발의 시작 스벨트」 11장부터 18장까지 읽고 개인적으로 학습한 내용을 정리한 글입니다.

  • 책: LUVIT 새로운 웹 개발의 시작 스벨트
  • 저자: 오시내
  • 출판사: 제이펍
  • 챕터: 11 ~ 18장

핵심 내용 정리

11장 스벨트 slot

slot은 부모 컴포넌트가 자손 컴포넌트에 컨텐츠를 전달하는 기능

  • slot의 기본 문법
1
2
3
4
5
6
7
<자손 컴포넌트명>

</자손 컴포넌트명>

<div>
    <slot></slot>
</div>

fallback slot

  • fallback : default 값을 가진 slot
  • 자식 컴포넌트
1
2
3
4
5
<div class="box">
    <slot>
        <p>입력된 데이터가 없습니다.</p>
    </slot>
</div>
  • 부모 컴포넌트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
    import Child02 from "./Child02.svelte";
</script>

<Child02>
    <h4>이름 : 스벨트(Svelte)</h4>
    <p>배포 년도 : 2016</p>
    <img src="https://svelte.dev/favicon.png" alt="스벨트(Svelte)" height="50" />
</Child02>

<Child02>
    <h4>이름 : 리액트(React)</h4>
    <p>배포 년도 : 2013</p>
    <img src="https://ko.legacy.reactjs.org/favicon.ico" alt="리액트(React)" height="50" />
</Child02>
<Child02></Child02>

slot 이름 설정

  • 자식 컴포넌트 : slot에 이름 지정한 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="box">
    <h4>
        이름 : 
        <slot name="name">
            전달받은 이름이 없습니다.
        </slot>
    </h4>
    <p>
        배포 년도 : 
        <slot name="release">
            전달받은 배포 년도가 없습니다. 
        </slot>
    </p>
</div>
  • 부모 컴포넌트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
    import Child03 from "./Child03.svelte";
</script>

<Child03>
    <span slot="name">스벨트(Svelte)</span>
    <span slot="release">2016</span>
</Child03>

<Child03>
    <span slot="name">리액트(React)</span>
    <span slot="release">2013</span>
</Child03>
<Child03></Child03>

slot props

  • slot props : slot 요소가 명시된 하위 컴포넌트의 값을 상위 컴포넌트로 전달할 때 사용
  • 자식 컴포넌트
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
    let hovering;
    const enter = () => hovering = true;
    const leave = () => hovering = false;
</script>
<div class="box">
    <h4 on:mouseenter={enter} on:mouseleave={leave}>
        이름 :
        <slot hovering={hovering}>
            전달받은 이름이 없습니다.
        </slot>
    </h4>
</div>
  • 부모 컴포넌트
1
2
3
4
5
6
7
8
9
<script>
    import Child04 from "./Child04.svelte";
</script>
<Child04 let:hovering={active}>
    <span class:active={active}>스벨트(Svelte)</span>
</Child04>
<Child04 let:hovering={active}>
    <span class:active={active}>리액트(React)</span>
</Child04>

조건 slot

1
2
3
4
5
6
7
8
9
10
{#if $$slots.name && $$slots.release}
<div class="box">
    <h4>
        이름 : <slot name="name"></slot>
    </h4>
    <p>
        배포 년도 : <slot name="release"></slot>
    </p>
</div>
{/if}

12장 라이프 사이클

onMount

  • onMount 함수는 컴포넌트가 DOM에 마운트(구현)되면 실행
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
    import { onMount } from 'svelte';
  
    let comments = [];
  
    onMount(async () => {
      const res = await fetch(`https://jsonplaceholder.typicode.com/comments?_limit=21`);
      comments = await res.json();
    });
  </script>
  <h3>회원 정보</h3>
  
  <div class="comments">
    {#each comments as comment}
      <article>
        <h4>이름 : {comment.name}</h4>
        <h4>이메일 주소 : {comment.email}</h4>
      </article>
    {:else}
      <!-- comments의 배열 데이터 개수가 0개인 경우(불러오는 ) -->
      <p>loading...</p>
    {/each}
  </div>

onDestory

  • onDestory : 컴포넌트가 제거될 때 호출
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
    import { onMount, onDestroy } from 'svelte'; 

    onMount(() => {
        console.log('onMount()실행');
    });

    onDestroy(() => {
        console.log('onDestroy()실행');
    });
</script>

<div>
    <h3>자손 컴포넌트</h3>
</div>

beforeUpdate & afterUpdate

  • beforeUpdate : DOM이 업데이트 되기 직전에 호출되는 라이프 사이클 함수
  • afterUpdate : DOM이 업데이트 직후 호출되는 라이프 사이클 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
    import { beforeUpdate, afterUpdate } from 'svelte';

    let num = 0;

    beforeUpdate(() => {
        console.log('업데이트가 실행되기 전...');
    });
    afterUpdate(() => {
        console.log('업데이트 완료!');
    });

    const handleClick = () => {
        num++;
    }
</script>

<button on:click={handleClick}>숫자 증가</button>
<p>현재 숫자: {num}</p>

tick 함수

  • tick
    • 언제든지 사용할 수 있는 함수(마운트, 언마운트 모두)
    • tick 함수는 변경된 내용이 있을 경우, 변경된 내용이 DOM에 반영된 지궇에 호출
    • tick 함수가 컴포넌트 또는 element에 적용되면 Promise 객체 반환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
    import { tick } from 'svelte';

    let isElementView = false;
    let input;

    const handleClick = (param) => {
        isElementView = param;
        if(isElementView === true) {
            tick().then(() => input.focus());
        }
    }
</script>

<button on:click={() => handleClick(true)}>input 활성화</button>
<button on:click={() => handleClick(false)}>input비 활성화</button>
{#if isElementView}
    <div>
        <input type="text" bind:this={input} />
    </div>
{/if}

13장 context API

  • context API
    • 최상위 컴포넌트에서 전달하려는 데이터를 콘텍스트 영역에 추가한 후, 자식 컴포넌트들이 이 영역에 접근해 데이터를 사용하는 방식
    • 사실상 컴포넌트단의 전역 변수
  • props drilling
    • 여러 컴포넌트에 Props를 계속 전달하는 과정 => 비효율적

Context API 기본 사용 법

  • setContext : Context에 데이터를 추가하는 함수, 매개 변수에 키와 값을 쌍으로 작성해야함
  • getContext : Context에 데이터를 가져오는 함수, 키를 매개변수로 전달해 데이터를 가져옴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
    import { setContext, getContext } from "svelte";
    import ContextFather from "./ContextFather.svelte";

    setContext('num',1);
    let num = getContext('num');
</script>

<div>
    <h1>Grand 구역</h1>
    <button on:click={() => num++}>1 증가</button>
    <p>기본 숫자 : { num }</p>
    <hr />
    <ContextFather />
</div>

dispatch

  • dispatch
    • 이벤트 전달을 위한 방법
    • 하위 컴포넌트에서 생성한 이벤트 함수를 상위 컴포넌트에서 사용할 수 있게 함
  • dispatch 예시
  • 최하위 컴포넌트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
    import { createEventDispatcher } from 'svelte'

    const dispatch = createEventDispatcher();

    const addAction = param => {
        dispatch('add', {
            value : param,
            message: param + ' 값 추가'
        });
    }
</script>
    
<button on:click={() => addAction(1)} >1 증가</button>
<button on:click={() => addAction(2)} >2 증가</button>
  • 중간 컴포넌트
1
2
3
4
5
<script>
    import DispatchChild from "./DispatchChild.svelte";
</script>
  
<DispatchChild on:add />
  • DispatchChild on:add를 통해 add인 dispatch를 전달해야함

  • 최상위 컴포넌트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
    import DispatchParent from "./DispatchParent.svelte";

    let num = 0;

    const handleValueAdd = e => {
        console.log(e.detail.message);
        num = num + e.detail.value;
    }
</script>

<p>num : {num}</p> 

<DispatchParent on:add={handleValueAdd}  />

14장 Store

  • Store
    • 전역에서 데이터를 전달받고 싶거나, 데이터 변경을 요청 시 사용
    • Vuejs의 Vuex와 동일

writable 스토어

  • writable 스토어 기본 문법
1
2
3
4
5
import {writable} from 'svelte/store';
export const storeName = writable(defaultValue);

// markup
{ $storeName}
  • get : 값 가져오기
  • set : 값 초기화
  • update : 값 수정
  • subscribe : 값을 반응성 형태로 조회

readable 스토어

  • readable 스토어
    • 읽기만 가능한 스토어
    • 현재 시간, 사용자 위치, 마우스 위치 등 수정이 필요하지 않은 스토어 선언 시 사용
    • 스토어는 js 파일로 생성 -> 컴포넌트가 아님
  • readable 스토어 예시
1
2
3
4
5
6
7
8
9
10
11
import { readable } from 'svelte/store';

export const time = readable(new Date(), function start(set) {
	const interval = setInterval(() => {
		set(new Date());
	}, 1000);

	return function stop() {
		clearInterval(interval);
	};
});

derived 스토어

  • derived 스토어
    • 기존의 스토어값에서 파생된 값을 가공해 사용할 수 있게 해주는 기능
  • derived 스토어 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { readable, derived } from 'svelte/store';

export const time = readable(new Date(), function start(set) {
	const interval = setInterval(() => {
		set(new Date());
	}, 1000);

	return function stop() {
		clearInterval(interval);
	};
});

const start = new Date(); 

export const elapsed = derived(time, ($time) => Math.round(($time - start) / 1000));

custom 스토어

  • custom 스토어
    • 개발자가 custom한 스토어
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { writable } from 'svelte/store';

const createCount = () => {
  const { subscribe, set, update } = writable(0);

  return {
    subscribe,
    increment: () => update(n => n + 1),
    decrement: () => update(n => n - 1),
    reset: () => set(0)
  };
}

export const count = createCount();

15장 스벨트 CSS 제어

스벨트 css 기본 사용법

  • 스벨트 css 기본 사용법 얘시
1
2
3
4
5
6
7
8
9
10
11
12
<script>
    let color01 = 'pink';
</script>

<h1>제목 태그1</h1>
<h2 class="title02">제목 태그2</h2>
<h3 style="color: {color01}">{color01}</h3>

<style>
    h1{ background-color: black; color: pink; }
    .title02{ background-color: black; color: yellow; }
</style>

class:지시문

  • class:지시문을 통한 상탯값과 연동해 콘텐츠를 제어하는 방법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
    let current = 'first';
</script>

<button class:active={current === 'first'} on:click={() => current = 'first'}> 번째 버튼</button>
<button class:active={current === 'second'} on:click={() => current = 'second'}> 번째 버튼</button>
<button class:active={current === 'third'} on:click={() => current = 'third'}> 번째 버튼</button>

<style>
    button{
        border: none; border-radius: 5px; background-color: #ededed; 
        padding: 5px 20px; cursor: pointer;
    }
    button::after{ content: ' - 비활성'; }
    .active{
        background-color: cornflowerblue; color: white;
    }
    .active::after{ content: ' - 활성'; }
</style>

rollup을 통한 Sass 플러그인 설치

  • rollup
    • webpack과 같은 bundler
  • Sass 적용하기

16장 스벨트 트랜지션

transition: 지시문

  • transition: 기본 문법
1
2
3
4
<script>
</script>
    import {transitionName} from 'svelte/transition';
<tag element transition:transitionName={parameter}/>
트랜지션명설명
fade요소의 투명도를 통한 애니메이션화
blur요소의 불투명도와 함께 흐림필터를 애니메이션에 적용
slide요소를 상단 혹은 좌측 기준으로 나타나거나 사라지게 함
scale요소의 불투명도와 크기에 애니메이션을 적용
fly요소의 x,y 위치와 불투명도에 애니메이션을 전환
drawSVG 요소에 애니메이션을 적용
crossfade두 개의 요소 간 화면전환 효과 적용

페이드 효과

  • 페이드 효과 예시(in,out 적용)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
	import { fade } from 'svelte/transition';
	let visible = false;
</script>

<label>
	<input type="checkbox" bind:checked={visible} /> 보</label>

{#if visible}
	<p 
        in:fade=
        out:fade=
    >Svelte Fade Effect</p>
{/if}

<style>
    p{ height: 100px; background-color: orange; }
</style>

블러 효과

  • 블러 효과 예시(in,out적용)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
	import { blur } from 'svelte/transition';
	let visible = false;
</script>

<label>
	<input type="checkbox" bind:checked={visible} /> 보</label>

{#if visible}
	<p
        in:blur=
        out:blur=
    >Svelte Blur Effect</p>
{/if}

<style>
    p{ height: 100px; background-color: orange; }
</style>
  • blur 파라미터 종류
파라미터명설명
delay효과를 지연시키는 속성, 기본값은 0, 숫자값으로 작성, ms단위
duration변화가 일어나는 시간, 기본값은 400, 숫자값으로작성, ms단위
easing변화에 속도감을 주는 속성, easing 함수명으로 작성, 기본값은 cubicInOut
opacity애니메이션의 투명도 값을 지정, 기본값은 0
amountblur의 번짐 크기를 지정,기본값은 5

슬라이드 효과

  • 슬라이드 효과 : 상단 혹은 좌측 기준으로 요소를 나타나거나 사라지게 함
  • 슬라이드 효과 예시(in,out 적용)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
	import { slide } from 'svelte/transition';
	let visible = false;
</script>

<label>
	<input type="checkbox" bind:checked={visible} /> 보</label>

{#if visible}
	<p
        in:slide=
        out:slide=
    >Svelte Slide Effect</p>
{/if}

<style>
    p{ height: 100px; background-color: orange; }
</style>

스케일 효과

  • 스케일 효과 : 요소의 불투명도와 크기를 애니메이션에 적용
  • 스케일 효과 예시(in,out 적용)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
	import { scale } from 'svelte/transition';
	let visible = false;
</script>

<label>
	<input type="checkbox" bind:checked={visible} /> 보</label>

{#if visible}
	<p
        in:scale=
        out:scale=
    >Svelte Scale Effect</p>
{/if}

<style>
    p{ height: 100px; background-color: orange; }
</style>

플라이 효과

  • 플라이 효과: 요소의 x,y 위치와 불투명도를 조정하는 애니메이션 효과
  • 플라이 효과 예시(in,out 적용)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
	import { fly } from 'svelte/transition';
	let visible = false;
</script>

<label>
	<input type="checkbox" bind:checked={visible} /> 보</label>

{#if visible}
	<p
        in:fly=
        out:fly=
    >Svelte Fly Effect</p>
{/if}

<style>
    p{ width: 100px; height: 100px; background-color: orange; }
</style>

그리기 효과

  • SVG 요소에 애니메이션 적용 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
    import { draw } from 'svelte/transition';
    import { quintOut } from 'svelte/easing';

    let visible = false;
</script>

<label>
	<input type="checkbox" bind:checked={visible} /> 보</label> 
<svg viewBox="0 0 5 5" xmlns="http://www.w3.org/2000/svg">
    {#if visible}
        <path
            transition:draw=
            d="M2 1 h1 v1 h1 v1 h-1 v1 h-1 v-1 h-1 v-1 h1 z"
            fill="none"
            stroke="cornflowerblue"
            stroke-width="0.1px"
            stroke-linejoin="round"
        />
    {/if}
</svg>

크로스페이드 효과

  • crossfade
    • 두 개의 요소 간의 화면전환 효과
  • crossfade 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<script>
    //crossfade 호출
    import { crossfade } from 'svelte/transition';
    import { quintOut } from 'svelte/easing';

    //crossfade() 내부 값 비구조화할당
    const [send, receive] = crossfade({
        duration: 400, //보내고 받을 때의 시간 지정
        easing: quintOut //보내고 받을 때의 easing 함수 지정
    });
    let bid = 1;
    let buckets = [
		{ id: bid++, chk: false, text: '웹 프론트엔드 개발자되기' },
		{ id: bid++, chk: false, text: '유럽 여행하기' },
		{ id: bid++, chk: false, text: '영국가서 손흥민 축구 경기 보기' }
	];
    let finished = [];
    $: remainingBuckets = buckets.filter(bucket => !bucket.chk).length;
    $: finishedBuckets = finished.filter(bucket => bucket.chk).length;

    const onAdd = () => {
        buckets = buckets.concat({ id: bid++, chk: false, text: '' });
    }
    
    //배열 데이터를 이동하는 함수선언
    const move = (item, from, to) => {
        //item : 선택된 요소
        //from : 요소의 현재 배열
        //to: 이동할 배열
        to.push(item);
        return [from.filter(i => i !== item), to];
    }

    //B영역의 요소를 A영역으로 보내는 함수선언
    const moveLeft = item => { 
        [finished, buckets] = move(item, finished, buckets);
    }

    //A영역의 요소를 B영역으로 보내는 함수선언
    const moveRight = item => {
        [buckets, finished] = move(item, buckets, finished);
    }
</script>

<h1>Bucket List</h1>
<div class="bucketBlock">
    <div class="unfinished">
        <h2>Unfinished Buckets</h2>
        {#each buckets as bucket (bucket.id)}
            <div
                in:receive=
                out:send=
            >
                <input type="checkbox" bind:checked={bucket.chk} on:change={() => moveRight(bucket)} />
                <input type="text" placeholder="당신의 버킷리스트는 뭔가요?" style="width: 250px" bind:value={bucket.text} disabled={bucket.chk} />
            </div>
        {/each}
        <p>남은 버킷리스트 : {remainingBuckets}</p>
        <button on:click={onAdd}>새로운 버킷 추가</button>
    </div>
    <div class="finished">
        <h2>Finished Buckets</h2>
        {#each finished as bucket (bucket.id)}
            <div
                in:receive=
                out:send=
            >
                <input type="checkbox" bind:checked={bucket.chk} on:change={() => moveLeft(bucket)} />
                <input type="text" placeholder="당신의 버킷리스트는 뭔가요?" style="width: 250px" bind:value={bucket.text} disabled={bucket.chk} />
            </div>
        {/each}
        <p>완료된 버킷리스트 : {finishedBuckets}</p>
    </div>
</div>

<style>
    .bucketBlock{ display: flex; }
    .unfinished{ margin-right: 40px; }
</style>

커스텀 트랜지션

  • 개발자가 스스로 제작한 트랜지션
  • 예시 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<script>
    import { fade } from 'svelte/transition';
	import { elasticOut } from 'svelte/easing';

    let visible = false;

    function spin(node, { duration }){
        return {
            duration,
            css: (t) => {
                const eased = elasticOut(t);

                console.log(eased);
                return `
                    transform: scale(${eased}) rotate(${eased * 1080}deg);
                `;
            }
        }
    }
</script>

<label>
	<input type="checkbox" bind:checked={visible} /> 보</label>
{#if visible}
	<div class="centered" in:spin= out:fade|global>
		<span>transitions!</span>
	</div>
{/if}

<style>
	.centered {
		position: absolute; left: 50%; top: 50%;
		transform: translate(-50%, -50%);
	}

	span {
		position: absolute; font-size: 4em;
		transform: translate(-50%, -50%);
	}
</style>

트랜지션 이벤트

  • 트랜지션 이벤트 종류
이벤트명명설명
introstart요소가 나타나는 트랜지션의 시작 이벤트
introend요소가 나타나는 트랜지션의 종료 이벤트
outtrostart요소가 사라지는 트랜지션의 시작 이벤트
outtroend요소가 사라지는 트랜지션의 종료 이벤트

트랜지션 수식어

  • 이벤트 수식어 기본 문법
1
<tag transition:effectName|local></tag>

17장 애니메이션과 모션

animate: 지시문과 flip 효과

  • animate: 기본 문법
1
2
3
4
<script>
    import {animationName} from 'svelte/animate';
</script>
<tag element animate:transitionName={parameter}/>
  • 실제 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
    import { flip } from 'svelte/animate';

    let items = [1, 2, 3];
    const shuffle = () => {
        items = items.sort(() => Math.random() - 0.5);
    }
</script>



{#each items as item (item)}
    <div class="item" animate:flip>
        {item}
    </div>
{/each}
<hr />
<button on:click={shuffle}>순환</button>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<script>
    //crossfade 호출
    import { crossfade } from 'svelte/transition';
    import { quintOut } from 'svelte/easing';
    import { flip } from 'svelte/animate';

    //기존 코드에서 추가된 부분
    //crossfade() 내부 값 비구조화할당
    const [send, receive] = crossfade({
        duration: 400, //보내고 받을 때의 시간 지정
        easing: quintOut, //보내고 받을 때의 easing함수 지정

        fallback(node, params) {
            return {
                duration: 300,
                easing: quintOut,
                css: t => `
                    transform: scale(${t});
                    opacity: ${t}
                `
            };
        }
    });
    let bid = 1;
    let buckets = [
		{ id: bid++, chk: false, text: '웹 프론트엔드 개발자되기' },
		{ id: bid++, chk: false, text: '유럽 여행하기' },
		{ id: bid++, chk: false, text: '영국가서 손흥민 축구 경기 보기' }
	];
    let finished = [];
    $: remainingBuckets = buckets.filter(bucket => !bucket.chk).length;
    $: finishedBuckets = finished.filter(bucket => bucket.chk).length;

    const onAdd = () => {
        buckets = buckets.concat({ id: bid++, chk: false, text: '' });
    }
    
    //배열 데이터를 이동하는 함수선언
    const move = (item, from, to) => {
        //item : 선택된 요소
        //from : 요소의 현재 배열
        //to: 이동할 배열
        to.push(item);
        return [from.filter(i => i !== item), to];
    }

    //B영역의 요소를 A영역으로 보내는 함수선언
    const moveLeft = item => { 
        [finished, buckets] = move(item, finished, buckets);
    }

    //A영역의 요소를 B영역으로 보내는 함수선언
    const moveRight = item => {
        [buckets, finished] = move(item, buckets, finished);
    }
</script>

<h1>Bucket List</h1>
<div class="bucketBlock">
    <div class="unfinished">
        <h2>Unfinished Buckets</h2>
        {#each buckets as bucket (bucket.id)}
            <div
                in:receive|global=
                out:send|global=
                animate:flip
            >
                <input type="checkbox" bind:checked={bucket.chk} on:change={() => moveRight(bucket)} />
                <input type="text" placeholder="당신의 버킷리스트는 뭔가요?" style="width: 250px" bind:value={bucket.text} disabled={bucket.chk} />
            </div>
        {/each}
        <p>남은 버킷리스트 : {remainingBuckets}</p>
        <button on:click={onAdd}>새로운 버킷 추가</button>
    </div>
    <div class="finished">
        <h2>Finished Buckets</h2>
        {#each finished as bucket (bucket.id)}
            <div
                in:receive|global=
                out:send|global=
                animate:flip
            >
                <input type="checkbox" bind:checked={bucket.chk} on:change={() => moveLeft(bucket)} />
                <input type="text" placeholder="당신의 버킷리스트는 뭔가요?" style="width: 250px" bind:value={bucket.text} disabled={bucket.chk} />
            </div>
        {/each}
        <p>완료된 버킷리스트 : {finishedBuckets}</p>
    </div>
</div>

<style>
    .bucketBlock{ display: flex; }
    .unfinished{ margin-right: 40px; }
</style>

커스텀 애니메이션 만들기

  • 커스텀 트랜지션 함수 기본 문법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<script>
    import { elasticOut } from 'svelte/easing';

    let items = [1, 2, 3];
    const shuffle = () => {
        items = items.sort(() => Math.random() - 0.5);
    }

    function spinFlip(node, { duration }){
        return {
            duration: 500,
            css: (t) => {
                const eased = elasticOut(t);
                return `
                    transform: scale(${eased}) rotate(${eased * 1080}deg);
                `;
            }
        }
    }
</script>



{#each items as item (item)}
    <div class="item" animate:spinFlip>
        {item}
    </div>
{/each}
<hr />
<button on:click={shuffle}>순환</button>

<style>
    .item {
        padding: 10px; margin: 5px;
        background-color: #f0f0f0;
        border: 1px solid #ccc;
        display: inline-block;
    }
</style>

모션의 tweened 효과

  • 스벨트는 변수값이 변경될 때 애니메이션을 사용할 수 있는 모션 기능 제공 => 스토어 기반 작동
  • 모션은 tweend , spring 함수로 구분
  • tweended 함수 기본 문법
1
const storeName = tweend(value, option);
  • tweended 옵션의 파라미터 종류
파라미터명설명
delay효과를 지연시키는 속성. 기본값은 0. 숫자라 작성. ms 단위
duration변화가 일어나는 시간. 기본값은 400. 숫자라 작성. ms 단위
easing변화에 속도감을 주는 속성. 기본값은 cubicInOut.
interpolate두 값 사이를 보간해 좀 더 부드럽게 보여주는 옵션
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
	import { tweened } from 'svelte/motion';
	import { cubicOut } from 'svelte/easing';

	const progress = tweened(0, {
		duration: 400,
		easing: cubicOut
	});
</script>

<progress value={$progress} />

<button on:click={() => progress.set(0)}> 0% </button>
<button on:click={() => progress.set(0.25)}> 25% </button>
<button on:click={() => progress.set(0.5)}> 50% </button>
<button on:click={() => progress.set(0.75)}> 75% </button>
<button on:click={() => progress.set(1)}> 100% </button>

<style>
	progress { display: block; width: 100%; }
</style>

모션의 spring 효과

  • spring 함수 기본 문법
1
const storeName = spring(value, option);
  • spring 옵션의 파라미터의 종류
파라미터명설명
stiffness관성이라는 뜻으로 수치가 높을수록 뻣뻣함이 사라져 모션이 반영. 움직임의 기본 값은 0.15, 0~1 사이 숫자로 작성
damping스프링처럼 튕기는 모션 범위. 값이 낮을수록 범위가 넓어짐. 기본값은 0.8, 0~1 사이 숫자로 작성
precision스프링처럼 튕기는 동작이 정착된 것으로 간주하는 값. 값이 클수록 스프링처럼 튕기는 횟수가 줄어듦. 기본값은 0.01
  • 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
	import { spring } from 'svelte/motion';

	let coords = spring(
		{ x: 50, y: 50 },
		{
			stiffness: 0.1,
			damping: 0.25
		}
	);
</script>

<svg on:mousemove={(e) => coords.set({ x: e.clientX, y: e.clientY })}>
	<circle cx={$coords.x} cy={$coords.y} r="10" />
</svg>

<style>
	svg{ width: 100%; height: 100%; margin: -8px; }
	circle{ fill: #ff3e00; }
</style>

18장 스벨트 액션

액션의 기본 사용 방버

  • 액션(action)
    • DOM 요소가 DOM에 추가될 때 실행하는 함수
  • 액션 기본 문법
1
2
3
4
const actionName = (argument) =>{
    // 실행 명령
}
<element use:actionName/>
  • 액션 기본 문법 예시
1
2
3
4
5
6
7
8
9
<script>
    const colorChange = (node) => {
        node.style.color = 'red';
    }
</script>

<h1 use:colorChange>제목 태그1</h1>
<h1 use:colorChange>제목 태그2</h1>
<h1>제목 태그3</h1>

액션에 매개변수 전달

  • 액션 매개변수 기본 문법
1
2
3
4
5
const actionName = (요소매개변수, 추가매개변수) =>{
    // 실행 명령
}
//마크업
<element use:actionName={적용값}/>
  • 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<script>
    let isInput01 = false;
    let isInput02 = false;

    const handleClick01 = () => {
        isInput01 = true;
    }
    const handleClick02 = () => {
        isInput02 = true;
    }

    const inputFocus = (node) => {
        node.focus();
    }
</script>

<button on:click={handleClick01}> 번째 입력 요소 활성</button>
{#if isInput01}
    <input type="text" placeholder="첫 번째" use:inputFocus />
{/if}
<hr />
<button on:click={handleClick02}> 번째 입력 요소 활성</button>
{#if isInput02}
    <input type="text" placeholder="두 번째" use:inputFocus />
{/if}

update와 destory

  • update : 해당 액션이 사용하는 곳에서 값이 변경될 때 사용
  • destory : 해당 DOM 요소가 사라질 때 호출
  • 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<script>
    let isInput = false;
    let inputValue = '아직 없음'; 

    const handleClick = (param) => {
        isInput = param;
    }

    const inputFocus = (node, value) => {
        node.focus();
        node.value = value;
        return {
            update: (newValue) => {
                node.value = newValue;
            },
            destroy: () => {
                inputValue = '없음';
            }
        }
    }
</script>

<button on:click={() => handleClick(true)}>활성</button>
<button on:click={() => handleClick(false)}>비활성</button>
<hr />
{#if isInput}
    <input type="text" bind:value={inputValue} use:inputFocus={inputValue} />
{/if}
<h3>입력값 : {inputValue}</h3>

19장 스벨트 특별 요소

요소

    • 컾모넌트가 자기 자신을 재귀적으로 포함
    • 마크업 영역의 최상위 수준에는 사용할 수 없음
    • 무한 루프 방지를 위해 if , each 블록 내부에 있거나 컴포넌트의 slot에 전달되어야 함
  • 예시
1
2
3
4
5
6
7
8
9
10
<script>
    export let count;
</script>

{#if count > 0}
    <p>카운트다운... {count}</p>
    <svelte:self count={count - 1} />
{:else}
    <p>발사!</p>
{/if}

요소

    • this 속성으로 지정된 컴포넌트 속성을 사용해 컴포넌트를 동적으로 구현
    • 속성이 변경되면 구성 요소가 삭제되고 다시 생성
  • 기본 문법
1
<svelte:component this={js expression}>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
	import Food01 from './Food01.svelte';
	import Food02 from './Food02.svelte';
	import Food03 from './Food03.svelte';

	const options = [
		{ name: '햄버거', component: Food01 },
		{ name: '피자', component: Food02 },
		{ name: '치킨', component: Food03 }
	];

	let selected = options[0];
</script>

<h2>음식을 선택하세요.</h2>
<select bind:value={selected}>
	{#each options as option}
		<option value={option}>{option.name}</option>
	{/each}
</select>

<svelte:component this={selected.component} />

요소

    • 동적으로 유형의 요소(태그)를 구현
  • 기본 문법
1
2
3
<svelte:element this={selected.component} />

  • 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
	const options = ['h1', 'h3', 'p'];
	let selected = options[0];
</script>

<select bind:value={selected}>
	{#each options as option}
		<option value={option}>{option}</option>
	{/each}
</select>

<svelte:element this={selected}>현재 요소는 {selected} 태그입니다.</svelte:element>

요소

    • 컴포넌트 삭제 시 이벤트 리스터를 제거하거나, 서버 측 렌더링 시 창의 존재 여부를 확인할 필요 없이 차 개체에 이벤트 리스너 추가 가능
  • 기본 문법
1
<svelte:window on:eventName={eventHandler} />
  • 예시 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
	let key, keyCode;

	const handleKeydown = (e) => {
		key = e.key;
		keyCode = e.keyCode;
	}
</script>

<svelte:window on:keydown={handleKeydown} />

<div>
	{#if key}
		<kbd>{key === ' ' ? 'Space' : key}</kbd>
		<p>{keyCode}</p>
	{:else}
		<p>창을 클릭  키보드를 눌러 주세요.</p>
	{/if}
</div>

  • window 객체에 속성 바인딩 처리
1
<svelte:window bind:속성={} />
속성명설명
innerWidth창의 내부 너비
innerHeight창의 내부 높이
outerWidth창의 외부 너비
outerHeight창의 외부 높이
scrollX가로 스크롤바의 좌측 좌표를 반환
scrollY세로 스크롤바의 상단 좌표를 반환
onLinewindow.navigator.onLine과 동일. 페이지가 인터넷에 연결되어 있는지를 확인
devicePixcelRatio현재 디스플레이 장치에 대한 ‘물리적 픽셀’ 해상도와 ‘CSS’ 픽셀’ 해상도의 비율을 반환
  • 일부 틀린 부분이 있어서 수정함

요소

    • 와 유사
    • 창에서 실행되지 않는 visibleChange 와 같은 document 객체의 이벤트에 리스너를 추가 가능
  • 기본 문법
1
2
<svelte:document on:eventName={eventHandler} use:actionName>
<svelte:document bind:element={value}>
  • 예시 코드
1
2
3
4
5
6
7
8
9
10
11
12
<script>
	let selection = '';

	const handleSelectionChange = (e) => (selection = document.getSelection());
</script>

<svelte:document on:selectionchange={handleSelectionChange} />

<h3> 글자를 선택하면 아래 그대로 반환됩니다.</h3>
<hr />
<p>선택된 글자: {selection}</p>

요소

    • document.body 와 같은 요소
    • mouseenter , mouseleave와 같은 document.body의 이벤트에 리스터를 추가 가능
    • use: 지시자를 사용해 액션 사용 가능
  • 기본 문법
1
<svelte:body on:eventName={eventHandler} use:actionName>
  • 예시 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
    import { fade } from 'svelte/transition';

    let visible = false;

    const handleMouseenter = () => (visible = true);
	const handleMouseleave = () => (visible = false);
</script>

<svelte:body on:mouseenter={handleMouseenter} on:mouseleave={handleMouseleave} />
{#if visible}
    <div class="centered" out:fade|global>
        {#each 'SVELTE' as char, i}
            <span in:fade|global=>{char}</span>
        {/each}
    </div>
{/if}

요소

    • 를 사용하면 document.head 요소에 삽입 가능
  • 예시 코드
1
2
3
4
5
6
7
<svelte:head>
    <title>Svelte 특수 요소</title>
	<link rel="stylesheet" href="build/dark-theme.css" />
    <link rel="icon" type="image/png" href="https://svelte.dev/favicon.png" />
</svelte:head>

<h1>Hello world!</h1>

요소

    • 컴포넌트에 지정된 컴파일 옵션을 지정
옵션명설명
immutable={true}변하지 않는 데이터를 사용할 것이라고 컴파일러에게 알려주는 옵션. 컴파일러는 간단한 검사로 참조 값이 변경되었는지 검사
immutable={false}기본값. 좀 더 엄격하게 값이 변경되었는지 확인하는 옵션
accessors={true}컴포넌트의 prop에 getter와 setter를 제공함
accessors={false}기본값. 컴포넌트의 prop에 getter와 setter를 제공x
namespace=”…”컴포넌트가 사용될 네임스페이스. 일반적으로 svg에서 사용
tag=”…”컴포넌트를 사용자가 정의한 요소로 컴파일할 때 사용하는 옵션

요소

    • 를 사용하면 DOM 요소를 작성하지 않고도 슬롯 정의 가능
  • 예시 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
    import Child03 from "./Child03.svelte";
</script>

<Child03>
    <svelte:fragment slot="name">스벨트(Svelte)</svelte:fragment>
    <svelte:fragment slot="release">2016</svelte:fragment>
</Child03>

<Child03>
    <svelte:fragment slot="name">리액트(React)</svelte:fragment>
    <svelte:fragment slot="release">2013</svelte:fragment>
</Child03>
<Child03></Child03>

This post is licensed under CC BY 4.0 by the author.