В интерфейсах на базе SvelteKit визуальные индикаторы загрузки выполняют две ключевые задачи: информирование о процессе ожидания и снижение субъективного ощущения задержки. Наиболее распространённые подходы — spinner (индикатор вращения) и skeleton loaders (скелетные заглушки). Они отличаются по восприятию и сценариям применения.
Обе категории активно реализуются в UI-библиотеках для SvelteKit: через компоненты, директивы или CSS-утилиты.
Spinner представляет собой анимированный элемент, обычно реализуемый через CSS:
<div class="spinner"></div>
<style>
.spinner {
width: 40px;
height: 40px;
border: 4px solid rgba(0,0,0,0.1);
border-top-color: #333;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>
Spinner часто применяется в связке с реактивными переменными:
<script>
let loading = true;
async function loadData() {
loading = true;
await fetch('/api/data');
loading = false;
}
</script>
{#if loading}
<Spinner />
{:else}
<Content />
{/if}
В SvelteKit загрузка данных происходит через load:
export async function load({ fetch }) {
const res = await fetch('/api/data');
const data = await res.json();
return { data };
}
Spinner используется на уровне компонента страницы, пока данные не доступны.
Компонент:
<Spinner size="lg" />
Особенности:
<Spinner color="blue" size="6" />
Поддержка:
<Loading active={true} />
Особенности:
Skeleton loader отображает структурную модель контента:
Это снижает когнитивную нагрузку, так как пользователь «видит», что именно загружается.
<div class="skeleton"></div>
<style>
.skeleton {
width: 100%;
height: 16px;
background: linear-gradient(
90deg,
#eee 25%,
#ddd 37%,
#eee 63%
);
background-size: 400% 100%;
animation: shimmer 1.4s ease infinite;
}
@keyframes shimmer {
100% {
background-position: -400% 0;
}
}
</style>
Реалистичные skeleton-интерфейсы создаются через композицию:
<div class="card">
<div class="image skeleton"></div>
<div class="text skeleton"></div>
<div class="text skeleton short"></div>
</div>
<style>
.image {
height: 150px;
}
.text {
height: 16px;
margin-top: 8px;
}
.short {
width: 60%;
}
</style>
{#if loading}
<SkeletonCard />
{:else}
<RealCard {data} />
{/if}
Skeleton-элементы должны максимально повторять структуру итогового компонента.
<Skeleton height="20px" width="100%" />
Преимущества:
<Skeleton class="w-full h-4" />
Особенности:
<LinearProgress indeterminate />
Хотя это не классический skeleton, используется как альтернатива.
| Критерий | Spinner | Skeleton loader |
|---|---|---|
| Информативность | Низкая | Высокая |
| Восприятие скорости | Медленнее | Быстрее |
| Сложность реализации | Простая | Средняя |
| UX | Базовый | Продвинутый |
Комбинация двух подходов:
{#if loading}
<SkeletonList />
<Spinner small />
{:else}
<Content />
{/if}
Применяется для:
В SvelteKit часто используются store:
import { writable } from 'svelte/store';
export const isLoading = writable(false);
Использование:
<script>
import { isLoading } from './store';
</script>
{#if $isLoading}
<Spinner />
{/if}
<div role="status" aria-live="polite">
Loading...
</div>
aria-hidden="true"Skeleton loaders:
Решения:
svelte-virtual)Типы анимаций:
Пример pulse:
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.4; }
100% { opacity: 1; }
}
В SvelteKit:
Комбинированный подход:
{#if data}
<Content />
{:else}
<Skeleton />
{/if}
<div class="animate-pulse bg-gray-300 h-4 w-full rounded"></div>
Преимущества:
<SkeletonCard />
<SkeletonList count={5} />
<Card loading={true} />
Внутри:
{#if loading}
<Skeleton />
{:else}
<RealContent />
{/if}
Такой подход обеспечивает предсказуемый UX и масштабируемость интерфейса в приложениях на SvelteKit.