14. Motion

2020. 12. 1. 18:36재주껏 하는 Front-End/Svelte (준비중)

반응형

이번 글부터는 Svelte에서 제공하는 효과에 대해 알아보자. 첫 번째로 알아볼 효과는 Motion으로 트위닝 효과와 스프링 효과를 제공한다. CSS로도 이러한 효과를 만들 수 있지만, Svelte의 Motion은 Writable Store를 사용하여 효과를 쉽게 적용할 수 있도록 기능을 제공한다. 아래의 예제를 통해서 어떻게 사용하는지 알아보자.

 

앞으로 4개의 게시글로 Svelte의 효과에 대해 알아볼 것이다. 다만, 실제 개발 (회사에서 또는 사이트)에서는 효과를 주기 위해 CSS의 애니메이션을 사용하기 때문에 개인 프로젝트나 프로토 타입 개발에서 빠르게 효과를 적용해야 할 때 주로 사용할 것 같다. 효과에 대해 자세히 알고 싶다면, Svelte의 효과보다는 CSS의 트랜지션이나 애니메이션을 학습하는 것을 추천한다.

 

Tweened

 

트위닝 효과는 DOM에서 상태가 변경될 때, 변경되는 요소를 부드럽게 변경하는 것처럼 보여주는 효과로 프로그래스 바의 진행 막대를 변경할 때 주로 사용한다. Tweened는 아래와 같이 여러 개의 옵션을 제공한다.

 

tweened(value: any, { options })

options 종류

1) delay : Tweened 효과가 시작하기 전 시간
2) duration : Tweened 효과가 지속되는 시간
3) ease : 시간 경과에 따른 효과를 지정 (https://svelte.dev/docs#svelte_easing에 효과가 정의되어 있음)
4) interpolate : Tweened 효과의 보간법 설정

※ 참고 : 두 번째 파라미터인 options를 설정하면 Tweened 기본 값이 모두 options의 내용으로 덮어써진다. Tweened 효과가 끝난 후에는 tweened.settweened.update가 Promise를 리턴한다.

 

아래의 예제는 버튼을 클릭할 때마다 프로그래스 막대를 퍼센트에 맞게 업데이트하는 예제 코드이다.

 

<script>
	let progress = 0
</script>

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

<progress value={progress} max="1"></progress>

<button on:click="{() => progress = 0}">
	0%
</button>

<button on:click="{() => progress = 0.25}">
	25%
</button>

<button on:click="{() => progress = 0.5}">
	50%
</button>

<button on:click="{() => progress = 0.75}">
	75%
</button>

<button on:click="{() => progress = 1}">
	100%
</button>

 

실행 결과는 아래와 같다.

 

 

버튼을 클릭할 때마다, 프로그래스 바의 막대가 변하는 것을 확인할 수 있다. 하지만 너무 절도 있게 (?) 변하다 보니 별로 마음에 들지 않는다. 이 경우에는 자바스크립트의 타이머를 이용하여 서서히 변하게 하거나 CSS의 트랜지션을 이용하면 부드럽게 바꿀 수 있다. 하지만 단순한 효과를 내기 위해 많은 작업들을 해야만 한다.

 

HTML5에 처음 적용된 <progress> 태그의 경우에는 CSS 스타일을 적용하기 매우 어렵다. 크로스 브라우징을 지원하기 위해서는 아직까지는 <div>나 <span>을 이용하여 만들어야 하기 때문이다.

 

Svelte의 경우에는 Writable Store 기반의 Motion을 지원한다. 아래의 코드로 변경해보자.

 

<script>
	import { tweened } from 'svelte/motion';
	import { cubicOut } from 'svelte/easing';

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

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

<progress value={$progress}></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>

 

실행하면 아래와 같이 부드럽게 프로그래스 바가 움직이는 것을 확인할 수 있다.

 

 

Spring

 

이번에는 또 다른 효과인 Spring에 대해 알아보자. 위에서 Svelte의 Motion은 쓰기가 가능한 Store라는 것을 설명했으며 Spring 역시 동일하다. 기본적인 사용 방법은 아래와 같다.

 

tweened(value: any, { options })

options 종류

1) stiffness : 스프링의 강도 설정, 값이 높을수록 반응이 빨라짐. (0 ~ 1)
2) damping : 스프링의 탄성 정도 설정, 값이 낮으면 튕기는 범위가 넓어짐. (0 ~ 1)
3) precision : 스프링의 안정화, 값이 높으면 튕기는 효과가 빠르게 없어짐. (0 ~ 1)

※ 참고 : Tweened와 동일하게 Promise가 리턴된다.

 

아래의 예제는 마우스 커서가 움직이는 곳에 SVG의 Circle이 따라가게 하는 예제로, 클릭 여부에 따라 Circle의 크기가 변한다.

 

<script>
	import { writable } from 'svelte/store';

	let coords = writable({ x: 50, y: 50 });
	let size = writable(10);
</script>

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

<div style="position: absolute; right: 1em;">
	<label>
		<h3>stiffness ({coords.stiffness})</h3>
		<input bind:value={coords.stiffness} type="range" min="0" max="1" step="0.01">
	</label>

	<label>
		<h3>damping ({coords.damping})</h3>
		<input bind:value={coords.damping} type="range" min="0" max="1" step="0.01">
	</label>
</div>

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

 

위의 코드에 Spring 효과를 적용해보자.

 

<script>
	import { spring } from 'svelte/motion';

	let coords = spring({ x: 50, y: 50 }, {
		stiffness: 0.1,
		damping: 0.25,
		precision: 0.001
	});

	let size = spring(10);
</script>

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

<div style="position: absolute; right: 1em;">
	<label>
		<h3>강도 ({coords.stiffness})</h3>
		<input bind:value={coords.stiffness} type="range" min="0" max="1" step="0.01">
	</label>

	<label>
		<h3>댐핑 ({coords.damping})</h3>
		<input bind:value={coords.damping} type="range" min="0" max="1" step="0.01">
	</label>
	
	<label>
		<h3>안전성 ({coords.precision})</h3>
		<input bind:value={coords.precision} type="range" min="0" max="1" step="0.001">
	</label>
</div>

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

 

출력 결과는 아래와 같다.

 

 

좀 이상한 점은 stiffness와 damping 옵션은 먹히는 거 같은데, percision 옵션이 먹히는 건지 아닌지 애매하다. 내가 코드를 잘못 짠 건 아닌 거 같은데... 좀 애매하긴 하지만 나머지 효과들은 잘 적용되는 것을 확인할 수 있었다.

 

상황에 따라 적절한 Motion 효과를 사용하여 페이지를 좀 더 동적으로 바꾸는 것도 좋을 것 같다. 다음에는 Animations에 대해 알아보도록 하겠다.

 

오늘은 여기까지~

반응형

'재주껏 하는 Front-End > Svelte (준비중)' 카테고리의 다른 글

16. Transitions  (0) 2020.12.14
15. Animations  (0) 2020.12.08
13. Module Context, Debug  (0) 2020.11.27
12. Context API  (2) 2020.11.24
11. Special Elements  (0) 2020.11.12