
Мы с друзьями любим играть в настольные игры, но не всегда под рукой оказываются громоздкие коробки с играми. Тут на помощь приходит смартфон. Раньше я написал игру Ну, этот, аналог коробочной игры Бум. Затем родилась идея игры с похожей механикой, но вместо слов - случайные фильмы с кинопоиска. В качестве основы проекта я взял 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¬_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