Svelte – Ease Visualiser

5 min read


Alright guysss yang ditunggu akhirnya balik lagi.. hahah Okay kali ini akan seru banget yagesya,, karena kita akan membuat data grafik menggunakan svelte.. oke langsung aja …

pertama install depenedency dulu ya…

npm i d3-interpolate

Okay setelah itu lanjut gass buat file eases.js

import * as eases from 'svelte/easing';

const processed_eases = {};

for (const ease in eases) {
	if (ease === "linear") {
		processed_eases.linear = eases.linear;
	} else {
		const name = ease.replace(/In$|InOut$|Out$/, '');
		const type = ease.match(/In$|InOut$|Out$/)[0];

		if (!(name in processed_eases)) processed_eases[name] = {};
		processed_eases[name][type] = {};
		processed_eases[name][type].fn = eases[ease];

		let shape = 'M0 1000';
		for (let i = 1; i <= 1000; i++) {
			shape = `${shape} L${(i / 1000) * 1000} ${1000 - eases[ease](i / 1000) * 1000} `;
			processed_eases[name][type].shape = shape;
		}
	}
}

const sorted_eases = new Map([
	['sine', processed_eases.sine],
	['quad', processed_eases.quad],
	['cubic', processed_eases.cubic],
	['quart', processed_eases.quart],
	['quint', processed_eases.quint],
	['expo', processed_eases.expo],
	['circ', processed_eases.circ],
	['back', processed_eases.back],
	['elastic', processed_eases.elastic],
	['bounce', processed_eases.bounce],
]);

export const types = [
	['Ease In', 'In'],
	['Ease Out', 'Out'],
	['Ease In Out', 'InOut']
];

export { sorted_eases as eases };

dan kita buat dua buah file child visual component,, pertama Grids.svelte

<script>
	export let x, y;
</script>

<svelte:options namespace="svg" />

<rect
	x=0
	y=0
	width=1400
	height=1800
	stroke=#ccc
	style="opacity: 0.5"
	fill=none
	stroke-width=2
/>

{#each { length: 8 } as _, i}
	{#if i < 6}
		<path
			d="M{(i+1) * 200} 0 L{(i+1)*200} 1802"
			class="grid-line"
		/>
	{/if}
	<path
		d="M0 {(i+1) * 200} L1400 {(i+1)*200} "
		class="grid-line"
	/>
{/each}

<path
	style="transform: translateX({x+200}px)"
	d="M0 0 L0 1800"
	class="grid-line-xy"
/>
<path
	style="transform: translateY({y}px)"
	d="M0 400 L1400 400"
	class="grid-line-xy"
/>
<rect
	x=200
	y=400
	width=1000
	height=1000
	stroke=#999
	fill=none
	stroke-width=2
/>

<style>
	.grid-line {
		stroke:#ccc;
		opacity: 0.5;
		stroke-width: 2;
	}

	.grid-line-xy {
		stroke: tomato;
		stroke-width: 2;
	}
</style>

kedua Controls.svelte

<script>
	import { createEventDispatcher } from 'svelte';

	export let current_ease;
	export let current_type;
	export let eases;
	export let types;
	export let duration;
	export let playing;
	export let width;

	const dispatch = createEventDispatcher();

	$: mobile = width && width < 600;
</script>

<div class="easing-sidebar">
		<div class="easing-types">
			<h3>Ease</h3>
			{#if mobile}
				<select bind:value={current_ease}>
					{#each [...eases] as [name]}
						<option
							value={name}
							class:selected={name === current_ease}
						>
							{name}
						</option>
					{/each}
				</select>
			{:else}
				<ul>
					{#each [...eases] as [name]}
						<li
							class:selected={name === current_ease}
							on:click={() => current_ease = name}
						>
							{name}
						</li>
					{/each}
				</ul>
			{/if}
			<h3>Type</h3>
			{#if mobile }
				<select bind:value={current_type}>
					{#each types as [name, type]}
						<option
							value={type}
						>
							{name}
						</option>
					{/each}
				</select>
			{:else}
				<ul>
					{#each types as [name, type]}
						<li
							class:selected={type === current_type}
							on:click={() => current_type = type}
						>
							{name}
					</li>
					{/each}
				</ul>
			{/if}
		</div>
		<h4>
			Duration
		</h4>
		<div class="duration">
			<span>
				<input type="number" bind:value={duration} min="0" step="100"/>
				<button class="number" on:click={() => duration -= 100}>-</button>
				<button class="number" on:click={() => duration += 100}>+</button>
			</span>
			<button class="play" on:click={() => dispatch('play')}>
				{playing ? 'Restart' : 'Play'}
			</button>
		</div>
	</div>

	
<style>
	.easing-sidebar {
		width: 11em;
	}

	ul {
		list-style: none;
		padding: 0;
		display: flex;
		flex-direction: column;
		align-items: flex-start;
		font-size: 18px;
	}

	li {
		padding: 5px 10px;
		background: #eee;
		border-radius: 2px;
		margin: 3px 0;
		cursor:pointer;
	}

	li:hover {
		background: #676778;
		color: white;
	}

	.selected {
		background: #ff3e00;
		color: white;
	}

	h3 {
		margin: 0 10px 0 0;
	}

	h4 {
		margin-bottom: 0;
	}

	select {
		display: inline;
		padding: 0.2em;
		margin: 0;
	}

	.duration {
		width: 100%;
		display: flex;
		align-items: center;
		flex-wrap: wrap;
	}

	.duration span {
		display: flex;
	}

	.duration input {
		width: 80px;
		margin: 10px 10px 10px 0 ;
	}

	.duration button {
		margin: 10px 5px;
	}

	.duration .number {
		width: 30px;
	}

	.duration .play {
		margin: 0 5px 0 auto;
		width: 100%;
	}

	@media (max-width:600px) {
		.easing-types {
			display: flex;
			align-items: center;
			margin-top: 10px;
		}

		.easing-sidebar {
			width: 100%;
		}

		.duration .play {
			margin-left: auto;
			width: unset;
		}

		h3 {
			font-size: 0.9em;
			display: inline;
		}

		h3:nth-of-type(2) {
			margin-left: auto;
		}

		ul li {
			margin-right: 10px;
		}
	}
</style>

kemudian kita import ke parent componentnya… Visualiser.svelte

<script>
	import { interpolateString as interpolate } from 'd3-interpolate';
	import { tweened } from 'svelte/motion';

	import Grid from './VisualiserComp/Grids.svelte';
	import Controls from './VisualiserComp/Controls.svelte';

	import { eases, types } from './VisualiserComp/store/eases';

	let current_type = 'In';
	let current_ease = 'sine';
	let duration = 2000;
	let current = eases.get(current_ease)[current_type];
	let playing = false;
	let width;

	const ease_path = tweened(current.shape, { interpolate });
	const time = tweened(0);
	const value = tweened(1000);

	async function runAnimations() {
		playing = true;

		value.set(1000, {duration: 0});
		time.set(0, {duration: 0});

		await ease_path.set(current.shape);
		await Promise.all([
			time.set(1000, {duration, easing: x => x}),
			value.set(0, {duration, easing: current.fn})
		]);

		playing = false;
	}

	$: current = eases.get(current_ease)[current_type];
	$: current && runAnimations();
</script>

<div bind:offsetWidth={width} class="easing-vis">
	<svg viewBox="0 0 1400 1802">
		<g class="canvas">
			<Grid x={$time} y={$value}/>
			<g class="graph">
				<path
					d={$ease_path}
					stroke="#333"
					stroke-width="2"
					fill="none"
				/>

				<path d="M0,23.647C0,22.41 27.014,0.407 28.496,0.025C29.978,-0.357 69.188,3.744 70.104,4.744C71.02,5.745 71.02,41.499 70.104,42.5C69.188,43.501 29.978,47.601 28.496,47.219C27.014,46.837 0,24.884 0,23.647Z"
					fill="#ff3e00"
					style="transform: translate(1060px, {($value - 24)}px)"
				/>

				<circle
					cx="{$time}"
					cy="{$value}"
					r="15"
					fill="#ff3e00"
				/>
			</g>
		</g>
	</svg>

	<Controls
		{eases}
		{types}
		{playing}
		{width}
		bind:duration
		bind:current_ease
		bind:current_type
		on:play={runAnimations}
	/>
</div>

<style>
	.easing-vis {
		display: flex;
		max-height: 95%;
		max-width: 800px;
		margin: auto;
		border: 1px solid #333;
		border-radius: 2px;
		padding: 20px;
	}

	svg {
		width: 100%;
		margin: 0 20px 0 0;
	}

	.graph {
		transform: translate(200px,400px)
	}

	@media (max-width:600px) {
		.easing-vis {
			flex-direction: column;
			max-height: calc(100% - 3rem);
		}
	}
</style>

dan kita import ke App.svelte

<main>
	<h1>Hello {name}!</h1>
	<p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>
	<Counter />
	<Toggle />
	<Img />
	<String />
	<Statement />
	<Condition />
	<Nested answer={42}/>
	<Nested/>
	<Info {...pkg}/>
	<IfLogin />
	<Comparison />
	<EachYoutube />
	<KeyEachThing />
	<GenerateNumber />
	<MouseMove />
	<MouseMoveInlineHandler />
	<EventModifier />
	<AlertCompEvent />
	<AlertEventForwarding />
	<CustomButtonComp />
	<TextInput />
	<NumericInput />
	<CheckboxInputs />
	<GroupInputs />
	<TextareaInputs />
	<FileInput />
	<SelectBindings />
	<SelectMultiple />
	<EachBlockBindings />
	<MediaElements />
	<Dimensions />
	<BindThisCanvas />
	<ComponentBindings />
	<OnMount />
	<OnDestroyIntervals />
	<BeforeAndAfterUpdate />
	<Tick />
	<h1>The count is {countValue}</h1>
	<Incrementer/>
	<Decrementer/>
	<Resetter/>
	<h1>The count is {$countAutoSubscriptions}</h1>
	<IncrementerAutoSubscriptions/>
	<DecrementerAutoSubscriptions/>
	<ResetterAutoSubscriptions/>
	<h1>The time is {formatter.format($time)}</h1>
	<h1>The time is {formatter.format($time)}</h1>
	<p>
		This page has been open for
		{$elapsed} {$elapsed === 1 ? 'second' : 'seconds'}
	</p>
	<h1>The count is {$countCustom}</h1>
	<button on:click={countCustom.increment}>+</button>
	<button on:click={countCustom.decrement}>-</button>
	<button on:click={countCustom.reset}>reset</button>
	<Tweened />
	<Spring />
	<TransitionsDirective />
	<AddingParameters />
	<InOut />
	<CustomCss />
	<CustomJs />
	<TransitionEvents />
	<Deffered />
	<AnimateDirective />
	<Visualiser />

</main>

dan hasilnya akan seperti ini yages…

Okay nice banget… kita lanjut ke materi selanjutnya… link github ada disini…. cyaaaaa

Bima Sena

Leave a Reply

Your email address will not be published. Required fields are marked *