Boden-Stufe: durchgehende Wiese mit Blumen und flatternden Schmetterlingen statt schwebender Fragmente
This commit is contained in:
parent
2adcd0d545
commit
72aa9837ce
|
|
@ -4,7 +4,8 @@
|
|||
import Cloud from '../svg/Cloud.svelte';
|
||||
import Moon from '../svg/Moon.svelte';
|
||||
import Star from '../svg/Star.svelte';
|
||||
import Ground from '../svg/Ground.svelte';
|
||||
import Meadow from '../svg/Meadow.svelte';
|
||||
import Butterfly from '../svg/Butterfly.svelte';
|
||||
|
||||
let { stage }: { stage: Stage } = $props();
|
||||
|
||||
|
|
@ -57,15 +58,6 @@
|
|||
{ l: 93, t: 61, s: 30, sc: 3, rot: 10, d: 2.5, dl: 0.2 },
|
||||
];
|
||||
|
||||
const grass: Deco[] = [
|
||||
{ l: 5, b: 1, s: 66, d: 2.4, dl: 0 },
|
||||
{ l: 19, b: 2, s: 48, flip: true, d: 2.8, dl: 0.4 },
|
||||
{ l: 33, b: 1, s: 56, d: 2.2, dl: 0.7 },
|
||||
{ l: 63, b: 2, s: 50, flip: true, d: 2.6, dl: 0.2 },
|
||||
{ l: 78, b: 1, s: 70, d: 2.3, dl: 0.9 },
|
||||
{ l: 90, b: 2, s: 46, flip: true, d: 2.7, dl: 0.5 },
|
||||
];
|
||||
|
||||
function baseStyle(d: Deco): string {
|
||||
const vert = d.b != null ? `bottom:${d.b}%` : `top:${d.t}%`;
|
||||
return `left:${d.l}%; ${vert}; transform: scaleX(${d.flip ? -1 : 1}) rotate(${d.rot ?? 0}deg);`;
|
||||
|
|
@ -98,11 +90,10 @@
|
|||
</span>
|
||||
{/each}
|
||||
{:else}
|
||||
{#each grass as d, i (i)}
|
||||
<span class="deco" style={baseStyle(d)}>
|
||||
<span class="anim sway" style={fx(d)}><Ground size={d.s} /></span>
|
||||
</span>
|
||||
{/each}
|
||||
<!-- Boden-Stufe: durchgehende Wiese unten + flatternde Schmetterlinge. -->
|
||||
<Meadow />
|
||||
<span class="butterfly b1"><Butterfly size={50} color="#ff8aa8" stroke="#c25578" flap={0.45} /></span>
|
||||
<span class="butterfly b2"><Butterfly size={40} color="#8ad1ff" stroke="#3a82c2" flap={0.55} /></span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -139,10 +130,22 @@
|
|||
50% { opacity: 1; transform: scale(1.35); }
|
||||
}
|
||||
|
||||
.sway { animation: sway var(--dur, 2.5s) ease-in-out infinite alternate; transform-origin: bottom center; }
|
||||
@keyframes sway {
|
||||
from { transform: rotate(-12deg); }
|
||||
to { transform: rotate(12deg); }
|
||||
.butterfly { position: absolute; }
|
||||
.b1 { animation: fly1 9s ease-in-out infinite; }
|
||||
.b2 { animation: fly2 11s ease-in-out infinite; }
|
||||
@keyframes fly1 {
|
||||
0% { left: 12%; top: 62%; }
|
||||
25% { left: 24%; top: 44%; }
|
||||
50% { left: 35%; top: 56%; }
|
||||
75% { left: 20%; top: 50%; }
|
||||
100% { left: 12%; top: 62%; }
|
||||
}
|
||||
@keyframes fly2 {
|
||||
0% { left: 64%; top: 42%; }
|
||||
25% { left: 80%; top: 58%; }
|
||||
50% { left: 88%; top: 46%; }
|
||||
75% { left: 72%; top: 38%; }
|
||||
100% { left: 64%; top: 42%; }
|
||||
}
|
||||
|
||||
.moon {
|
||||
|
|
@ -179,6 +182,6 @@
|
|||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.anim, .moon { animation: none; }
|
||||
.anim, .moon, .butterfly { animation: none; }
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
46
src/lib/components/svg/Butterfly.svelte
Normal file
46
src/lib/components/svg/Butterfly.svelte
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<script lang="ts">
|
||||
// Schmetterling von oben: zwei Flügelpaare, schmaler Körper, Fühler — keine Gesichter.
|
||||
// Die Flügel flattern (scaleX-Puls um die Körpermitte).
|
||||
let {
|
||||
size = 48,
|
||||
color = '#ff8aa8',
|
||||
stroke = '#c25578',
|
||||
flap = 0.5,
|
||||
}: { size?: number; color?: string; stroke?: string; flap?: number } = $props();
|
||||
</script>
|
||||
|
||||
<svg
|
||||
width={size}
|
||||
height={size * 0.85}
|
||||
viewBox="0 0 64 54"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
style="--flap:{flap}s"
|
||||
>
|
||||
<g class="wings" {stroke} stroke-width="1.5">
|
||||
<ellipse cx="19" cy="19" rx="16" ry="13" fill={color} />
|
||||
<ellipse cx="45" cy="19" rx="16" ry="13" fill={color} />
|
||||
<ellipse cx="23" cy="37" rx="11" ry="10" fill={color} />
|
||||
<ellipse cx="41" cy="37" rx="11" ry="10" fill={color} />
|
||||
<circle cx="17" cy="18" r="3.5" fill="#ffffff" opacity="0.5" stroke="none" />
|
||||
<circle cx="47" cy="18" r="3.5" fill="#ffffff" opacity="0.5" stroke="none" />
|
||||
</g>
|
||||
<ellipse cx="32" cy="28" rx="2.6" ry="15" fill="#4a3a2a" />
|
||||
<path d="M32 14 Q28 6 23 5" stroke="#4a3a2a" stroke-width="1.5" fill="none" stroke-linecap="round" />
|
||||
<path d="M32 14 Q36 6 41 5" stroke="#4a3a2a" stroke-width="1.5" fill="none" stroke-linecap="round" />
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
.wings {
|
||||
transform-box: fill-box;
|
||||
transform-origin: center;
|
||||
animation: flap var(--flap, 0.5s) ease-in-out infinite;
|
||||
}
|
||||
@keyframes flap {
|
||||
0%, 100% { transform: scaleX(1); }
|
||||
50% { transform: scaleX(0.55); }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.wings { animation: none; }
|
||||
}
|
||||
</style>
|
||||
95
src/lib/components/svg/Meadow.svelte
Normal file
95
src/lib/components/svg/Meadow.svelte
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<script lang="ts">
|
||||
// Wehende Wiese über die volle Breite: Erdschicht, zweilagige Grasnarbe mit welliger
|
||||
// Oberkante und darauf verteilte, sich wiegende Grashalme und Blümchen.
|
||||
type Tuft = {
|
||||
l: number; type: 'blade' | 'flower'; color: string; h: number; d: number; dl: number; flip: boolean;
|
||||
};
|
||||
const tufts: Tuft[] = [
|
||||
{ l: 5, type: 'blade', color: '#4ea34e', h: 38, d: 2.6, dl: 0, flip: false },
|
||||
{ l: 13, type: 'flower', color: '#ff7aa8', h: 44, d: 3.0, dl: 0.5, flip: false },
|
||||
{ l: 23, type: 'blade', color: '#3f8f3f', h: 30, d: 2.4, dl: 0.8, flip: true },
|
||||
{ l: 33, type: 'flower', color: '#ffd24a', h: 48, d: 3.2, dl: 0.2, flip: false },
|
||||
{ l: 44, type: 'blade', color: '#4ea34e', h: 34, d: 2.7, dl: 1.0, flip: false },
|
||||
{ l: 55, type: 'flower', color: '#c79aff', h: 42, d: 2.9, dl: 0.4, flip: true },
|
||||
{ l: 65, type: 'blade', color: '#3f8f3f', h: 32, d: 2.5, dl: 0.7, flip: false },
|
||||
{ l: 75, type: 'flower', color: '#ff9d5c', h: 46, d: 3.1, dl: 0.1, flip: false },
|
||||
{ l: 85, type: 'blade', color: '#4ea34e', h: 36, d: 2.6, dl: 0.9, flip: true },
|
||||
{ l: 93, type: 'flower', color: '#ff7aa8', h: 38, d: 2.8, dl: 0.3, flip: false },
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="meadow" aria-hidden="true">
|
||||
<div class="soil"></div>
|
||||
<svg class="grass back" preserveAspectRatio="none" viewBox="0 0 100 24">
|
||||
<path d="M0 24 L0 13 Q5 6 10 13 Q15 6 20 13 Q25 6 30 13 Q35 6 40 13 Q45 6 50 13 Q55 6 60 13 Q65 6 70 13 Q75 6 80 13 Q85 6 90 13 Q95 6 100 13 L100 24 Z" />
|
||||
</svg>
|
||||
<svg class="grass front" preserveAspectRatio="none" viewBox="0 0 100 24">
|
||||
<path d="M0 24 L0 15 Q4 9 9 15 Q14 9 19 15 Q24 9 29 15 Q34 9 39 15 Q44 9 49 15 Q54 9 59 15 Q64 9 69 15 Q74 9 79 15 Q84 9 89 15 Q94 9 99 15 L100 24 Z" />
|
||||
</svg>
|
||||
|
||||
{#each tufts as t, i (i)}
|
||||
<span class="tuft" style="left:{t.l}%; transform: scaleX({t.flip ? -1 : 1});">
|
||||
<span class="sway" style="--dur:{t.d}s; animation-delay:{t.dl}s;">
|
||||
{#if t.type === 'blade'}
|
||||
<svg width={t.h * 0.45} height={t.h} viewBox="0 0 18 40" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 40 Q3 22 8 2 Q12 22 11 40 Z" fill={t.color} />
|
||||
</svg>
|
||||
{:else}
|
||||
<svg width={t.h * 0.6} height={t.h} viewBox="0 0 24 40" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 40 L12 18" stroke="#3f8f3f" stroke-width="2.5" />
|
||||
<circle cx="12" cy="11" r="6" fill={t.color} />
|
||||
<circle cx="6" cy="13" r="4.5" fill={t.color} />
|
||||
<circle cx="18" cy="13" r="4.5" fill={t.color} />
|
||||
<circle cx="9" cy="6" r="4.5" fill={t.color} />
|
||||
<circle cx="15" cy="6" r="4.5" fill={t.color} />
|
||||
<circle cx="12" cy="10" r="3" fill="#fff3b0" />
|
||||
</svg>
|
||||
{/if}
|
||||
</span>
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.meadow {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 128px;
|
||||
}
|
||||
.soil {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 60px;
|
||||
background: linear-gradient(to top, #6e4527, #9c6a3c);
|
||||
}
|
||||
.grass {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.grass path { fill: currentColor; }
|
||||
.grass.back { bottom: 52px; height: 44px; color: #3f8f3f; }
|
||||
.grass.front { bottom: 44px; height: 44px; color: #5fb85f; }
|
||||
|
||||
.tuft {
|
||||
position: absolute;
|
||||
bottom: 58px;
|
||||
}
|
||||
.sway {
|
||||
display: inline-block;
|
||||
transform-origin: bottom center;
|
||||
animation: sway var(--dur, 2.6s) ease-in-out infinite alternate;
|
||||
}
|
||||
@keyframes sway {
|
||||
from { transform: rotate(-9deg); }
|
||||
to { transform: rotate(9deg); }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.sway { animation: none; }
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user