feat: migrate frontend React 18 → Svelte 5 + SvelteKit
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 22s
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 22s
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)
This commit is contained in:
50
Frontend/src/lib/components/ClickParticles.svelte
Normal file
50
Frontend/src/lib/components/ClickParticles.svelte
Normal file
@@ -0,0 +1,50 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user