Ergebnis-Ansicht: stufenpassende Animationen um die Highscore-Karten statt Regenbogen
This commit is contained in:
parent
b54effef25
commit
60a0fe4297
139
src/lib/components/home/StageAmbience.svelte
Normal file
139
src/lib/components/home/StageAmbience.svelte
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Stage } from '../../game/stages';
|
||||||
|
import Balloon from '../svg/Balloon.svelte';
|
||||||
|
import Cloud from '../svg/Cloud.svelte';
|
||||||
|
import Moon from '../svg/Moon.svelte';
|
||||||
|
import Star from '../svg/Star.svelte';
|
||||||
|
import Ground from '../svg/Ground.svelte';
|
||||||
|
|
||||||
|
let { stage }: { stage: Stage } = $props();
|
||||||
|
|
||||||
|
// Deko-Positionen rahmen die zentrierte Highscore-Spalte ein (Mitte bleibt frei).
|
||||||
|
// l/t/b in %, s = Symbolgröße, d = Animationsdauer (s), dl = Verzögerung (s).
|
||||||
|
type Deco = { l: number; t?: number; b?: number; s: number; d: number; dl: number };
|
||||||
|
|
||||||
|
const balloons: Deco[] = [
|
||||||
|
{ l: 9, t: 60, s: 66, d: 4.5, dl: 0 },
|
||||||
|
{ l: 21, t: 26, s: 46, d: 5.2, dl: 0.6 },
|
||||||
|
{ l: 82, t: 54, s: 72, d: 4.0, dl: 0.3 },
|
||||||
|
{ l: 90, t: 22, s: 50, d: 5.6, dl: 0.9 },
|
||||||
|
{ l: 5, t: 36, s: 42, d: 4.8, dl: 1.2 },
|
||||||
|
{ l: 74, t: 80, s: 40, d: 5.0, dl: 0.4 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const clouds: Deco[] = [
|
||||||
|
{ l: 3, t: 15, s: 96, d: 9, dl: 0 },
|
||||||
|
{ l: 58, t: 22, s: 120, d: 11, dl: 1 },
|
||||||
|
{ l: 11, t: 72, s: 84, d: 10, dl: 0.5 },
|
||||||
|
{ l: 70, t: 76, s: 104, d: 8.5, dl: 1.5 },
|
||||||
|
{ l: 83, t: 44, s: 76, d: 9.5, dl: 0.8 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const moonStars: Deco[] = [
|
||||||
|
{ l: 14, t: 24, s: 24, d: 2.2, dl: 0 },
|
||||||
|
{ l: 24, t: 58, s: 18, d: 2.8, dl: 0.5 },
|
||||||
|
{ l: 8, t: 74, s: 22, d: 2.4, dl: 0.9 },
|
||||||
|
{ l: 88, t: 60, s: 20, d: 2.6, dl: 0.3 },
|
||||||
|
{ l: 18, t: 40, s: 14, d: 3.0, dl: 1.1 },
|
||||||
|
{ l: 72, t: 30, s: 16, d: 2.5, dl: 0.7 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const stars: Deco[] = [
|
||||||
|
{ l: 10, t: 18, s: 28, d: 2.2, dl: 0 },
|
||||||
|
{ l: 22, t: 46, s: 20, d: 2.6, dl: 0.6 },
|
||||||
|
{ l: 8, t: 70, s: 24, d: 2.4, dl: 1.0 },
|
||||||
|
{ l: 30, t: 80, s: 16, d: 3.0, dl: 0.3 },
|
||||||
|
{ l: 90, t: 20, s: 26, d: 2.3, dl: 0.8 },
|
||||||
|
{ l: 78, t: 46, s: 18, d: 2.7, dl: 0.2 },
|
||||||
|
{ l: 92, t: 70, s: 22, d: 2.5, dl: 1.2 },
|
||||||
|
{ l: 70, t: 80, s: 16, d: 2.9, dl: 0.5 },
|
||||||
|
{ l: 16, t: 32, s: 14, d: 3.1, dl: 1.4 },
|
||||||
|
{ l: 84, t: 34, s: 14, d: 2.8, dl: 0.9 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const grass: Deco[] = [
|
||||||
|
{ l: 7, b: 2, s: 48, d: 3.0, dl: 0 },
|
||||||
|
{ l: 23, b: 2, s: 38, d: 3.4, dl: 0.4 },
|
||||||
|
{ l: 69, b: 2, s: 44, d: 2.8, dl: 0.2 },
|
||||||
|
{ l: 86, b: 2, s: 36, d: 3.2, dl: 0.6 },
|
||||||
|
{ l: 47, b: 2, s: 34, d: 3.0, dl: 0.5 },
|
||||||
|
];
|
||||||
|
|
||||||
|
function pos(d: Deco): string {
|
||||||
|
const vert = d.b != null ? `bottom:${d.b}%` : `top:${d.t}%`;
|
||||||
|
return `left:${d.l}%; ${vert}; --dur:${d.d}s; animation-delay:${d.dl}s`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="ambience" aria-hidden="true">
|
||||||
|
{#if stage === 1}
|
||||||
|
{#each balloons as d, i (i)}
|
||||||
|
<span class="deco bob" style={pos(d)}><Balloon size={d.s} /></span>
|
||||||
|
{/each}
|
||||||
|
{:else if stage === 2}
|
||||||
|
{#each clouds as d, i (i)}
|
||||||
|
<span class="deco drift" style={pos(d)}><Cloud size={d.s} opacity={0.92} /></span>
|
||||||
|
{/each}
|
||||||
|
{:else if stage === 3}
|
||||||
|
<span class="deco glow" style="left:80%; top:14%; --dur:5s; animation-delay:0s"><Moon size={86} /></span>
|
||||||
|
{#each moonStars as d, i (i)}
|
||||||
|
<span class="deco twinkle" style={pos(d)}><Star size={d.s} color="#fff7c8" /></span>
|
||||||
|
{/each}
|
||||||
|
{:else if stage >= 4}
|
||||||
|
{#each stars as d, i (i)}
|
||||||
|
<span class="deco twinkle" style={pos(d)}><Star size={d.s} /></span>
|
||||||
|
{/each}
|
||||||
|
{:else}
|
||||||
|
{#each grass as d, i (i)}
|
||||||
|
<span class="deco sway" style={pos(d)}><Ground size={d.s} /></span>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.ambience {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.deco {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bob { animation: bob var(--dur, 4s) ease-in-out infinite alternate; }
|
||||||
|
@keyframes bob {
|
||||||
|
from { transform: translateY(8px) rotate(-3deg); }
|
||||||
|
to { transform: translateY(-16px) rotate(3deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.drift { animation: drift var(--dur, 9s) ease-in-out infinite alternate; }
|
||||||
|
@keyframes drift {
|
||||||
|
from { transform: translateX(-26px); }
|
||||||
|
to { transform: translateX(26px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.twinkle { animation: twinkle var(--dur, 2.4s) ease-in-out infinite; }
|
||||||
|
@keyframes twinkle {
|
||||||
|
0%, 100% { opacity: 0.3; transform: scale(0.75); }
|
||||||
|
50% { opacity: 1; transform: scale(1.15); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.glow { animation: glow var(--dur, 5s) ease-in-out infinite alternate; }
|
||||||
|
@keyframes glow {
|
||||||
|
from { filter: drop-shadow(0 0 4px rgba(255, 233, 163, 0.4)); transform: translateY(0); }
|
||||||
|
to { filter: drop-shadow(0 0 16px rgba(255, 233, 163, 0.9)); transform: translateY(-8px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.sway { animation: sway var(--dur, 3s) ease-in-out infinite alternate; transform-origin: bottom center; }
|
||||||
|
@keyframes sway {
|
||||||
|
from { transform: rotate(-7deg); }
|
||||||
|
to { transform: rotate(7deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.deco { animation: none; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
let { size = 200 }: { size?: number } = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svg width={size} height={size * 0.6} viewBox="0 0 200 120" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
||||||
<g fill="none" stroke-width="14" stroke-linecap="round">
|
|
||||||
<path d="M20 110 A80 80 0 0 1 180 110" stroke="var(--c-rainbow-1)" />
|
|
||||||
<path d="M34 110 A66 66 0 0 1 166 110" stroke="var(--c-rainbow-2)" />
|
|
||||||
<path d="M48 110 A52 52 0 0 1 152 110" stroke="var(--c-rainbow-3)" />
|
|
||||||
<path d="M62 110 A38 38 0 0 1 138 110" stroke="var(--c-rainbow-4)" />
|
|
||||||
<path d="M76 110 A24 24 0 0 1 124 110" stroke="var(--c-rainbow-5)" />
|
|
||||||
<path d="M90 110 A10 10 0 0 1 110 110" stroke="var(--c-rainbow-6)" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
import { startGame } from '../stores/game';
|
import { startGame } from '../stores/game';
|
||||||
import { goHome, goGame } from '../stores/route';
|
import { goHome, goGame } from '../stores/route';
|
||||||
import { play, unlockAudio } from '../audio/soundManager';
|
import { play, unlockAudio } from '../audio/soundManager';
|
||||||
import Rainbow from '../components/svg/Rainbow.svelte';
|
|
||||||
import HighscoreColumn from '../components/home/HighscoreColumn.svelte';
|
import HighscoreColumn from '../components/home/HighscoreColumn.svelte';
|
||||||
|
import StageAmbience from '../components/home/StageAmbience.svelte';
|
||||||
|
|
||||||
type Props = { target: Target };
|
type Props = { target: Target };
|
||||||
let { target }: Props = $props();
|
let { target }: Props = $props();
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
const current = $derived($lastRun?.target === target ? $lastRun : null);
|
const current = $derived($lastRun?.target === target ? $lastRun : null);
|
||||||
const lastRunDate = $derived(current?.date ?? null);
|
const lastRunDate = $derived(current?.date ?? null);
|
||||||
const lastStage = $derived(current?.stage ?? 0);
|
const lastStage = $derived(current?.stage ?? 0);
|
||||||
const isWin = $derived(lastStage >= 4);
|
|
||||||
|
|
||||||
function retry() {
|
function retry() {
|
||||||
unlockAudio();
|
unlockAudio();
|
||||||
|
|
@ -32,11 +31,7 @@
|
||||||
|
|
||||||
<div class="screen" in:fade={{ duration: 240 }}>
|
<div class="screen" in:fade={{ duration: 240 }}>
|
||||||
<div class="celebration">
|
<div class="celebration">
|
||||||
{#if isWin}
|
<StageAmbience stage={lastStage} />
|
||||||
<div class="rainbow-wrap" in:fly={{ y: 30, duration: 600 }}>
|
|
||||||
<Rainbow size={300} />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<div class="scores" in:fly={{ y: 40, duration: 500 }}>
|
<div class="scores" in:fly={{ y: 40, duration: 500 }}>
|
||||||
<HighscoreColumn {target} highlightDate={lastRunDate} />
|
<HighscoreColumn {target} highlightDate={lastRunDate} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -71,14 +66,6 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
.rainbow-wrap {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
z-index: 1;
|
|
||||||
opacity: 0.85;
|
|
||||||
}
|
|
||||||
.scores {
|
.scores {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user