Cookie Clicker - Apprendre Svelte par la pratique
Vue d’ensemble
Section titled “Vue d’ensemble”Objectif : Maîtriser les fondamentaux de Svelte 5 en construisant un cookie clicker interactif
Pourquoi un cookie clicker ?
- Concept simple mais extensible
- Démontre les principes clés : réactivité, événements, état, boucles
- Permet de progresser du basique au complexe
- Chaque niveau apporte de nouveaux concepts
Format : Atelier progressif avec deux niveaux de complexité
Cookie Clicker - Niveau 1
Section titled “Cookie Clicker - Niveau 1”Étape 1 : Setup du projet
Section titled “Étape 1 : Setup du projet”Si vous n’avez pas encore de projet Svelte, créez-en un :
npm create vite@latest mon-cookie-clicker -- --template sveltecd mon-cookie-clickernpm installnpm run devExplication :
npm create vite@latest: crée un nouveau projet avec Vite (outil de build ultra-rapide)mon-cookie-clicker: nom de votre dossier projet--template svelte: configure le projet pour Sveltecd mon-cookie-clicker: entre dans le dossiernpm install: installe les dépendancesnpm run dev: démarre le serveur de développement (accès viahttp://localhost:5173)
Le terminal affichera l’URL locale. Votre navigateur recharge automatiquement quand vous modifiez le code.
Si vous avez déjà un projet Svelte :
Vérifiez que le serveur tourne :
- Terminal ouvert
- Serveur Vite actif (
npm run dev) - Éditeur de code ouvert (VSCode/PhpStorm)
Si besoin de créer un nouveau composant :
macOS/Linux :
mkdir -p src/libtouch src/lib/CookieClicker.svelteWindows PowerShell :
mkdir src\lib -ForceNew-Item src\lib\CookieClicker.svelteAlternative : Créer le fichier directement dans l’éditeur.
Étape 2 : Cookie Clicker basique
Section titled “Étape 2 : Cookie Clicker basique”Créer src/lib/CookieClicker.svelte :
{cookieClickerNiveau1}Utiliser le composant dans src/App.svelte :
<script> import CookieClicker from './lib/CookieClicker.svelte';</script>
<main> <CookieClicker /></main>
<style> main { padding: 2rem; }</style>Étape 3 : Explication des concepts
Section titled “Étape 3 : Explication des concepts”1. Réactivité automatique avec $state()
let cookies = $state(0); // Variable réactive avec la rune $state()
function cliquerCookie() { cookies += 1; // Simple assignation → interface mise à jour automatiquement}La rune $state() rend la variable réactive. Pas besoin de setState, this.$set, ou autre. Une simple assignation suffit.
2. Événements
<button on:click={cliquerCookie}>Syntax : on:nomEvenement={fonction}
Autres événements utiles :
on:click: clicon:dblclick: double-clicon:mouseover: survolon:input: saisie de texteon:keydown: touche clavier
3. Conditions avec {#if} (inchangé)
{#if cookies < 10} <p>Message pour débutant</p>{:else if cookies < 50} <p>Message intermédiaire</p>{:else} <p>Message expert</p>{/if}Syntax :
{#if condition}: ouvre le bloc conditionnel{:else if condition}: condition alternative{:else}: sinon{/if}: ferme le bloc
Cookie Clicker - Niveau 2
Section titled “Cookie Clicker - Niveau 2”Étape 1 : Créer le composant Upgrade
Section titled “Étape 1 : Créer le composant Upgrade”Créer src/lib/Upgrade.svelte :
<script> // Props : données passées par le composant parent (Svelte 5 rune) let { nom, // Nom de l'upgrade prix, // Prix en cookies gain, // Cookies par seconde gagnés quantite, // Nombre possédé cookiesActuels, // Pour savoir si on peut acheter onacheter // Callback au lieu d'événement custom } = $props();
// Réactivité dérivée avec $derived() (Svelte 5) let peutAcheter = $derived(cookiesActuels >= prix);</script>
<div class="upgrade" class:disabled={!peutAcheter}> <div class="info"> <h3>{nom}</h3> <p class="description">+{gain} 🍪/sec</p> <p class="quantite">Possédé : {quantite}</p> </div>
<button class="acheter" on:click={onacheter} disabled={!peutAcheter} > {prix} 🍪 </button></div>
<style> .upgrade { display: flex; justify-content: space-between; align-items: center; padding: 1rem; margin: 0.5rem 0; background: #f8f9fa; border-radius: 8px; border: 2px solid #dee2e6; transition: all 0.2s ease; }
.upgrade:hover { background: #e9ecef; }
.upgrade.disabled { opacity: 0.5; }
.info { text-align: left; }
h3 { margin: 0 0 0.25rem 0; color: #333; font-size: 1.1rem; }
.description { margin: 0.25rem 0; color: #666; font-size: 0.9rem; }
.quantite { margin: 0.25rem 0; color: #999; font-size: 0.85rem; }
.acheter { padding: 0.5rem 1rem; background: #e67e22; color: white; border: none; border-radius: 6px; font-weight: bold; cursor: pointer; font-size: 1rem; }
.acheter:hover:not(:disabled) { background: #d35400; }
.acheter:disabled { cursor: not-allowed; opacity: 0.5; }</style>Étape 2 : Mettre à jour CookieClicker
Section titled “Étape 2 : Mettre à jour CookieClicker”Modifier src/lib/CookieClicker.svelte :
<script> import { onMount } from 'svelte'; import Upgrade from './Upgrade.svelte';
// État du jeu (Svelte 5 runes) let cookies = $state(0); let cookiesParSeconde = $state(0);
// Liste des upgrades disponibles let upgrades = $state([ { id: 1, nom: 'Curseur', prix: 10, gain: 1, quantite: 0 }, { id: 2, nom: 'Grand-mère', prix: 50, gain: 5, quantite: 0 }, { id: 3, nom: 'Ferme à cookies', prix: 200, gain: 20, quantite: 0 }, { id: 4, nom: 'Usine', prix: 500, gain: 50, quantite: 0 }, ]);
// Fonction pour cliquer sur le cookie function cliquerCookie() { cookies += 1; }
// Fonction pour acheter un upgrade function acheterUpgrade(id) { // Trouver l'upgrade dans la liste const upgrade = upgrades.find(u => u.id === id);
// Vérifier si on a assez de cookies if (cookies >= upgrade.prix) { cookies -= upgrade.prix; // Déduire le prix upgrade.quantite += 1; // Incrémenter la quantité upgrade.prix = Math.floor(upgrade.prix * 1.5); // Augmenter le prix pour le prochain achat
// Recalculer les cookies par seconde calculerCookiesParSeconde(); } }
// Calculer le total de cookies par seconde function calculerCookiesParSeconde() { cookiesParSeconde = upgrades.reduce((total, upgrade) => { return total + (upgrade.gain * upgrade.quantite); }, 0); }
// Au montage du composant : démarrer le timer // En Svelte 5, onMount peut retourner une fonction de cleanup onMount(() => { const interval = setInterval(() => { cookies += cookiesParSeconde; }, 1000); // Chaque seconde
// Fonction de cleanup automatique return () => clearInterval(interval); });</script>
<div class="container"> <div class="game"> <!-- Colonne gauche : le cookie --> <div class="cookie-zone"> <h1>Cookie Clicker</h1>
<p class="score">🍪 {Math.floor(cookies)} cookies</p> <p class="rate">+{cookiesParSeconde} cookies/sec</p>
<button class="cookie" on:click={cliquerCookie}> 🍪 </button>
{#if cookies < 10} <p class="message">Cliquez sur le cookie !</p> {:else if cookiesParSeconde === 0} <p class="message">Achetez votre premier upgrade →</p> {:else} <p class="message">🎉 Votre empire grandit !</p> {/if} </div>
<!-- Colonne droite : les upgrades --> <div class="upgrades-zone"> <h2>Upgrades</h2>
{#each upgrades as upgrade (upgrade.id)} <Upgrade nom={upgrade.nom} prix={upgrade.prix} gain={upgrade.gain} quantite={upgrade.quantite} cookiesActuels={cookies} onacheter={() => acheterUpgrade(upgrade.id)} /> {/each} </div> </div></div>
<style> .container { max-width: 1000px; margin: 0 auto; padding: 2rem; }
.game { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; }
.cookie-zone { text-align: center; }
h1 { color: #333; margin-bottom: 1rem; }
.score { font-size: 2rem; font-weight: bold; color: #e67e22; margin: 1rem 0 0.5rem 0; }
.rate { font-size: 1.2rem; color: #27ae60; margin: 0 0 1rem 0; }
.cookie { font-size: 8rem; background: none; border: none; cursor: pointer; transition: transform 0.1s ease; padding: 0; margin: 2rem 0; }
.cookie:hover { transform: scale(1.1); }
.cookie:active { transform: scale(0.95); }
.message { font-size: 1.2rem; color: #666; font-style: italic; }
.upgrades-zone { background: #fff; padding: 1rem; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
h2 { margin-top: 0; color: #333; }
@media (max-width: 768px) { .game { grid-template-columns: 1fr; } }</style>Étape 3 : Explication des nouveaux concepts
Section titled “Étape 3 : Explication des nouveaux concepts”1. Props : passer des données à un composant enfant (Svelte 5)
Dans le composant enfant (Upgrade.svelte) :
let { nom, prix } = $props();La rune $props() récupère les props passées par le parent. Plus besoin de export let.
Dans le composant parent (CookieClicker.svelte) :
<Upgrade nom={upgrade.nom} prix={upgrade.prix}/>Le passage des props reste identique.
2. Listes avec {#each}
{#each upgrades as upgrade (upgrade.id)} <Upgrade ... />{/each}Syntax :
{#each liste as element (clé unique)}: boucle(upgrade.id): clé unique pour optimiser le rendu{/each}: ferme la boucle
3. Événements custom → Callbacks (Svelte 5)
En Svelte 5, les événements custom sont remplacés par des props callback.
Dans le composant enfant (Upgrade.svelte) :
let { onacheter } = $props();
// Appeler directement la fonction<button on:click={onacheter}>Dans le composant parent (CookieClicker.svelte) :
<Upgrade onacheter={() => acheterUpgrade(upgrade.id)} />Notez le changement : on:acheter devient onacheter (une prop fonction).
4. Réactivité dérivée avec $derived() (Svelte 5)
let peutAcheter = $derived(cookiesActuels >= prix);La rune $derived() recalcule automatiquement la valeur quand ses dépendances changent. C’est le remplacement de $: pour les valeurs dérivées.
5. Lifecycle avec onMount (Svelte 5 simplifié)
import { onMount } from 'svelte';
onMount(() => { // Code exécuté quand le composant est créé const interval = setInterval(() => { cookies += cookiesParSeconde; }, 1000);
// En Svelte 5, retourner une fonction la rend automatiquement fonction de cleanup return () => clearInterval(interval);});Svelte 5 simplifie : plus besoin de onDestroy, juste retourner la fonction de nettoyage depuis onMount.
Partie 4 : Mission et prochaines étapes
Section titled “Partie 4 : Mission et prochaines étapes”Synthèse de ce qu’on a appris
Section titled “Synthèse de ce qu’on a appris”Concepts Svelte 5 maîtrisés :
- ✅ Réactivité avec
$state() - ✅ Événements DOM (
on:click) - ✅ Callbacks (remplacement des événements custom)
- ✅ Conditions (
{#if}) - ✅ Listes (
{#each}) - ✅ Props avec
$props()(communication parent → enfant) - ✅ Callbacks (communication enfant → parent)
- ✅ Lifecycle (
onMountavec cleanup) - ✅ Réactivité dérivée avec
$derived()
Ce qu’on peut construire avec ça :
- Interfaces interactives
- Jeux simples
- Expériences narratives
- Outils créatifs
Recap des 5 territoires
Section titled “Recap des 5 territoires”→ Voir la page complète des territoires
- Expériences muséales/spatiales
- Jeux de société hybrides
- Expériences de lecture augmentées
- Interfaces expérimentales/ludiques
- Outils créatifs et générateurs
Mission pour la prochaine séance
Section titled “Mission pour la prochaine séance”1. Explorer des œuvres (obligatoire)
Section titled “1. Explorer des œuvres (obligatoire)”Trouvez 3 à 5 œuvres qui vous inspirent dans votre territoire préféré.
Pour chaque œuvre, notez dans votre carnet :
- Nom et lien
- Description de l’expérience
- Ce qui fonctionne / ne fonctionne pas
- Pourquoi ça vous inspire
Où chercher :
2. Enrichir votre cookie clicker (optionnel mais recommandé)
Section titled “2. Enrichir votre cookie clicker (optionnel mais recommandé)”Idées d’améliorations :
Facile :
- Ajouter plus d’upgrades
- Changer les emojis et le thème visuel
- Ajouter des sons au clic
- Sauvegarder le score dans LocalStorage
Moyen :
- Ajouter des achievements (badges)
- Créer des combos de clics
- Animations de particules au clic
- Thème jour/nuit
Avancé :
- Mini-jeux bonus
- Système de prestige (reset avec bonus)
- Graphique de progression
- Mode multiplayer (partage de score)
3. Commencer à réfléchir à votre projet
Section titled “3. Commencer à réfléchir à votre projet”Répondez à ces 3 questions :
Intention : Que voulez-vous faire ressentir/découvrir ?
Geste : Quelle action fait l’utilisateur ?
Synergie : Comment le geste et l’intention se renforcent ?
Exemple :
- Intention : Faire ressentir la solitude d’un personnage
- Geste : Cliquer pour interagir, mais le personnage s’éloigne
- Synergie : Plus on essaie de se rapprocher, plus il fuit → frustration = émotion
Ressources
Section titled “Ressources”Documentation Svelte :
Aide technique :
Ce qu’on a accompli ensemble
Section titled “Ce qu’on a accompli ensemble”- ✅ Cookie clicker fonctionnel avec réactivité Svelte
- ✅ Compréhension des props et événements
- ✅ Gestion de listes avec
{#each} - ✅ État complexe avec cookies par seconde
- ✅ Premières idées de territoire de projet