Pull to refresh

О, кинчик

Reading time5 min
Views9K

Мы с друзьями любим играть в настольные игры, но не всегда под рукой оказываются громоздкие коробки с играми. Тут на помощь приходит смартфон. Раньше я написал игру Ну, этот, аналог коробочной игры Бум. Затем родилась идея игры с похожей механикой, но вместо слов - случайные фильмы с кинопоиска. В качестве основы проекта я взял SvelteKit. В статье расскажу об интересном функционале SvelteKit и быстром деплое на площадке Vercel.

Встречайте, О, кинчик!

Немного о проекте

О, кинчик - это игра, по механике похожая на Алиас или Активити. Только вместо скучных слов - фильмы из базы кинопоиска. Как показала практика, отгадывать Человек на проволоке или Реквием по мечте куда интересней, чем банальные слова типа "рыбак" и "охотник".

Игроки разбиваются на две команды, затем игрок за отведенное время пытается объяснить своей команде как можно больше фильмов. Когда фильмы закончатся, команда с большим количеством отгаданных фильмов побеждает.

Исходный код проекта доступен в Gitlab, сыграть можно здесь.

Я не буду углубляться в особенности Svelte (об этом можно прочитать в моих предыдущих статьях: раз, два, три, четыре), а больше уделю внимания функциям, которые мне понравились в SvelteKit.

Что такое SvelteKit

SvelteKit это метафреймворк, построенный на базе svelte с файл роутером (похожим на Next), поддержкой SSR, статической генерацией, serveless и невероятным dev experience, который дает Svelte.

Запуск проекта

Чтобы запустить проект достаточно выполнить команду:

npm init svelte@next my-app

Мастер создания проекта спросит, хотите ли вы использовать демо данные, нужна ли вам поддержка TypeScript, EsLint и Prettier. И подскажет, как установить зависимости и запустить дев сервер.

Api

Для получения данных о фильмах из кинопоиска, мы будем использовать api "Случайный фильм!". Напрямую запросить данные из фронтенда у нас не получится, мы столкнемся с CORS ограничениями. Поэтому воспользуемся функционалом Endpoints, который позволяет создавать серверные функции.

Для этого достаточно создать файл .js или .ts, который будет экспортировать функцию get, post, put, patch или del, для соответвующего HTTP метода.

api/index.js
import parse from 'node-html-parser';

export async function get({ url }) {
	const min_years = url.searchParams.get('min_years') || 1920;
	const max_years = url.searchParams.get('max_years') || new Date().getFullYear();

	const response = await fetch(
		`https://www.kinopoisk.ru/chance/?item=true&not_show_rated=false&count=5&max_years=${max_years}&min_years=${min_years}&rnd=${Math.random()}`
	);

	const json = await response.json();
	const data = json.map((item) => {
		const root = parse(item);
		const id = root.querySelector('.movieBlock').getAttribute('data-id-film');
		const source = root.querySelector('img').getAttribute('src');
		const rating =
			Math.round(Number(root.querySelector('.WidgetStars').getAttribute('value')) * 10) / 10;
		const src = `https://kinopoisk.ru/${source.slice(3)}`;
		const nameRaw = root.querySelector('.filmName a').textContent;
		const name = nameRaw.replace(/ /g, ' ');
		const yearRaw = root.querySelector('.filmName span').textContent;
		const year = yearRaw.match(/\(\d{4}\)/)[0].slice(1, -1);
		return { id, src, name, rating, year };
	});

	return {
		body: data
	};
}

Теперь при заходе на страницу http://localhost:3000/api будет отдаваться набор из случайных фильмов. Поскольку фронтенд и /api теперь находятся на одном домене, можно спокойно получать данные.

Роутинг

Игровой процесс разделен по экранам. Я использовал встроенный файл роутер для навигации между ними. Для создания страницы достаточно создать файл в папке routes, соответствующий пути страницы. Например, файл src/routes/turn.svelte будет соответстовать странице http://localhost:3000/turn. Подбронее про роутинг и систему шаблонов можно прочитать в документации.

Поскольку начать игру можно только с главного экрана, нужно сделать редирект с остальных страниц в корень. Вообще, в SvelteKit есть функционал Loading, который позволяет выполнять код на стороне сервера, что-то вроде getServerSideProps из Next.js. Но я воспользовался функционалом Hooks, который позволяет перехватывать выполнение на стороне сервера и по-разному обрабатывать ответы. В файле src/hooks/index.js содержится следующая логика обработки:

src/hooks/index.js
export async function handle({ request, resolve }) {
	const response = ['/', '/rules', '/api'].includes(request.url.pathname)
		? await resolve(request)
		: { status: 303, headers: { location: '/' } };

	return response;
}

Теперь при заходе на любой адрес, отличный от /, /rules и /api , получим редирект в корень проекта. При этом переходы на уже загруженной странице будут обрабатываться как обычно.

Переходы между экранами

Во время разработки я захотел сделать переходы между страницами с эффектом прокрутки кинопленки. Svelte имеет богатый функционал анимаций, который легко можно встроить для анимаций переходов между страницами. Для этого я завернул страницы в компонент Frames.svelte

src/components/Frames.svelte
<script>
	import {fly} from 'svelte/transition'
	import {linear} from 'svelte/easing'
	let windowHeight;
</script>

<div
	class="frame"
	in:fly={{ y: windowHeight, duration: 500, opacity: 1, easing: linear }}
	out:fly={{ y: -windowHeight, duration: 500, opacity: 1, easing: linear}}
>
	<div class="frameBg left" />
	<div class="frameBg right" />
	<slot />
</div>
<svelte:window bind:innerHeight={windowHeight} />

<style>
	.frame {
		overflow: hidden;
		box-sizing: border-box;
		position: absolute;
		top: 0;
		left: 0;
		bottom: 0;
		right: 0;
		height: 100%;
		width: 100%;
		display: flex;
		flex-direction: column;
		justify-content: space-between;
		padding: 16px 40px;
	}
	.frameBg {
		position: absolute;
		top: 0;
		bottom: 0;
		width: 31px;
		background-repeat: repeat-y;
		background-image: url(/img/frame.svg);
		z-index: 2;
	}
	.left {
		left: 0;
	}
	.right {
		right: 0;
	}
</style>

Когда осуществляется переход, к текущей странице применяется анимация out:fly, а к новой применяется анимация in:fly. Задав нужные параметры, легко получить эффект прокрутки кинопленки. Кстати, обратите внимание на выражение:

<svelte:window bind:innerHeight={windowHeight} />

Такая конструкция позволяет хранить в переменной текущую высоту окна браузера. При изменении размеров, в переменной будет обновленное значение. Эта функция не раз спасала меня при верстке макетов для мобильных браузеров safari, где строка навигации включена в css выражение 100vh.

Абсолютные импорты

Под капотом у SvelteKit скрывается сборщик Vite, что позволяет использовать возможности этого инструмента. В проекте я использовал абсолютные импорты, чтобы получить быстрый доступ к компонентам и стору. Для этого достаточно добавить в файл svelte.config.js директиву, определяющую импорты

svelte.config.js
import adapter from '@sveltejs/adapter-auto';
import path from 'path';

const config = {
	kit: {
		adapter: adapter(),
		target: '#svelte',
		vite: {
			resolve: {
				alias: {
					components: path.resolve('./src/components'),
					store: path.resolve('./src/store.js')
				}
			}
		}
	}
};

export default config;

Деплой

Для деплоя я использовал площадку Vercel. Чтобы задеплоить проект, достаточно выбрать репозиторий и указать фреймворк SvelteKit. После клика Deploy, проект будет собран и развернут на площадке. В дальнейшем, на каждый коммит в выбранную ветку будет автоматически запускаться сборка и деплой проекта.

В качестве заключения

На мой взгляд, разработка и деплой современных веб проектов не были никогда такими простыми. Больше не нужно вручную настраивать сборщик, SSR и кучу всего, что появилось в веб разработке за последние годы. SvelteKit из коробки предоставляет обширные возможности, которые дает разработчикам Svelte, при этом добавляя серверный рендеринг, возможности писать api ендпоинты, использовать файловый роутинг и многое другое. А Vercel позволяет развернуть проект за считаные секунды.

SvelteKit находится в фазе активной разработки и что-то к релизу версии 1.0.0 может измениться, но уже сейчас я рекомендую обратить внимание на этот фреймворк и попробовать его в пет проектах.

Телеграм-группа русского сообщества Svelte: @sveltejs

Tags:
Hubs:
Total votes 18: ↑18 and ↓0+18
Comments16

Articles