All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 54s
Neue Komponente src/lib/components/SearchLoader.svelte ersetzt die stumpfen "Suche läuft …"-Zeilen an allen vier Stellen: - Startseite (scope=local und scope=web) - Header-Dropdown (size=sm, beide Scopes) Was passiert: - Ein Pfannen-Emoji (🍳 → 🥘 → 🍲 → 🍜 → 🥣) wechselt alle 900 ms - Wobble-Animation kippt es im 1.4-s-Takt hin und her - Drei Dampf-Punkte steigen zeitversetzt auf und fadeen - Caption unten rotiert alle 1.8 s durch vier passende Sprüche (lokal: "Stöbere im Rezeptbuch …", web: "Schnuppere in fremden Küchen …" etc.) Zwei Size-Varianten: md (Homepage) und sm (Header-Dropdown).
181 lines
3.7 KiB
Svelte
181 lines
3.7 KiB
Svelte
<script lang="ts">
|
|
import { onMount, onDestroy } from 'svelte';
|
|
|
|
type Scope = 'local' | 'web';
|
|
type Size = 'sm' | 'md';
|
|
let { scope = 'local', size = 'md' }: { scope?: Scope; size?: Size } = $props();
|
|
|
|
const LOCAL_MESSAGES = [
|
|
'Stöbere im Rezeptbuch …',
|
|
'Schaue unter den Topfdeckeln …',
|
|
'Krame in den Gewürzregalen …',
|
|
'Durchsuche Omas Geheimrezepte …'
|
|
];
|
|
const WEB_MESSAGES = [
|
|
'Schnuppere in fremden Küchen …',
|
|
'Befrage Chefkoch, Emmi und Co. …',
|
|
'Durchforste die Kochblog-Gassen …',
|
|
'Klopfe an Internet-Kochtöpfe …'
|
|
];
|
|
|
|
const EMOJIS = ['🍳', '🥘', '🍲', '🍜', '🥣'];
|
|
|
|
const messages = $derived(scope === 'web' ? WEB_MESSAGES : LOCAL_MESSAGES);
|
|
let msgIdx = $state(0);
|
|
let emojiIdx = $state(0);
|
|
|
|
let msgTimer: ReturnType<typeof setInterval> | null = null;
|
|
let emojiTimer: ReturnType<typeof setInterval> | null = null;
|
|
|
|
onMount(() => {
|
|
msgTimer = setInterval(() => {
|
|
msgIdx = (msgIdx + 1) % messages.length;
|
|
}, 1800);
|
|
emojiTimer = setInterval(() => {
|
|
emojiIdx = (emojiIdx + 1) % EMOJIS.length;
|
|
}, 900);
|
|
});
|
|
|
|
onDestroy(() => {
|
|
if (msgTimer) clearInterval(msgTimer);
|
|
if (emojiTimer) clearInterval(emojiTimer);
|
|
});
|
|
</script>
|
|
|
|
<div class="loader" class:sm={size === 'sm'}>
|
|
<div class="pot-wrap" aria-hidden="true">
|
|
<span class="steam s1">·</span>
|
|
<span class="steam s2">·</span>
|
|
<span class="steam s3">·</span>
|
|
<span class="pot">{EMOJIS[emojiIdx]}</span>
|
|
</div>
|
|
<p class="caption" aria-live="polite">{messages[msgIdx]}</p>
|
|
</div>
|
|
|
|
<style>
|
|
.loader {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 1.75rem 0;
|
|
}
|
|
.loader.sm {
|
|
padding: 0.85rem 0;
|
|
gap: 0.35rem;
|
|
}
|
|
.pot-wrap {
|
|
position: relative;
|
|
width: 80px;
|
|
height: 80px;
|
|
}
|
|
.loader.sm .pot-wrap {
|
|
width: 50px;
|
|
height: 50px;
|
|
}
|
|
.pot {
|
|
font-size: 2.8rem;
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
transform-origin: 50% 85%;
|
|
animation: wobble 1.4s ease-in-out infinite;
|
|
display: inline-block;
|
|
}
|
|
.loader.sm .pot {
|
|
font-size: 1.8rem;
|
|
}
|
|
.steam {
|
|
font-size: 1.7rem;
|
|
font-weight: 900;
|
|
color: #8fb097;
|
|
position: absolute;
|
|
bottom: 55%;
|
|
opacity: 0;
|
|
line-height: 1;
|
|
}
|
|
.s1 {
|
|
left: 22%;
|
|
animation: rise 2.4s ease-out infinite;
|
|
}
|
|
.s2 {
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
animation: rise 2.4s ease-out infinite 0.6s;
|
|
}
|
|
.s3 {
|
|
left: 72%;
|
|
animation: rise 2.4s ease-out infinite 1.2s;
|
|
}
|
|
@keyframes wobble {
|
|
0%,
|
|
100% {
|
|
transform: translateX(-50%) rotate(-7deg);
|
|
}
|
|
50% {
|
|
transform: translateX(-50%) rotate(7deg);
|
|
}
|
|
}
|
|
@keyframes rise {
|
|
0% {
|
|
opacity: 0;
|
|
transform: translate(-50%, 0) scale(0.6);
|
|
}
|
|
25% {
|
|
opacity: 0.9;
|
|
}
|
|
100% {
|
|
opacity: 0;
|
|
transform: translate(-50%, -34px) scale(1.6);
|
|
}
|
|
}
|
|
.s1,
|
|
.s3 {
|
|
transform: none;
|
|
}
|
|
.s1 {
|
|
animation-name: rise-left;
|
|
}
|
|
.s3 {
|
|
animation-name: rise-right;
|
|
}
|
|
@keyframes rise-left {
|
|
0% {
|
|
opacity: 0;
|
|
transform: translate(0, 0) scale(0.6);
|
|
}
|
|
25% {
|
|
opacity: 0.9;
|
|
}
|
|
100% {
|
|
opacity: 0;
|
|
transform: translate(-8px, -34px) scale(1.5);
|
|
}
|
|
}
|
|
@keyframes rise-right {
|
|
0% {
|
|
opacity: 0;
|
|
transform: translate(0, 0) scale(0.6);
|
|
}
|
|
25% {
|
|
opacity: 0.9;
|
|
}
|
|
100% {
|
|
opacity: 0;
|
|
transform: translate(8px, -34px) scale(1.5);
|
|
}
|
|
}
|
|
.caption {
|
|
color: #6a7670;
|
|
font-style: italic;
|
|
font-size: 0.95rem;
|
|
margin: 0;
|
|
min-height: 1.3em;
|
|
text-align: center;
|
|
}
|
|
.loader.sm .caption {
|
|
font-size: 0.85rem;
|
|
}
|
|
</style>
|