# Story 3.6: Carte interactive desktop (Konva.js) Status: ready-for-dev ## Story As a visiteur desktop, I want naviguer via une carte interactive visuelle, so that j'explore librement le portfolio comme un monde. ## Acceptance Criteria 1. **Given** le visiteur est sur desktop (>= 1024px) et accède à la carte **When** la carte se charge **Then** un canvas Konva.js affiche une carte stylisée avec les zones (Projets, Compétences, Parcours, Témoignages, Contact) 2. **And** le composant est chargé en lazy-loading (`.client.vue`) pour respecter le budget JS 3. **And** chaque zone a une apparence distincte (teinte unique, icône) 4. **And** les zones visitées ont une apparence différente des zones non visitées 5. **And** la zone Contact est verrouillée visuellement si `contactUnlocked` est `false` 6. **And** la position actuelle du visiteur est marquée sur la carte 7. **And** au hover sur une zone : le nom et le statut s'affichent (tooltip) 8. **And** au clic sur une zone : navigation vers la section correspondante avec transition 9. **And** un curseur personnalisé indique les zones cliquables 10. **And** la navigation au clavier est fonctionnelle (Tab entre zones, Enter pour naviguer) 11. **And** les zones ont des labels ARIA descriptifs ## Tasks / Subtasks - [ ] **Task 1: Installer et configurer Konva.js** (AC: #2) - [ ] Installer `konva` et `vue-konva` - [ ] Configurer pour Nuxt (SSR-safe) - [ ] Créer le wrapper `.client.vue` pour lazy-loading - [ ] **Task 2: Définir la structure des zones** (AC: #1, #3) - [ ] Créer les données des 5 zones : projets, competences, parcours, temoignages, contact - [ ] Pour chaque zone : position (x, y), couleur, icône, label, route - [ ] Design en forme d'île/territoire stylisé - [ ] **Task 3: Créer le composant InteractiveMap** (AC: #1, #2) - [ ] Créer `frontend/app/components/feature/InteractiveMap.client.vue` - [ ] Initialiser le Stage et Layer Konva - [ ] Dessiner le fond de carte (texture, grille, etc.) - [ ] Placer les zones selon les positions définies - [ ] **Task 4: Implémenter les états visuels des zones** (AC: #3, #4, #5) - [ ] Zone non visitée : couleur atténuée, opacité réduite - [ ] Zone visitée : couleur vive, checkmark ou brillance - [ ] Zone Contact verrouillée : effet grisé + icône cadenas - [ ] Zone Contact débloquée : brillance, invitation visuelle - [ ] **Task 5: Implémenter le marqueur de position** (AC: #6) - [ ] Créer un marqueur animé (pulsation) - [ ] Positionner sur la zone actuelle (basé sur la route) - [ ] Animer le déplacement entre zones - [ ] **Task 6: Implémenter les interactions hover** (AC: #7, #9) - [ ] Détecter le hover sur chaque zone - [ ] Afficher un tooltip avec nom + statut - [ ] Changer le curseur en pointer - [ ] Effet de surbrillance sur la zone - [ ] **Task 7: Implémenter les interactions clic** (AC: #8) - [ ] Détecter le clic sur une zone - [ ] Si zone accessible : naviguer avec router.push() - [ ] Si zone Contact verrouillée : afficher message ou shake - [ ] Animation de transition (zoom ou fade) - [ ] **Task 8: Implémenter l'accessibilité** (AC: #10, #11) - [ ] Rendre les zones focusables (tabindex) - [ ] Gérer Tab pour naviguer entre zones - [ ] Gérer Enter/Space pour cliquer - [ ] Ajouter aria-label descriptif à chaque zone - [ ] Ajouter role="button" aux zones cliquables - [ ] **Task 9: Responsive et performance** - [ ] Masquer la carte sous 1024px (afficher alternative mobile) - [ ] Optimiser les redessins (cache les images) - [ ] Lazy-load les images des zones - [ ] **Task 10: Tests et validation** - [ ] Tester le chargement lazy - [ ] Vérifier les 5 zones distinctes - [ ] Tester les états (visité/non visité/verrouillé) - [ ] Valider hover et clic - [ ] Tester navigation clavier - [ ] Vérifier accessibilité (screen reader) ## Dev Notes ### Installation de Konva ```bash # Dans le dossier frontend pnpm add konva vue-konva ``` ### Nuxt Config (Konva SSR-safe) ```typescript // nuxt.config.ts export default defineNuxtConfig({ // ... build: { transpile: ['konva', 'vue-konva'], }, }) ``` ### Définition des zones ```typescript // frontend/app/data/mapZones.ts import type { Section } from '~/stores/progression' export interface MapZone { id: Section | 'contact' label: { fr: string en: string } route: { fr: string en: string } position: { x: number; y: number } color: string icon: string // URL ou emoji size: number // rayon ou taille } export const mapZones: MapZone[] = [ { id: 'projets', label: { fr: 'Projets', en: 'Projects' }, route: { fr: '/projets', en: '/en/projects' }, position: { x: 200, y: 150 }, color: '#3b82f6', // blue-500 icon: '/images/map/icon-projects.svg', size: 80, }, { id: 'competences', label: { fr: 'Compétences', en: 'Skills' }, route: { fr: '/competences', en: '/en/skills' }, position: { x: 450, y: 120 }, color: '#10b981', // emerald-500 icon: '/images/map/icon-skills.svg', size: 80, }, { id: 'temoignages', label: { fr: 'Témoignages', en: 'Testimonials' }, route: { fr: '/temoignages', en: '/en/testimonials' }, position: { x: 350, y: 280 }, color: '#f59e0b', // amber-500 icon: '/images/map/icon-testimonials.svg', size: 80, }, { id: 'parcours', label: { fr: 'Parcours', en: 'Journey' }, route: { fr: '/parcours', en: '/en/journey' }, position: { x: 550, y: 300 }, color: '#8b5cf6', // violet-500 icon: '/images/map/icon-journey.svg', size: 80, }, { id: 'contact', label: { fr: 'Contact', en: 'Contact' }, route: { fr: '/contact', en: '/en/contact' }, position: { x: 650, y: 180 }, color: '#fa784f', // sky-accent icon: '/images/map/icon-contact.svg', size: 80, }, ] ``` ### Composant InteractiveMap ```vue ``` ### Clés i18n **fr.json :** ```json { "map": { "ariaLabel": "Carte interactive du portfolio. Utilisez Tab pour naviguer entre les zones et Entrée pour explorer.", "instructions": "Utilisez les touches Tab pour naviguer entre les zones et Entrée ou Espace pour explorer une zone.", "locked": "Zone verrouillée - Explorez davantage pour débloquer", "visited": "Déjà visité", "clickToExplore": "Cliquez pour explorer", "legend": { "notVisited": "Non visité", "visited": "Visité", "locked": "Verrouillé" } } } ``` **en.json :** ```json { "map": { "ariaLabel": "Interactive portfolio map. Use Tab to navigate between zones and Enter to explore.", "instructions": "Use Tab keys to navigate between zones and Enter or Space to explore a zone.", "locked": "Locked zone - Explore more to unlock", "visited": "Already visited", "clickToExplore": "Click to explore", "legend": { "notVisited": "Not visited", "visited": "Visited", "locked": "Locked" } } } ``` ### Utilisation dans une page ```vue ``` ### Dépendances **Cette story nécessite :** - Story 3.5 : Store de progression (visitedSections, contactUnlocked) - Nuxt/Vue 3 avec support Konva **Cette story prépare pour :** - Story 3.7 : Navigation mobile (alternative à la carte) - Story 4.2 : Intro narrative (peut utiliser la carte) ### Project Structure Notes **Fichiers à créer :** ``` frontend/ ├── app/ │ ├── components/feature/ │ │ └── InteractiveMap.client.vue # CRÉER │ └── data/ │ └── mapZones.ts # CRÉER └── public/images/map/ ├── icon-projects.svg # CRÉER (optionnel) ├── icon-skills.svg # CRÉER (optionnel) ├── icon-testimonials.svg # CRÉER (optionnel) ├── icon-journey.svg # CRÉER (optionnel) └── icon-contact.svg # CRÉER (optionnel) ``` **Fichiers à modifier :** ``` frontend/package.json # AJOUTER konva, vue-konva frontend/nuxt.config.ts # AJOUTER transpile konva frontend/i18n/fr.json # AJOUTER map.* frontend/i18n/en.json # AJOUTER map.* ``` ### References - [Source: docs/planning-artifacts/epics.md#Story-3.6] - [Source: docs/planning-artifacts/ux-design-specification.md#Interactive-Map] - [Source: docs/planning-artifacts/architecture.md#JS-Budget] - [Konva.js Documentation](https://konvajs.org/) ### Technical Requirements | Requirement | Value | Source | |-------------|-------|--------| | Breakpoint desktop | >= 1024px | Epics | | Bibliothèque canvas | Konva.js + vue-konva | Architecture | | Chargement | Lazy (.client.vue) | JS Budget | | Zones | 5 (projets, competences, temoignages, parcours, contact) | Epics | | Accessibilité | Tab + Enter/Space, ARIA | Epics | ## Dev Agent Record ### Agent Model Used {{agent_model_name_version}} ### Debug Log References ### Completion Notes List ### Change Log | Date | Change | Author | |------|--------|--------| | 2026-02-04 | Story créée avec contexte complet | SM Agent | ### File List