Files
ClickerZ/Frontend/src/lib/components/ClickParticles.svelte
Tetardtek f6bff6e389
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 22s
feat: migrate frontend React 18 → Svelte 5 + SvelteKit
Core logic portable (economy, balance, cosmetics, migrateSave) — zero rewrite.
136 tests green, identiques. Backend inchangé.

- Svelte 5 runes stores (game, auth, toast) remplacent Zustand
- SvelteKit adapter-static SPA (dist/ output, fallback index.html)
- Tailwind v4 conservé, design system .gp-* porté
- Transitions natives : slide, fly, scale, fade sur toute l'UI
- Sidebar tabbée (Production/Evolution/Collection) + CollapsiblePanel
- Mobile bottom sheet avec FAB toggle + backdrop blur
- Click particles réactifs Svelte (plus de DOM impératif)
- TadpoleSprite bounce + glow ring au clic
- Guide refait en accordéon, Achievements avec filtres
- a11y : focus-visible, Escape modals, aria-current, aria-labels
- CI/CD adapté (tests + build + rsync)
- Build 504K (vs ~1.2MB React)
2026-03-28 20:03:21 +01:00

51 lines
1.4 KiB
Svelte

<script lang="ts">
import { fly, fade } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
import { formatNumber } from '$lib/utils/formatNumber';
interface Particle {
id: number;
x: number;
y: number;
gain: number;
isDouble: boolean;
isCrit: boolean;
}
let particles = $state<Particle[]>([]);
let nextId = 0;
export function spawn(x: number, y: number, gain: number, isDouble: boolean, isCrit: boolean) {
const id = nextId++;
// Random horizontal spread
const offsetX = (Math.random() - 0.5) * 40;
particles = [...particles, { id, x: x + offsetX, y, gain, isDouble, isCrit }];
setTimeout(() => {
particles = particles.filter(p => p.id !== id);
}, 1000);
}
</script>
<div class="fixed inset-0 pointer-events-none z-[100]">
{#each particles as p (p.id)}
{@const prefix = p.isCrit ? 'CRIT ' : p.isDouble ? 'x2 ' : ''}
{@const color = p.isCrit ? '#f59e0b' : p.isDouble ? '#a78bfa' : '#34d399'}
{@const size = p.isCrit ? '2rem' : p.isDouble ? '1.8rem' : '1.6rem'}
<span
class="absolute font-extrabold"
style="
left: {p.x}px;
top: {p.y}px;
color: {color};
font-size: {size};
font-family: var(--font);
text-shadow: 0 0 10px {color}60, 0 2px 6px rgba(0,0,0,0.7);
"
in:fly={{ y: 0, duration: 50 }}
out:fly={{ y: -90, duration: 900, easing: quintOut, opacity: 0 }}
>
{prefix}+{formatNumber(p.gain)}
</span>
{/each}
</div>