Initial commit: Sirius en Guadeloupe - Application web multilingue
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
name: Deploy to GitHub Pages
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: "pages"
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-pages-artifact@v3
|
||||||
|
with:
|
||||||
|
path: ./dist
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
steps:
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v4
|
||||||
+27
-27
@@ -1,33 +1,33 @@
|
|||||||
# Logs
|
# Fichiers de build
|
||||||
logs
|
dist/
|
||||||
*.log
|
build/
|
||||||
npm-debug.log*
|
.svelte-kit/
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
node_modules
|
# Dépendances
|
||||||
dist
|
node_modules/
|
||||||
dist-ssr
|
|
||||||
*.local
|
|
||||||
|
|
||||||
# Editor directories and files
|
# Fichiers d'environnement
|
||||||
.vscode/*
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
||||||
|
|
||||||
# Environment variables
|
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
.env.production
|
.env.production
|
||||||
|
|
||||||
# Build files
|
# Logs
|
||||||
build/
|
*.log
|
||||||
.svelte-kit/
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# Temporaires
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
.cache/
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 cyber-mawonaj & OKI
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -1,31 +1,29 @@
|
|||||||
# Sirius en Guadeloupe 🌟
|
# Sirius en Guadeloupe - Gwada-Sirius
|
||||||
|
|
||||||
Un site web interactif pour découvrir et observer le lever héliaque de Sirius depuis la Guadeloupe.
|
Un projet web célébrant le lever héliaque de Sirius en Guadeloupe, fusionnant astronomie, culture africaine et héritage caribéen.
|
||||||
|
|
||||||
## 🌍 À propos
|
## 🌟 À propos
|
||||||
|
|
||||||
Ce projet célèbre le patrimoine astronomique africain et caribéen à travers l'observation du lever héliaque de Sirius. Il combine science moderne et savoirs ancestraux pour offrir un guide complet d'observation.
|
Cette application web interactive permet de :
|
||||||
|
- Calculer les dates précises du lever héliaque de Sirius pour différents lieux en Guadeloupe
|
||||||
|
- Explorer l'héritage culturel de Sirius à travers les civilisations (Égypte ancienne, Dogons, Caraïbes)
|
||||||
|
- Comprendre la science astronomique derrière ce phénomène
|
||||||
|
- Découvrir les meilleurs sites d'observation en Guadeloupe
|
||||||
|
|
||||||
## ✨ Fonctionnalités
|
## 🚀 Technologies utilisées
|
||||||
|
|
||||||
- **Calculateur de prédictions** : Trouvez la date et l'heure exactes du lever héliaque pour 7 sites en Guadeloupe
|
- **Svelte** - Framework JavaScript réactif
|
||||||
- **Guide scientifique** : Comprenez la mécanique céleste du phénomène
|
- **Canvas 2D** - Pour les animations et visualisations
|
||||||
- **Patrimoine culturel** : Découvrez l'importance de Sirius dans les cultures africaines
|
- **GeoJSON** - Pour les cartes interactives
|
||||||
- **Guide pratique** : Instructions détaillées pour réussir votre observation
|
- **i18n** - Support multilingue (Français, English, Kreyòl Ayisyen)
|
||||||
- **Visualisation interactive** : Globe animé montrant la progression mondiale du phénomène
|
|
||||||
|
|
||||||
## 🛠️ Technologies
|
## 📦 Installation
|
||||||
|
|
||||||
- **Svelte** : Framework JS réactif et performant
|
|
||||||
- **Vite** : Build tool moderne et rapide
|
|
||||||
- **Canvas 2D** : Pour les animations et visualisations
|
|
||||||
- **CSS moderne** : Animations, gradients, et design responsive
|
|
||||||
|
|
||||||
## 🚀 Installation
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Cloner le repository
|
# Cloner le repository
|
||||||
git clone https://github.com/yourusername/gwada-sirius.git
|
git clone https://github.com/[votre-username]/gwada-sirius.git
|
||||||
|
|
||||||
|
# Entrer dans le dossier
|
||||||
cd gwada-sirius
|
cd gwada-sirius
|
||||||
|
|
||||||
# Installer les dépendances
|
# Installer les dépendances
|
||||||
@@ -35,41 +33,39 @@ npm install
|
|||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📦 Build
|
## 🌍 Fonctionnalités
|
||||||
|
|
||||||
```bash
|
- **Calculateur de prédictions** : Dates précises pour 7 sites d'observation
|
||||||
# Créer une version de production
|
- **Globe interactif** : Visualisation de la trajectoire de Sirius
|
||||||
npm run build
|
- **Carte des sites** : Localisation des meilleurs points d'observation
|
||||||
|
- **Multilingue** : Interface en français, anglais et créole haïtien
|
||||||
# Prévisualiser la version de production
|
- **Responsive** : Optimisé pour mobile et desktop
|
||||||
npm run preview
|
- **Animations** : Ciel étoilé animé et patterns africains
|
||||||
```
|
|
||||||
|
|
||||||
## 🎨 Design
|
## 🎨 Design
|
||||||
|
|
||||||
Le design s'inspire des motifs géométriques africains (Kente, Bogolan, Adinkra) avec une palette de couleurs panafricaine et caribéenne :
|
Le design s'inspire des motifs africains (Kente, Adinkra, Bogolan) avec une palette de couleurs symbolique :
|
||||||
- Or (Sirius, soleil)
|
- Or (#FFD700) - Sirius et la lumière divine
|
||||||
- Rouge profond
|
- Rouge (#FF6B35) - Force et énergie vitale
|
||||||
- Vert forêt
|
- Vert (#4CAF50) - Nature et renouveau
|
||||||
- Bleu nuit profond
|
- Bleu nuit (#0A1628) - Ciel nocturne
|
||||||
|
|
||||||
## 📱 Mobile First
|
## 👥 Crédits
|
||||||
|
|
||||||
L'application est conçue en priorité pour les appareils mobiles avec :
|
Projet réalisé avec ❤️ par :
|
||||||
- Navigation responsive
|
- [cyber-mawonaj](https://github.com/cyber-mawonaj)
|
||||||
- Touch-friendly
|
- [OKI](https://o-k-i.net)
|
||||||
- Performance optimisée
|
|
||||||
|
## 📜 Licence
|
||||||
|
|
||||||
|
Ce projet est sous licence MIT - voir le fichier [LICENSE](LICENSE) pour plus de détails.
|
||||||
|
|
||||||
## 🙏 Remerciements
|
## 🙏 Remerciements
|
||||||
|
|
||||||
- Données astronomiques calculées avec Stellarium
|
- Les anciens Égyptiens pour leur sagesse astronomique
|
||||||
- Inspiré du rapport "Sirius en Guadeloupe : Guide Astronomique et Culturel"
|
- Les Dogons du Mali pour avoir préservé ces connaissances
|
||||||
- Patrimoine culturel africain et caribéen
|
- La communauté guadeloupéenne pour perpétuer ces traditions
|
||||||
|
|
||||||
## 📄 Licence
|
|
||||||
|
|
||||||
Ce projet est sous licence MIT.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*"Les étoiles sont les ancêtres qui veillent sur nous"* - Proverbe africain
|
*"Les étoiles sont les ancêtres qui veillent sur nous" - Proverbe africain*
|
||||||
+10
-4
@@ -1,5 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import { initLanguage } from './lib/i18n.js';
|
||||||
import Hero from './components/Hero.svelte';
|
import Hero from './components/Hero.svelte';
|
||||||
import Navigation from './components/Navigation.svelte';
|
import Navigation from './components/Navigation.svelte';
|
||||||
import About from './components/About.svelte';
|
import About from './components/About.svelte';
|
||||||
@@ -7,15 +8,19 @@
|
|||||||
import Science from './components/Science.svelte';
|
import Science from './components/Science.svelte';
|
||||||
import Culture from './components/Culture.svelte';
|
import Culture from './components/Culture.svelte';
|
||||||
import Observatory from './components/Observatory.svelte';
|
import Observatory from './components/Observatory.svelte';
|
||||||
import Globe from './components/Globe.svelte';
|
|
||||||
import GlobeOpenGlobus from './components/GlobeOpenGlobus.svelte';
|
|
||||||
import GlobeGeoJSON from './components/GlobeGeoJSON.svelte';
|
import GlobeGeoJSON from './components/GlobeGeoJSON.svelte';
|
||||||
import Footer from './components/Footer.svelte';
|
import Footer from './components/Footer.svelte';
|
||||||
|
import Dogon from './components/Dogon.svelte';
|
||||||
|
import Calculations from './components/Calculations.svelte';
|
||||||
|
import ObservationMap from './components/ObservationMap.svelte';
|
||||||
|
|
||||||
let scrollY = 0;
|
let scrollY = 0;
|
||||||
let activeSection = 'home';
|
let activeSection = 'home';
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
// Initialiser la langue
|
||||||
|
initLanguage();
|
||||||
|
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
scrollY = window.scrollY;
|
scrollY = window.scrollY;
|
||||||
|
|
||||||
@@ -45,10 +50,11 @@
|
|||||||
<About />
|
<About />
|
||||||
<Predictions />
|
<Predictions />
|
||||||
<Science />
|
<Science />
|
||||||
|
<Calculations />
|
||||||
|
<Dogon />
|
||||||
<Culture />
|
<Culture />
|
||||||
<Observatory />
|
<Observatory />
|
||||||
<Globe />
|
<ObservationMap />
|
||||||
<GlobeOpenGlobus />
|
|
||||||
<GlobeGeoJSON />
|
<GlobeGeoJSON />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import { t } from '../lib/i18n.js';
|
||||||
|
|
||||||
let visible = false;
|
let visible = false;
|
||||||
|
|
||||||
@@ -71,8 +72,8 @@
|
|||||||
{#each features as feature, i}
|
{#each features as feature, i}
|
||||||
<div class="feature-card card" style="animation-delay: {i * 0.2}s">
|
<div class="feature-card card" style="animation-delay: {i * 0.2}s">
|
||||||
<div class="feature-icon">{feature.icon}</div>
|
<div class="feature-icon">{feature.icon}</div>
|
||||||
<h3>{feature.title}</h3>
|
<h3>{$t(feature.title)}</h3>
|
||||||
<p>{feature.description}</p>
|
<p>{$t(feature.description)}</p>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
@@ -165,6 +166,17 @@
|
|||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.features-grid {
|
.features-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
@@ -0,0 +1,230 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
let visible = false;
|
||||||
|
let selectedCalculation = 'heliacal';
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
visible = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const section = document.getElementById('calculations');
|
||||||
|
if (section) observer.observe(section);
|
||||||
|
|
||||||
|
return () => observer.disconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
const calculations = {
|
||||||
|
heliacal: {
|
||||||
|
title: 'Lever Héliaque',
|
||||||
|
description: 'Calcul de la première visibilité de Sirius avant l\'aube'
|
||||||
|
},
|
||||||
|
arcus: {
|
||||||
|
title: 'Arcus Visionis',
|
||||||
|
description: 'Angle minimal entre le Soleil et Sirius pour la visibilité'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<section id="calculations" class="calculations-section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header" class:visible>
|
||||||
|
<h2>Calculs Astronomiques</h2>
|
||||||
|
<p class="section-subtitle">
|
||||||
|
La science précise derrière le lever héliaque
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="calculations-wrapper" class:visible>
|
||||||
|
<!-- Sélecteur de calcul -->
|
||||||
|
<div class="calc-selector">
|
||||||
|
{#each Object.entries(calculations) as [key, calc]}
|
||||||
|
<button
|
||||||
|
class="calc-btn"
|
||||||
|
class:active={selectedCalculation === key}
|
||||||
|
on:click={() => selectedCalculation = key}
|
||||||
|
>
|
||||||
|
<h4>{calc.title}</h4>
|
||||||
|
<p>{calc.description}</p>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Contenu des calculs -->
|
||||||
|
<div class="calc-content">
|
||||||
|
{#if selectedCalculation === 'heliacal'}
|
||||||
|
<div class="calc-panel">
|
||||||
|
<h3>Calcul du Lever Héliaque de Sirius</h3>
|
||||||
|
<div class="formula-box">
|
||||||
|
<h4>Formule Principale</h4>
|
||||||
|
<div class="formula">
|
||||||
|
<p><strong>Date du lever héliaque = </strong></p>
|
||||||
|
<p>Date de conjonction + Arcus Visionis / Vitesse angulaire du Soleil</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="calculation-steps">
|
||||||
|
<h4>Étapes du Calcul pour la Guadeloupe</h4>
|
||||||
|
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-number">1</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h5>Coordonnées de la Guadeloupe</h5>
|
||||||
|
<code>Latitude: 16.25°N, Longitude: 61.58°O</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-number">2</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h5>Position de Sirius</h5>
|
||||||
|
<code>Ascension droite: 6h 45m 09s</code>
|
||||||
|
<code>Déclinaison: -16° 42' 58"</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> {/if}
|
||||||
|
|
||||||
|
{#if selectedCalculation === 'arcus'}
|
||||||
|
<div class="calc-panel">
|
||||||
|
<h3>Arcus Visionis - L'Arc de Vision</h3>
|
||||||
|
|
||||||
|
<div class="explanation">
|
||||||
|
<p>
|
||||||
|
L'Arcus Visionis est l'angle minimal entre le Soleil et une étoile
|
||||||
|
pour que celle-ci devienne visible.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.calculations-section {
|
||||||
|
padding: var(--spacing-xl) 0;
|
||||||
|
background: var(--gradient-dawn);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculations-wrapper {
|
||||||
|
margin-top: var(--spacing-lg);
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
transition: all 0.8s ease-out;
|
||||||
|
}
|
||||||
|
.calculations-wrapper.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calc-selector {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calc-btn {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calc-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calc-btn.active {
|
||||||
|
background: rgba(255, 215, 0, 0.1);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
.calc-btn h4 {
|
||||||
|
color: var(--color-primary);
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calc-btn p {
|
||||||
|
color: var(--color-light);
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calc-panel {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formula-box {
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formula {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
color: var(--color-light);
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculation-steps {
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
.step {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-number {
|
||||||
|
background: var(--color-primary);
|
||||||
|
color: var(--color-dark);
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: bold;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-content h5 {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-content code {
|
||||||
|
display: block;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 0.25rem 0;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.calc-selector {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calc-panel {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -224,6 +224,17 @@
|
|||||||
color: rgba(255, 255, 255, 0.7);
|
color: rgba(255, 255, 255, 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.timeline-items {
|
.timeline-items {
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
|
|||||||
@@ -0,0 +1,587 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
let visible = false;
|
||||||
|
let activeTab = 'cosmogonie';
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
visible = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const section = document.getElementById('dogon');
|
||||||
|
if (section) observer.observe(section);
|
||||||
|
|
||||||
|
return () => observer.disconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{ id: 'cosmogonie', label: 'Cosmogonie', icon: '🌌' },
|
||||||
|
{ id: 'sirius', label: 'Système de Sirius', icon: '⭐' },
|
||||||
|
{ id: 'astronomie', label: 'Astronomie Dogon', icon: '🔭' },
|
||||||
|
{ id: 'heritage', label: 'Héritage', icon: '🌍' }
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section id="dogon" class="dogon-section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header" class:visible>
|
||||||
|
<h2>Les Dogons et Sirius</h2>
|
||||||
|
<p class="section-subtitle">
|
||||||
|
Une connaissance astronomique millénaire
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content-wrapper" class:visible>
|
||||||
|
<!-- Navigation par onglets -->
|
||||||
|
<div class="tabs-nav">
|
||||||
|
{#each tabs as tab}
|
||||||
|
<button
|
||||||
|
class="tab-btn"
|
||||||
|
class:active={activeTab === tab.id}
|
||||||
|
on:click={() => activeTab = tab.id}
|
||||||
|
>
|
||||||
|
<span class="tab-icon">{tab.icon}</span>
|
||||||
|
<span class="tab-label">{tab.label}</span>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Contenu des onglets -->
|
||||||
|
<div class="tabs-content">
|
||||||
|
{#if activeTab === 'cosmogonie'}
|
||||||
|
<div class="tab-panel">
|
||||||
|
<h3>La Cosmogonie Dogon</h3>
|
||||||
|
<div class="content-grid">
|
||||||
|
<div class="content-text">
|
||||||
|
<p>
|
||||||
|
Les Dogons du Mali possèdent une cosmogonie complexe centrée sur <strong>Sigu Tolo</strong> (Sirius),
|
||||||
|
l'étoile la plus brillante du ciel. Leur savoir astronomique, transmis oralement depuis des millénaires,
|
||||||
|
révèle une compréhension sophistiquée du cosmos.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Le <strong>Sigui</strong>, cérémonie célébrée tous les 60 ans, marque le renouvellement du monde
|
||||||
|
et commémore la révélation de la parole aux hommes. Cette périodicité correspond étrangement
|
||||||
|
à l'orbite de Sirius B autour de Sirius A (50 ans), ajustée par des considérations rituelles.
|
||||||
|
</p>
|
||||||
|
<div class="highlight-box">
|
||||||
|
<h4>Le Nommo</h4>
|
||||||
|
<p>
|
||||||
|
Être primordial amphibie venu de Sirius, le Nommo apporta la civilisation
|
||||||
|
et les connaissances fondamentales aux ancêtres des Dogons.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-visual">
|
||||||
|
<div class="cosmogony-diagram">
|
||||||
|
<svg viewBox="0 0 400 400" class="diagram-svg">
|
||||||
|
<!-- Amma (Dieu créateur) au centre -->
|
||||||
|
<circle cx="200" cy="200" r="30" fill="var(--color-primary)" opacity="0.8"/>
|
||||||
|
<text x="200" y="205" text-anchor="middle" fill="white" font-weight="bold">Amma</text>
|
||||||
|
|
||||||
|
<!-- Oeuf cosmique -->
|
||||||
|
<ellipse cx="200" cy="200" rx="150" ry="100" fill="none" stroke="var(--color-secondary)" stroke-width="2" stroke-dasharray="5,5" opacity="0.6"/>
|
||||||
|
|
||||||
|
<!-- Les 4 éléments -->
|
||||||
|
<g class="elements">
|
||||||
|
<circle cx="200" cy="100" r="25" fill="var(--color-accent)"/>
|
||||||
|
<text x="200" y="105" text-anchor="middle" fill="white" font-size="12">Eau</text>
|
||||||
|
|
||||||
|
<circle cx="300" cy="200" r="25" fill="#FF6B35"/>
|
||||||
|
<text x="300" y="205" text-anchor="middle" fill="white" font-size="12">Feu</text>
|
||||||
|
|
||||||
|
<circle cx="200" cy="300" r="25" fill="#4CAF50"/>
|
||||||
|
<text x="200" y="305" text-anchor="middle" fill="white" font-size="12">Terre</text>
|
||||||
|
|
||||||
|
<circle cx="100" cy="200" r="25" fill="#87CEEB"/>
|
||||||
|
<text x="100" y="205" text-anchor="middle" fill="white" font-size="12">Air</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Lignes de connexion -->
|
||||||
|
<line x1="200" y1="230" x2="200" y2="275" stroke="var(--color-light)" stroke-width="1" opacity="0.5"/>
|
||||||
|
<line x1="225" y1="200" x2="275" y2="200" stroke="var(--color-light)" stroke-width="1" opacity="0.5"/>
|
||||||
|
<line x1="200" y1="175" x2="200" y2="125" stroke="var(--color-light)" stroke-width="1" opacity="0.5"/>
|
||||||
|
<line x1="175" y1="200" x2="125" y2="200" stroke="var(--color-light)" stroke-width="1" opacity="0.5"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if activeTab === 'sirius'}
|
||||||
|
<div class="tab-panel">
|
||||||
|
<h3>Le Système de Sirius selon les Dogons</h3>
|
||||||
|
<div class="sirius-system">
|
||||||
|
<div class="star-info">
|
||||||
|
<h4>Sigu Tolo (Sirius A)</h4>
|
||||||
|
<p>L'étoile principale, source de lumière et de vie</p>
|
||||||
|
<ul>
|
||||||
|
<li>Associée à la féminité et à la fécondité</li>
|
||||||
|
<li>Guide des saisons et des récoltes</li>
|
||||||
|
<li>Centre du système stellaire</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="star-info">
|
||||||
|
<h4>Po Tolo (Sirius B)</h4>
|
||||||
|
<p>L'étoile compagne invisible à l'œil nu</p>
|
||||||
|
<ul>
|
||||||
|
<li>Décrite comme "lourde" et "blanche"</li>
|
||||||
|
<li>Orbite de 50 ans autour de Sirius A</li>
|
||||||
|
<li>Découverte par les astronomes occidentaux en 1862</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="star-info">
|
||||||
|
<h4>Emme Ya Tolo (Sirius C ?)</h4>
|
||||||
|
<p>Troisième étoile hypothétique du système</p>
|
||||||
|
<ul>
|
||||||
|
<li>Quatre fois plus légère que Po Tolo</li>
|
||||||
|
<li>Orbite plus large</li>
|
||||||
|
<li>Non confirmée par l'astronomie moderne</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="knowledge-mystery">
|
||||||
|
<h4>Le Mystère de la Connaissance</h4>
|
||||||
|
<p>
|
||||||
|
Comment les Dogons connaissaient-ils l'existence de Sirius B,
|
||||||
|
invisible à l'œil nu et découverte en Occident seulement en 1862 ?
|
||||||
|
</p>
|
||||||
|
<div class="theories">
|
||||||
|
<div class="theory-card">
|
||||||
|
<h5>Transmission Ancienne</h5>
|
||||||
|
<p>Héritage d'une civilisation antérieure avancée</p>
|
||||||
|
</div>
|
||||||
|
<div class="theory-card">
|
||||||
|
<h5>Contact Extraterrestre</h5>
|
||||||
|
<p>Les Nommos venus de Sirius</p>
|
||||||
|
</div>
|
||||||
|
<div class="theory-card">
|
||||||
|
<h5>Observation Minutieuse</h5>
|
||||||
|
<p>Déduction à partir des perturbations de Sirius A</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if activeTab === 'astronomie'}
|
||||||
|
<div class="tab-panel">
|
||||||
|
<h3>L'Astronomie Dogon</h3>
|
||||||
|
<div class="astronomy-content">
|
||||||
|
<div class="concept-grid">
|
||||||
|
<div class="concept-card">
|
||||||
|
<h4>📐 Géométrie Sacrée</h4>
|
||||||
|
<p>
|
||||||
|
Les Dogons utilisent des figures géométriques complexes pour
|
||||||
|
représenter les mouvements célestes et les cycles cosmiques.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="concept-card">
|
||||||
|
<h4>🌙 Calendrier Lunaire</h4>
|
||||||
|
<p>
|
||||||
|
Un système calendaire sophistiqué basé sur les phases lunaires
|
||||||
|
et la position de Sirius pour l'agriculture.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="concept-card">
|
||||||
|
<h4>🪐 Connaissance Planétaire</h4>
|
||||||
|
<p>
|
||||||
|
Description des anneaux de Saturne et des lunes de Jupiter,
|
||||||
|
invisibles sans télescope.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="concept-card">
|
||||||
|
<h4>🌌 Structure de l'Univers</h4>
|
||||||
|
<p>
|
||||||
|
Concept d'univers infini avec des mondes multiples,
|
||||||
|
similaire aux théories modernes.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="calculation-example">
|
||||||
|
<h4>Calcul de la Période Sigui</h4>
|
||||||
|
<pre><code>Période orbitale Po Tolo : 50 ans
|
||||||
|
Ajustement rituel : × 1.2
|
||||||
|
Période Sigui : 60 ans
|
||||||
|
|
||||||
|
Correspondance avec Sirius B :
|
||||||
|
- Orbite réelle : 50.1 ans
|
||||||
|
- Précision : 99.8%</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if activeTab === 'heritage'}
|
||||||
|
<div class="tab-panel">
|
||||||
|
<h3>Héritage et Influence</h3>
|
||||||
|
<div class="heritage-content">
|
||||||
|
<div class="timeline">
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-date">Antiquité</div>
|
||||||
|
<div class="timeline-content">
|
||||||
|
<h4>Origines Égyptiennes</h4>
|
||||||
|
<p>Liens possibles avec les prêtres de l'Égypte ancienne qui vénéraient Sothis (Sirius)</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-date">XIIIe siècle</div>
|
||||||
|
<div class="timeline-content">
|
||||||
|
<h4>Migration vers le Mali</h4>
|
||||||
|
<p>Installation dans les falaises de Bandiagara, préservation des traditions</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-date">1931-1956</div>
|
||||||
|
<div class="timeline-content">
|
||||||
|
<h4>Marcel Griaule</h4>
|
||||||
|
<p>Étude ethnographique révélant les connaissances astronomiques dogons</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-date">Aujourd'hui</div>
|
||||||
|
<div class="timeline-content">
|
||||||
|
<h4>Patrimoine Mondial</h4>
|
||||||
|
<p>Les Dogons inspirent scientifiques et chercheurs du monde entier</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="influence-map">
|
||||||
|
<h4>Influence sur la Caraïbe</h4>
|
||||||
|
<p>
|
||||||
|
Les traditions astronomiques africaines, incluant celles des Dogons,
|
||||||
|
ont traversé l'Atlantique avec la diaspora, enrichissant les pratiques
|
||||||
|
spirituelles et calendaires des Antilles.
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>🇭🇹 Haïti : Vaudou et astronomie sacrée</li>
|
||||||
|
<li>🇬🇵 Guadeloupe : Traditions Gwoka et cycles cosmiques</li>
|
||||||
|
<li>🇲🇶 Martinique : Bèlè et rythmes stellaires</li>
|
||||||
|
<li>🇯🇲 Jamaïque : Rastafari et conscience cosmique</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.dogon-section {
|
||||||
|
padding: var(--spacing-xl) 0;
|
||||||
|
background: var(--gradient-night);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
margin-top: var(--spacing-lg);
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
transition: all 0.8s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Onglets */
|
||||||
|
.tabs-nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 50px;
|
||||||
|
color: var(--color-light);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-btn.active {
|
||||||
|
background: var(--color-primary);
|
||||||
|
color: var(--color-dark);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-icon {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Contenu des onglets */
|
||||||
|
.tab-panel {
|
||||||
|
animation: fadeIn 0.5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: var(--spacing-lg);
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-box {
|
||||||
|
background: rgba(255, 215, 0, 0.1);
|
||||||
|
border-left: 4px solid var(--color-primary);
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-box h4 {
|
||||||
|
color: var(--color-primary);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Diagramme cosmogonie */
|
||||||
|
.cosmogony-diagram {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagram-svg {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.elements circle {
|
||||||
|
animation: pulse 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.elements circle:nth-child(2) { animation-delay: 0.5s; }
|
||||||
|
.elements circle:nth-child(4) { animation-delay: 1s; }
|
||||||
|
.elements circle:nth-child(6) { animation-delay: 1.5s; }
|
||||||
|
|
||||||
|
/* Système de Sirius */
|
||||||
|
.sirius-system {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star-info {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 15px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.star-info h4 {
|
||||||
|
color: var(--color-primary);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star-info ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star-info li {
|
||||||
|
padding: 0.25rem 0;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star-info li::before {
|
||||||
|
content: "✦";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mystère de la connaissance */
|
||||||
|
.knowledge-mystery {
|
||||||
|
background: rgba(255, 215, 0, 0.05);
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 15px;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theories {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theory-card {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theory-card h5 {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Astronomie */
|
||||||
|
.concept-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.concept-card {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 15px;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.concept-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.concept-card h4 {
|
||||||
|
color: var(--color-primary);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculation-example {
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculation-example pre {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-light);
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Timeline */
|
||||||
|
.timeline {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 1rem;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 2px;
|
||||||
|
background: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: -2.5rem;
|
||||||
|
top: 0.5rem;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
background: var(--color-primary);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-date {
|
||||||
|
color: var(--color-primary);
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-content h4 {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Carte d'influence */
|
||||||
|
.influence-map {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 15px;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.influence-map ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 1rem;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.influence-map li {
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% { transform: scale(1); opacity: 0.8; }
|
||||||
|
50% { transform: scale(1.1); opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(20px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.content-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-nav {
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-btn {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline {
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { t } from '../lib/i18n.js';
|
||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -57,6 +58,14 @@
|
|||||||
|
|
||||||
<div class="footer-bottom">
|
<div class="footer-bottom">
|
||||||
<p>© {currentYear} Sirius Gwada. Levez les yeux vers le ciel.</p>
|
<p>© {currentYear} Sirius Gwada. Levez les yeux vers le ciel.</p>
|
||||||
|
<p class="footer-credits">
|
||||||
|
{$t('footer_copyright')}
|
||||||
|
<a href="https://github.com/cyber-mawonaj" target="_blank" rel="noopener">cyber-mawonaj</a>
|
||||||
|
{$t('footer_and')}
|
||||||
|
<a href="https://o-k-i.net" target="_blank" rel="noopener">
|
||||||
|
OKI <img src="https://o-k-i.net/assets/img/logo-oki.png" alt="OKI Logo" class="oki-logo">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
<p class="footer-quote">
|
<p class="footer-quote">
|
||||||
"Les étoiles sont les ancêtres qui veillent sur nous" - Proverbe africain
|
"Les étoiles sont les ancêtres qui veillent sur nous" - Proverbe africain
|
||||||
</p>
|
</p>
|
||||||
@@ -121,6 +130,27 @@
|
|||||||
border-top: 1px solid rgba(255, 215, 0, 0.2);
|
border-top: 1px solid rgba(255, 215, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer-credits {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-credits a {
|
||||||
|
color: var(--color-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-credits a:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.oki-logo {
|
||||||
|
height: 20px;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.footer-quote {
|
.footer-quote {
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
|||||||
@@ -264,7 +264,7 @@
|
|||||||
|
|
||||||
// Rotation automatique
|
// Rotation automatique
|
||||||
if (!isDragging) {
|
if (!isDragging) {
|
||||||
rotation += 0.002;
|
rotation -= 0.002;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(draw);
|
requestAnimationFrame(draw);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
async function initGlobe() {
|
async function initGlobe() {
|
||||||
// Charger OpenGlobus depuis CDN
|
// Charger OpenGlobus depuis CDN
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = 'https://cdn.jsdelivr.net/npm/@openglobus/og@0.10.0/dist/og.umd.js';
|
script.src = 'https://unpkg.com/@openglobus/og@0.10.0/dist/og.umd.js';
|
||||||
script.onload = () => {
|
script.onload = () => {
|
||||||
const { Globe, XYZ, Vector, Entity, Label } = window.og;
|
const { Globe, XYZ, Vector, Entity, Label } = window.og;
|
||||||
|
|
||||||
@@ -0,0 +1,271 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
let container;
|
||||||
|
let visible = false;
|
||||||
|
let animationId;
|
||||||
|
let scene, camera, renderer, globe, markers;
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting && !scene) {
|
||||||
|
visible = true;
|
||||||
|
initGlobe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const section = document.getElementById('threejs-section');
|
||||||
|
if (section) observer.observe(section);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
observer.disconnect();
|
||||||
|
if (animationId) {
|
||||||
|
cancelAnimationFrame(animationId);
|
||||||
|
}
|
||||||
|
if (renderer) {
|
||||||
|
renderer.dispose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
async function initGlobe() {
|
||||||
|
// Charger Three.js depuis CDN
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js';
|
||||||
|
script.onload = () => {
|
||||||
|
createThreeJSGlobe();
|
||||||
|
};
|
||||||
|
document.head.appendChild(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createThreeJSGlobe() {
|
||||||
|
const THREE = window.THREE;
|
||||||
|
|
||||||
|
// Scène
|
||||||
|
scene = new THREE.Scene();
|
||||||
|
scene.background = new THREE.Color(0x0a1628);
|
||||||
|
|
||||||
|
// Caméra
|
||||||
|
camera = new THREE.PerspectiveCamera(
|
||||||
|
45,
|
||||||
|
container.clientWidth / container.clientHeight,
|
||||||
|
0.1,
|
||||||
|
1000
|
||||||
|
);
|
||||||
|
camera.position.z = 2.5;
|
||||||
|
|
||||||
|
// Renderer
|
||||||
|
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||||
|
renderer.setSize(container.clientWidth, container.clientHeight);
|
||||||
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
|
container.appendChild(renderer.domElement);
|
||||||
|
|
||||||
|
// Lumières
|
||||||
|
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
|
||||||
|
scene.add(ambientLight);
|
||||||
|
|
||||||
|
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
||||||
|
directionalLight.position.set(5, 3, 5);
|
||||||
|
scene.add(directionalLight);
|
||||||
|
|
||||||
|
// Globe
|
||||||
|
const geometry = new THREE.SphereGeometry(1, 64, 64);
|
||||||
|
|
||||||
|
// Texture de la Terre
|
||||||
|
const textureLoader = new THREE.TextureLoader();
|
||||||
|
const earthTexture = textureLoader.load(
|
||||||
|
'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_atmos_2048.jpg',
|
||||||
|
() => { renderer.render(scene, camera); }
|
||||||
|
);
|
||||||
|
|
||||||
|
const material = new THREE.MeshPhongMaterial({
|
||||||
|
map: earthTexture,
|
||||||
|
bumpScale: 0.05,
|
||||||
|
specular: new THREE.Color('grey'),
|
||||||
|
shininess: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
globe = new THREE.Mesh(geometry, material);
|
||||||
|
scene.add(globe);
|
||||||
|
|
||||||
|
// Atmosphère
|
||||||
|
const atmosphereGeometry = new THREE.SphereGeometry(1.1, 64, 64);
|
||||||
|
const atmosphereMaterial = new THREE.MeshPhongMaterial({
|
||||||
|
color: 0x4444ff,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.1,
|
||||||
|
side: THREE.BackSide
|
||||||
|
});
|
||||||
|
const atmosphere = new THREE.Mesh(atmosphereGeometry, atmosphereMaterial);
|
||||||
|
scene.add(atmosphere);
|
||||||
|
|
||||||
|
// Marqueurs
|
||||||
|
markers = new THREE.Group();
|
||||||
|
scene.add(markers);
|
||||||
|
|
||||||
|
const locations = [
|
||||||
|
{ name: 'La Réunion', lat: -21.1151, lon: 55.5364, color: 0xFF6B35 },
|
||||||
|
{ name: 'Kinshasa', lat: -4.4419, lon: 15.2663, color: 0xFFA500 },
|
||||||
|
{ name: 'Guadeloupe', lat: 16.2650, lon: -61.5510, color: 0xFFD700 },
|
||||||
|
{ name: 'Martinique', lat: 14.6415, lon: -61.0242, color: 0xFFD700 },
|
||||||
|
{ name: 'Dakar', lat: 14.7167, lon: -17.4677, color: 0xFFE44D },
|
||||||
|
{ name: 'Le Caire', lat: 30.0444, lon: 31.2357, color: 0xFFFACD }
|
||||||
|
];
|
||||||
|
|
||||||
|
locations.forEach(loc => {
|
||||||
|
const markerGeometry = new THREE.SphereGeometry(0.02, 16, 16);
|
||||||
|
const markerMaterial = new THREE.MeshBasicMaterial({ color: loc.color });
|
||||||
|
const marker = new THREE.Mesh(markerGeometry, markerMaterial);
|
||||||
|
|
||||||
|
// Convertir lat/lon en position 3D
|
||||||
|
const phi = (90 - loc.lat) * (Math.PI / 180);
|
||||||
|
const theta = (loc.lon + 180) * (Math.PI / 180);
|
||||||
|
|
||||||
|
marker.position.x = -1.05 * Math.sin(phi) * Math.cos(theta);
|
||||||
|
marker.position.y = 1.05 * Math.cos(phi);
|
||||||
|
marker.position.z = 1.05 * Math.sin(phi) * Math.sin(theta);
|
||||||
|
|
||||||
|
markers.add(marker);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Contrôles de la souris
|
||||||
|
let mouseX = 0, mouseY = 0;
|
||||||
|
let isMouseDown = false;
|
||||||
|
|
||||||
|
container.addEventListener('mousedown', () => { isMouseDown = true; });
|
||||||
|
container.addEventListener('mouseup', () => { isMouseDown = false; });
|
||||||
|
container.addEventListener('mouseleave', () => { isMouseDown = false; });
|
||||||
|
|
||||||
|
container.addEventListener('mousemove', (e) => {
|
||||||
|
if (isMouseDown) {
|
||||||
|
mouseX = (e.clientX - container.offsetLeft) / container.clientWidth * 2 - 1;
|
||||||
|
mouseY = -(e.clientY - container.offsetTop) / container.clientHeight * 2 + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Animation
|
||||||
|
function animate() {
|
||||||
|
animationId = requestAnimationFrame(animate);
|
||||||
|
|
||||||
|
// Rotation automatique
|
||||||
|
if (!isMouseDown) {
|
||||||
|
globe.rotation.y += 0.002;
|
||||||
|
markers.rotation.y += 0.002;
|
||||||
|
} else {
|
||||||
|
// Rotation manuelle
|
||||||
|
globe.rotation.y += mouseX * 0.05;
|
||||||
|
globe.rotation.x = mouseY * 0.5;
|
||||||
|
markers.rotation.y = globe.rotation.y;
|
||||||
|
markers.rotation.x = globe.rotation.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
animate();
|
||||||
|
|
||||||
|
// Responsive
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
camera.aspect = container.clientWidth / container.clientHeight;
|
||||||
|
camera.updateProjectionMatrix();
|
||||||
|
renderer.setSize(container.clientWidth, container.clientHeight);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section id="threejs-section" class="globe-section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header" class:visible>
|
||||||
|
<h2>Globe avec Three.js</h2>
|
||||||
|
<p class="section-subtitle">
|
||||||
|
Rendu 3D WebGL avec textures réalistes
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="globe-container threejs" class:visible>
|
||||||
|
<div bind:this={container} class="threejs-container"></div>
|
||||||
|
|
||||||
|
<div class="globe-info">
|
||||||
|
<div class="info-card card">
|
||||||
|
<h3>Three.js</h3>
|
||||||
|
<p><strong>Avantages :</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>✅ Rendu 3D réaliste</li>
|
||||||
|
<li>✅ Textures haute qualité</li>
|
||||||
|
<li>✅ Effets atmosphériques</li>
|
||||||
|
<li>✅ Très bien documenté</li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>Inconvénients :</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>❌ Plus lourd (~150KB)</li>
|
||||||
|
<li>❌ Nécessite WebGL</li>
|
||||||
|
<li>❌ Plus complexe à personnaliser</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.globe-section {
|
||||||
|
padding: var(--spacing-xl) 0;
|
||||||
|
background: var(--gradient-dawn);
|
||||||
|
}
|
||||||
|
|
||||||
|
.globe-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: var(--spacing-lg);
|
||||||
|
align-items: center;
|
||||||
|
margin-top: var(--spacing-lg);
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
transition: all 0.8s ease-out 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.globe-container.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.threejs-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 500px;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
.threejs-container:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card h3 {
|
||||||
|
color: var(--color-primary);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card li {
|
||||||
|
padding: 0.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.globe-container {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.threejs-container {
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+181
-141
@@ -1,123 +1,132 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import { t } from '../lib/i18n.js';
|
||||||
|
|
||||||
let starsCanvas;
|
let starsCanvas;
|
||||||
let mouseX = 0;
|
let mouseX = 0.5;
|
||||||
let mouseY = 0;
|
let mouseY = 0.5;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const ctx = starsCanvas.getContext('2d');
|
const ctx = starsCanvas.getContext('2d');
|
||||||
const stars = [];
|
const stars = [];
|
||||||
|
const starCount = 150;
|
||||||
|
|
||||||
// Créer les étoiles
|
// Attendre que le DOM soit prêt
|
||||||
for (let i = 0; i < 200; i++) {
|
setTimeout(() => {
|
||||||
stars.push({
|
// Redimensionner le canvas
|
||||||
x: Math.random() * starsCanvas.width,
|
const resize = () => {
|
||||||
y: Math.random() * starsCanvas.height,
|
const hero = starsCanvas.parentElement;
|
||||||
size: Math.random() * 2,
|
starsCanvas.width = hero.offsetWidth;
|
||||||
speed: Math.random() * 0.5 + 0.1,
|
starsCanvas.height = hero.offsetHeight;
|
||||||
brightness: Math.random()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Animation des étoiles
|
|
||||||
const animate = () => {
|
|
||||||
ctx.clearRect(0, 0, starsCanvas.width, starsCanvas.height);
|
|
||||||
|
|
||||||
stars.forEach(star => {
|
|
||||||
// Effet parallaxe avec la souris
|
|
||||||
const offsetX = (mouseX - 0.5) * star.speed * 20;
|
|
||||||
const offsetY = (mouseY - 0.5) * star.speed * 20;
|
|
||||||
|
|
||||||
ctx.beginPath();
|
// Réinitialiser les étoiles après redimensionnement
|
||||||
ctx.arc(
|
if (stars.length === 0) {
|
||||||
star.x + offsetX,
|
for (let i = 0; i < starCount; i++) {
|
||||||
star.y + offsetY,
|
stars.push({
|
||||||
star.size,
|
x: Math.random() * starsCanvas.width,
|
||||||
0,
|
y: Math.random() * starsCanvas.height,
|
||||||
Math.PI * 2
|
size: Math.random() * 2,
|
||||||
);
|
brightness: Math.random(),
|
||||||
ctx.fillStyle = `rgba(255, 215, 0, ${star.brightness})`;
|
speed: 0.1 + Math.random() * 0.5
|
||||||
ctx.fill();
|
});
|
||||||
|
}
|
||||||
// Animer la brillance
|
}
|
||||||
star.brightness += (Math.random() - 0.5) * 0.05;
|
};
|
||||||
star.brightness = Math.max(0.1, Math.min(1, star.brightness));
|
|
||||||
});
|
|
||||||
|
|
||||||
requestAnimationFrame(animate);
|
resize();
|
||||||
};
|
window.addEventListener('resize', resize);
|
||||||
|
|
||||||
// Redimensionner le canvas
|
// Animation des étoiles
|
||||||
const resize = () => {
|
const animate = () => {
|
||||||
starsCanvas.width = window.innerWidth;
|
ctx.clearRect(0, 0, starsCanvas.width, starsCanvas.height);
|
||||||
starsCanvas.height = window.innerHeight;
|
|
||||||
};
|
// Effet de parallaxe avec la souris
|
||||||
|
const offsetX = (mouseX - 0.5) * 20;
|
||||||
resize();
|
const offsetY = (mouseY - 0.5) * 20;
|
||||||
animate();
|
|
||||||
|
stars.forEach(star => {
|
||||||
window.addEventListener('resize', resize);
|
// Mouvement subtil
|
||||||
|
star.x += Math.sin(Date.now() * 0.0001 * star.speed) * 0.5;
|
||||||
return () => {
|
star.y += Math.cos(Date.now() * 0.0001 * star.speed) * 0.3;
|
||||||
window.removeEventListener('resize', resize);
|
|
||||||
};
|
ctx.beginPath();
|
||||||
|
ctx.arc(
|
||||||
|
star.x + offsetX,
|
||||||
|
star.y + offsetY,
|
||||||
|
star.size,
|
||||||
|
0,
|
||||||
|
Math.PI * 2
|
||||||
|
);
|
||||||
|
ctx.fillStyle = `rgba(255, 215, 0, ${star.brightness})`;
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Animer la brillance
|
||||||
|
star.brightness += (Math.random() - 0.5) * 0.05;
|
||||||
|
star.brightness = Math.max(0.1, Math.min(1, star.brightness));
|
||||||
|
});
|
||||||
|
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
};
|
||||||
|
|
||||||
|
animate();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', resize);
|
||||||
|
};
|
||||||
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleMouseMove = (e) => {
|
const handleMouseMove = (e) => {
|
||||||
mouseX = e.clientX / window.innerWidth;
|
const rect = e.currentTarget.getBoundingClientRect();
|
||||||
mouseY = e.clientY / window.innerHeight;
|
mouseX = (e.clientX - rect.left) / rect.width;
|
||||||
|
mouseY = (e.clientY - rect.top) / rect.height;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section id="home" class="hero" on:mousemove={handleMouseMove}>
|
<section id="home" class="hero" on:mousemove={handleMouseMove}>
|
||||||
<canvas bind:this={starsCanvas} class="stars-canvas"></canvas>
|
<canvas bind:this={starsCanvas} class="stars-canvas"></canvas>
|
||||||
|
|
||||||
<div class="hero-pattern pattern-adinkra"></div>
|
<div class="hero-pattern">
|
||||||
|
<!-- Patterns africains visibles -->
|
||||||
|
<div class="pattern-overlay pattern-kente"></div>
|
||||||
|
<div class="pattern-overlay pattern-adinkra"></div>
|
||||||
|
<div class="pattern-overlay pattern-bogolan"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="hero-content">
|
<div class="hero-content">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="hero-text">
|
<div class="hero-text">
|
||||||
<h1 class="hero-title">
|
<h1 class="hero-title">
|
||||||
<span class="title-line">Sirius</span>
|
<span class="title-line">{$t('hero_title1')}</span>
|
||||||
<span class="title-line">en Guadeloupe</span>
|
<span class="title-line">{$t('hero_title2')}</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p class="hero-subtitle">
|
<p class="hero-subtitle">
|
||||||
Wep Ronpet - L'Ouverture de l'Année Cosmique
|
{$t('hero_subtitle')}
|
||||||
</p>
|
</p>
|
||||||
<p class="hero-description">
|
<p class="hero-description">
|
||||||
Le rendez-vous millénaire entre Ciel, Science et Culture
|
{$t('hero_description')}
|
||||||
</p>
|
</p>
|
||||||
<p class="hero-date">
|
<p class="hero-date">
|
||||||
Lever héliaque : <strong>22 Juillet 2025</strong>
|
{$t('hero_date')}<strong>{$t('hero_date_value')}</strong>
|
||||||
</p>
|
</p>
|
||||||
<div class="hero-cta">
|
<div class="hero-cta">
|
||||||
<a href="#predictions" class="btn btn-primary">
|
<a href="#predictions" class="btn btn-primary">
|
||||||
Découvrir les Prédictions
|
{$t('hero_cta_predictions')}
|
||||||
</a>
|
</a>
|
||||||
<a href="#about" class="btn btn-outline">
|
<a href="#about" class="btn btn-outline">
|
||||||
En Savoir Plus
|
{$t('hero_cta_learn')}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hero-visual">
|
|
||||||
<div class="sirius-glow"></div>
|
|
||||||
<div class="ankh-symbol">☥</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="scroll-indicator">
|
|
||||||
<span>Défiler pour explorer</span>
|
|
||||||
<div class="scroll-arrow">↓</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.hero {
|
.hero {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
background: var(--gradient-dawn);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -130,6 +139,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-pattern {
|
.hero-pattern {
|
||||||
@@ -138,36 +148,107 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
opacity: 0.1;
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pattern-overlay {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 0.1;
|
||||||
|
background-size: 200px 200px;
|
||||||
|
mix-blend-mode: overlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pattern-kente {
|
||||||
|
background-image:
|
||||||
|
repeating-linear-gradient(
|
||||||
|
45deg,
|
||||||
|
var(--color-primary) 0px,
|
||||||
|
var(--color-primary) 10px,
|
||||||
|
transparent 10px,
|
||||||
|
transparent 20px,
|
||||||
|
var(--color-secondary) 20px,
|
||||||
|
var(--color-secondary) 30px,
|
||||||
|
transparent 30px,
|
||||||
|
transparent 40px
|
||||||
|
),
|
||||||
|
repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
var(--color-accent) 0px,
|
||||||
|
var(--color-accent) 10px,
|
||||||
|
transparent 10px,
|
||||||
|
transparent 20px
|
||||||
|
);
|
||||||
|
animation: slidePattern 30s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pattern-adinkra {
|
||||||
|
background-image:
|
||||||
|
radial-gradient(circle at 20% 20%, var(--color-primary) 2px, transparent 2px),
|
||||||
|
radial-gradient(circle at 80% 80%, var(--color-secondary) 2px, transparent 2px),
|
||||||
|
radial-gradient(circle at 50% 50%, var(--color-accent) 3px, transparent 3px);
|
||||||
|
background-size: 50px 50px;
|
||||||
|
animation: rotatePattern 40s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pattern-bogolan {
|
||||||
|
background-image:
|
||||||
|
repeating-conic-gradient(
|
||||||
|
from 0deg at 50% 50%,
|
||||||
|
var(--color-primary) 0deg 10deg,
|
||||||
|
transparent 10deg 20deg
|
||||||
|
);
|
||||||
|
background-size: 100px 100px;
|
||||||
|
opacity: 0.05;
|
||||||
|
animation: pulsePattern 20s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slidePattern {
|
||||||
|
from { transform: translateX(0); }
|
||||||
|
to { transform: translateX(200px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotatePattern {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulsePattern {
|
||||||
|
0%, 100% { opacity: 0.05; }
|
||||||
|
50% { opacity: 0.15; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-content {
|
.hero-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 3;
|
z-index: 10;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding: var(--spacing-xl) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-content .container {
|
.hero-text {
|
||||||
display: grid;
|
max-width: 600px;
|
||||||
grid-template-columns: 1fr 1fr;
|
text-align: center;
|
||||||
gap: 4rem;
|
margin: 0 auto;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-title {
|
.hero-title {
|
||||||
font-size: clamp(3rem, 10vw, 6rem);
|
font-size: clamp(3rem, 8vw, 5rem);
|
||||||
line-height: 1;
|
font-weight: 700;
|
||||||
|
line-height: 1.1;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
color: var(--color-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-line {
|
.title-line {
|
||||||
display: block;
|
display: block;
|
||||||
animation: fadeInUp 1s ease-out;
|
opacity: 0;
|
||||||
animation-fill-mode: both;
|
animation: fadeInUp 1s ease-out forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-line:nth-child(2) {
|
.title-line:nth-child(2) {
|
||||||
|
color: var(--color-primary);
|
||||||
animation-delay: 0.2s;
|
animation-delay: 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,10 +276,16 @@
|
|||||||
animation-fill-mode: both;
|
animation-fill-mode: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hero-date strong {
|
||||||
|
color: var(--color-primary);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
.hero-cta {
|
.hero-cta {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
animation: fadeInUp 1s ease-out 0.8s;
|
animation: fadeInUp 1s ease-out 0.8s;
|
||||||
animation-fill-mode: both;
|
animation-fill-mode: both;
|
||||||
}
|
}
|
||||||
@@ -214,44 +301,6 @@
|
|||||||
color: var(--color-dark);
|
color: var(--color-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-visual {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sirius-glow {
|
|
||||||
position: absolute;
|
|
||||||
width: 300px;
|
|
||||||
height: 300px;
|
|
||||||
background: radial-gradient(circle, var(--color-primary) 0%, transparent 70%);
|
|
||||||
opacity: 0.5;
|
|
||||||
animation: glow 3s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ankh-symbol {
|
|
||||||
font-size: 15rem;
|
|
||||||
color: var(--color-primary);
|
|
||||||
animation: float 6s ease-in-out infinite;
|
|
||||||
filter: drop-shadow(0 0 30px rgba(255, 215, 0, 0.5));
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-indicator {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 2rem;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
text-align: center;
|
|
||||||
animation: fadeInUp 1s ease-out 1s;
|
|
||||||
animation-fill-mode: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-arrow {
|
|
||||||
font-size: 2rem;
|
|
||||||
animation: bounce 2s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeInUp {
|
@keyframes fadeInUp {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -263,28 +312,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes bounce {
|
|
||||||
0%, 100% {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: translateY(10px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile */
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.hero-content .container {
|
.hero-title {
|
||||||
grid-template-columns: 1fr;
|
font-size: clamp(2.5rem, 10vw, 4rem);
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-visual {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-cta {
|
.hero-cta {
|
||||||
justify-content: center;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 300px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
<script>
|
||||||
|
import { currentLanguage, availableLanguages, setLanguage } from '../lib/i18n.js';
|
||||||
|
|
||||||
|
let isOpen = false;
|
||||||
|
|
||||||
|
function toggleDropdown() {
|
||||||
|
isOpen = !isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectLanguage(lang) {
|
||||||
|
setLanguage(lang);
|
||||||
|
isOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fermer le dropdown si on clique ailleurs
|
||||||
|
function handleClickOutside(event) {
|
||||||
|
if (!event.target.closest('.language-selector')) {
|
||||||
|
isOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:click={handleClickOutside} />
|
||||||
|
|
||||||
|
<div class="language-selector">
|
||||||
|
<button class="language-button" on:click={toggleDropdown}>
|
||||||
|
<span class="flag">
|
||||||
|
{availableLanguages.find(l => l.code === $currentLanguage)?.flag}
|
||||||
|
</span>
|
||||||
|
<span class="language-name">
|
||||||
|
{availableLanguages.find(l => l.code === $currentLanguage)?.name}
|
||||||
|
</span>
|
||||||
|
<span class="arrow" class:open={isOpen}>▼</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{#if isOpen}
|
||||||
|
<div class="language-dropdown">
|
||||||
|
{#each availableLanguages as lang}
|
||||||
|
<button
|
||||||
|
class="language-option"
|
||||||
|
class:active={$currentLanguage === lang.code}
|
||||||
|
on:click={() => selectLanguage(lang.code)}
|
||||||
|
>
|
||||||
|
<span class="flag">{lang.flag}</span>
|
||||||
|
<span>{lang.name}</span>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.language-selector {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--color-light);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-button:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flag {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-name {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow.open {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
right: 0;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
background: var(--color-dark);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 1000;
|
||||||
|
animation: slideDown 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--color-light);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-option:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-option.active {
|
||||||
|
background: rgba(255, 215, 0, 0.2);
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.language-name {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-button {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { t } from '../lib/i18n.js';
|
||||||
|
import LanguageSelector from './LanguageSelector.svelte';
|
||||||
|
|
||||||
export let activeSection = 'home';
|
export let activeSection = 'home';
|
||||||
|
|
||||||
let isMenuOpen = false;
|
let isMenuOpen = false;
|
||||||
@@ -8,12 +11,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ id: 'home', label: 'Accueil', icon: '🌟' },
|
{ id: 'home', label: 'nav_home', icon: '🌟' },
|
||||||
{ id: 'about', label: 'Découvrir', icon: '✨' },
|
{ id: 'about', label: 'nav_about', icon: '✨' },
|
||||||
{ id: 'predictions', label: 'Prédictions', icon: '📅' },
|
{ id: 'predictions', label: 'nav_predictions', icon: '📅' },
|
||||||
{ id: 'science', label: 'Science', icon: '🔭' },
|
{ id: 'science', label: 'nav_science', icon: '🔭' },
|
||||||
{ id: 'culture', label: 'Culture', icon: '🌍' },
|
{ id: 'culture', label: 'nav_culture', icon: '🌍' },
|
||||||
{ id: 'observatory', label: 'Observer', icon: '👁️' }
|
{ id: 'observatory', label: 'nav_observatory', icon: '👁️' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const scrollToSection = (id) => {
|
const scrollToSection = (id) => {
|
||||||
@@ -43,11 +46,13 @@
|
|||||||
on:click|preventDefault={() => scrollToSection(item.id)}
|
on:click|preventDefault={() => scrollToSection(item.id)}
|
||||||
>
|
>
|
||||||
<span class="nav-icon">{item.icon}</span>
|
<span class="nav-icon">{item.icon}</span>
|
||||||
<span class="nav-label">{item.label}</span>
|
<span class="nav-label">{$t(item.label)}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<LanguageSelector />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -0,0 +1,661 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
let visible = false;
|
||||||
|
let selectedSite = null;
|
||||||
|
let mapCanvas;
|
||||||
|
let ctx;
|
||||||
|
let guadeloupeGeoJson = null;
|
||||||
|
|
||||||
|
const sites = [
|
||||||
|
{
|
||||||
|
id: 'moule',
|
||||||
|
name: 'Le Moule',
|
||||||
|
lat: 16.3333,
|
||||||
|
lng: -61.3472,
|
||||||
|
description: 'Plage de l\'Autre Bord - Vue dégagée sur l\'Est',
|
||||||
|
features: ['Plage isolée', 'Horizon Est dégagé', 'Peu de pollution lumineuse'],
|
||||||
|
bestTime: '5h00 - 5h30',
|
||||||
|
image: '🏖️'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'deshaies',
|
||||||
|
name: 'Deshaies',
|
||||||
|
lat: 16.3039,
|
||||||
|
lng: -61.7966,
|
||||||
|
description: 'Plage de Grande Anse - Cadre idyllique',
|
||||||
|
features: ['Plage populaire', 'Montagnes en arrière-plan', 'Accès facile'],
|
||||||
|
bestTime: '5h15 - 5h45',
|
||||||
|
image: '🌅'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'sainte-anne',
|
||||||
|
name: 'Sainte-Anne',
|
||||||
|
lat: 16.2262,
|
||||||
|
lng: -61.3809,
|
||||||
|
description: 'Plage de Bois Jolan - Tranquillité garantie',
|
||||||
|
features: ['Plage sauvage', 'Très peu fréquentée', 'Nature préservée'],
|
||||||
|
bestTime: '5h00 - 5h30',
|
||||||
|
image: '🌴'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'port-louis',
|
||||||
|
name: 'Port-Louis',
|
||||||
|
lat: 16.4180,
|
||||||
|
lng: -61.5312,
|
||||||
|
description: 'Plage du Souffleur - Vue panoramique',
|
||||||
|
features: ['Vue à 180°', 'Site historique', 'Calme au petit matin'],
|
||||||
|
bestTime: '5h00 - 5h30',
|
||||||
|
image: '🏛️'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'marie-galante',
|
||||||
|
name: 'Marie-Galante',
|
||||||
|
lat: 15.9500,
|
||||||
|
lng: -61.2667,
|
||||||
|
description: 'Plage de la Feuillère - Horizon infini',
|
||||||
|
features: ['Île préservée', 'Ciel très noir', 'Conditions optimales'],
|
||||||
|
bestTime: '4h45 - 5h15',
|
||||||
|
image: '🏝️'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'soufriere',
|
||||||
|
name: 'Route de la Soufrière',
|
||||||
|
lat: 16.0439,
|
||||||
|
lng: -61.6639,
|
||||||
|
description: 'Parking de Savane à Mulets - Altitude',
|
||||||
|
features: ['1000m altitude', 'Air pur', 'Vue dégagée', 'Nuits fraîches'],
|
||||||
|
bestTime: '5h00 - 5h30',
|
||||||
|
image: '🌋'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'petite-terre',
|
||||||
|
name: 'Petite-Terre',
|
||||||
|
lat: 16.1700,
|
||||||
|
lng: -61.1200,
|
||||||
|
description: 'Réserve naturelle - Conditions parfaites',
|
||||||
|
features: ['Aucune pollution lumineuse', 'Réserve protégée', 'Accès réglementé'],
|
||||||
|
bestTime: '4h45 - 5h15',
|
||||||
|
image: '🦎'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting && !ctx) {
|
||||||
|
visible = true;
|
||||||
|
initMap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const section = document.getElementById('observation-map');
|
||||||
|
if (section) observer.observe(section);
|
||||||
|
|
||||||
|
return () => observer.disconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function initMap() {
|
||||||
|
ctx = mapCanvas.getContext('2d');
|
||||||
|
|
||||||
|
// Charger les données GeoJSON de la Guadeloupe
|
||||||
|
try {
|
||||||
|
const response = await fetch('https://raw.githubusercontent.com/gregoiredavid/france-geojson/master/regions/guadeloupe/departements-guadeloupe.geojson');
|
||||||
|
guadeloupeGeoJson = await response.json();
|
||||||
|
|
||||||
|
// Redimensionner le canvas
|
||||||
|
resizeCanvas();
|
||||||
|
window.addEventListener('resize', resizeCanvas);
|
||||||
|
|
||||||
|
// Dessiner la carte
|
||||||
|
drawMap();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur de chargement GeoJSON:', error);
|
||||||
|
// Fallback : dessiner une carte simple
|
||||||
|
drawSimpleMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resizeCanvas() {
|
||||||
|
const container = mapCanvas.parentElement;
|
||||||
|
mapCanvas.width = container.offsetWidth;
|
||||||
|
mapCanvas.height = container.offsetWidth * 0.75; // Ratio 4:3
|
||||||
|
if (guadeloupeGeoJson) drawMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawMap() {
|
||||||
|
if (!ctx || !guadeloupeGeoJson) return;
|
||||||
|
|
||||||
|
// Effacer le canvas
|
||||||
|
ctx.clearRect(0, 0, mapCanvas.width, mapCanvas.height);
|
||||||
|
|
||||||
|
// Fond océan
|
||||||
|
ctx.fillStyle = '#1a3a52';
|
||||||
|
ctx.fillRect(0, 0, mapCanvas.width, mapCanvas.height);
|
||||||
|
|
||||||
|
// Calculer les limites et l'échelle
|
||||||
|
const bounds = calculateBounds(guadeloupeGeoJson);
|
||||||
|
const scale = calculateScale(bounds, mapCanvas.width, mapCanvas.height);
|
||||||
|
|
||||||
|
// Dessiner chaque feature
|
||||||
|
guadeloupeGeoJson.features.forEach(feature => {
|
||||||
|
drawFeature(feature, bounds, scale);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Dessiner les sites
|
||||||
|
drawSites(bounds, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSimpleMap() {
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
// Effacer le canvas
|
||||||
|
ctx.clearRect(0, 0, mapCanvas.width, mapCanvas.height);
|
||||||
|
|
||||||
|
// Fond océan
|
||||||
|
ctx.fillStyle = '#1a3a52';
|
||||||
|
ctx.fillRect(0, 0, mapCanvas.width, mapCanvas.height);
|
||||||
|
|
||||||
|
// Dessiner une forme approximative de la Guadeloupe
|
||||||
|
const centerX = mapCanvas.width / 2;
|
||||||
|
const centerY = mapCanvas.height / 2;
|
||||||
|
const scale = mapCanvas.width / 4;
|
||||||
|
|
||||||
|
// Grande-Terre
|
||||||
|
ctx.fillStyle = '#2d5a3d';
|
||||||
|
ctx.strokeStyle = '#4CAF50';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX + scale * 0.5, centerY - scale * 0.2, scale * 0.4, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Basse-Terre
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX - scale * 0.3, centerY, scale * 0.5, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Marie-Galante
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX + scale * 0.2, centerY + scale * 0.8, scale * 0.25, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Dessiner les sites avec des positions approximatives
|
||||||
|
const bounds = { minLng: -62, maxLng: -61, minLat: 15.8, maxLat: 16.5 };
|
||||||
|
const simpleScale = {
|
||||||
|
x: mapCanvas.width / (bounds.maxLng - bounds.minLng),
|
||||||
|
y: mapCanvas.height / (bounds.maxLat - bounds.minLat)
|
||||||
|
};
|
||||||
|
|
||||||
|
drawSites(bounds, simpleScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateBounds(geoJson) {
|
||||||
|
let minLng = Infinity, maxLng = -Infinity;
|
||||||
|
let minLat = Infinity, maxLat = -Infinity;
|
||||||
|
|
||||||
|
function processBounds(coords) {
|
||||||
|
if (Array.isArray(coords[0])) {
|
||||||
|
coords.forEach(processBounds);
|
||||||
|
} else {
|
||||||
|
minLng = Math.min(minLng, coords[0]);
|
||||||
|
maxLng = Math.max(maxLng, coords[0]);
|
||||||
|
minLat = Math.min(minLat, coords[1]);
|
||||||
|
maxLat = Math.max(maxLat, coords[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
geoJson.features.forEach(feature => {
|
||||||
|
if (feature.geometry && feature.geometry.coordinates) {
|
||||||
|
processBounds(feature.geometry.coordinates);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ajouter une marge
|
||||||
|
const margin = 0.1;
|
||||||
|
return {
|
||||||
|
minLng: minLng - margin,
|
||||||
|
maxLng: maxLng + margin,
|
||||||
|
minLat: minLat - margin,
|
||||||
|
maxLat: maxLat + margin
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateScale(bounds, width, height) {
|
||||||
|
return {
|
||||||
|
x: width / (bounds.maxLng - bounds.minLng),
|
||||||
|
y: height / (bounds.maxLat - bounds.minLat)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function projectPoint(lng, lat, bounds, scale) {
|
||||||
|
return {
|
||||||
|
x: (lng - bounds.minLng) * scale.x,
|
||||||
|
y: mapCanvas.height - (lat - bounds.minLat) * scale.y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawFeature(feature, bounds, scale) {
|
||||||
|
ctx.fillStyle = '#2d5a3d';
|
||||||
|
ctx.strokeStyle = '#4CAF50';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
|
||||||
|
function drawCoordinates(coords) {
|
||||||
|
if (!coords || coords.length === 0) return;
|
||||||
|
|
||||||
|
if (Array.isArray(coords[0][0])) {
|
||||||
|
coords.forEach(drawCoordinates);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
coords.forEach((coord, i) => {
|
||||||
|
const point = projectPoint(coord[0], coord[1], bounds, scale);
|
||||||
|
if (i === 0) {
|
||||||
|
ctx.moveTo(point.x, point.y);
|
||||||
|
} else {
|
||||||
|
ctx.lineTo(point.x, point.y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (feature.geometry && feature.geometry.coordinates) {
|
||||||
|
drawCoordinates(feature.geometry.coordinates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSites(bounds, scale) {
|
||||||
|
sites.forEach(site => {
|
||||||
|
const point = projectPoint(site.lng, site.lat, bounds, scale);
|
||||||
|
|
||||||
|
// Cercle du marqueur
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(point.x, point.y, selectedSite?.id === site.id ? 15 : 10, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = selectedSite?.id === site.id ? '#FFD700' : '#FF6B35';
|
||||||
|
ctx.fill();
|
||||||
|
ctx.strokeStyle = 'white';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Icône du site
|
||||||
|
ctx.fillStyle = 'white';
|
||||||
|
ctx.font = selectedSite?.id === site.id ? '18px sans-serif' : '14px sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.fillText(site.image, point.x, point.y);
|
||||||
|
|
||||||
|
// Nom du site si sélectionné
|
||||||
|
if (selectedSite?.id === site.id) {
|
||||||
|
ctx.fillStyle = 'white';
|
||||||
|
ctx.font = 'bold 14px sans-serif';
|
||||||
|
ctx.fillText(site.name, point.x, point.y + 25);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMapClick(event) {
|
||||||
|
const rect = mapCanvas.getBoundingClientRect();
|
||||||
|
const x = event.clientX - rect.left;
|
||||||
|
const y = event.clientY - rect.top;
|
||||||
|
|
||||||
|
// Vérifier si on clique sur un site
|
||||||
|
const bounds = guadeloupeGeoJson ?
|
||||||
|
calculateBounds(guadeloupeGeoJson) :
|
||||||
|
{ minLng: -62, maxLng: -61, minLat: 15.8, maxLat: 16.5 };
|
||||||
|
|
||||||
|
const scale = guadeloupeGeoJson ?
|
||||||
|
calculateScale(bounds, mapCanvas.width, mapCanvas.height) :
|
||||||
|
{ x: mapCanvas.width / (bounds.maxLng - bounds.minLng),
|
||||||
|
y: mapCanvas.height / (bounds.maxLat - bounds.minLat) };
|
||||||
|
|
||||||
|
let clickedSite = null;
|
||||||
|
sites.forEach(site => {
|
||||||
|
const point = projectPoint(site.lng, site.lat, bounds, scale);
|
||||||
|
const distance = Math.sqrt(Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2));
|
||||||
|
if (distance < 20) {
|
||||||
|
clickedSite = site;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (clickedSite) {
|
||||||
|
selectedSite = selectedSite?.id === clickedSite.id ? null : clickedSite;
|
||||||
|
drawMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectSite(site) {
|
||||||
|
selectedSite = selectedSite?.id === site.id ? null : site;
|
||||||
|
if (guadeloupeGeoJson) {
|
||||||
|
drawMap();
|
||||||
|
} else {
|
||||||
|
drawSimpleMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section id="observation-map" class="map-section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header" class:visible>
|
||||||
|
<h2>Carte des Sites d'Observation</h2>
|
||||||
|
<p class="section-subtitle">
|
||||||
|
Les meilleurs endroits en Guadeloupe pour observer le lever héliaque
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="map-wrapper" class:visible>
|
||||||
|
<!-- Carte Canvas de la Guadeloupe -->
|
||||||
|
<div class="map-container">
|
||||||
|
<canvas
|
||||||
|
bind:this={mapCanvas}
|
||||||
|
class="guadeloupe-map"
|
||||||
|
on:click={handleMapClick}
|
||||||
|
></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Liste des sites -->
|
||||||
|
<div class="sites-list">
|
||||||
|
<h3>Sites d'Observation</h3>
|
||||||
|
{#each sites as site}
|
||||||
|
<div
|
||||||
|
class="site-card"
|
||||||
|
class:active={selectedSite?.id === site.id}
|
||||||
|
on:click={() => selectSite(site)}
|
||||||
|
>
|
||||||
|
<div class="site-header">
|
||||||
|
<span class="site-icon">{site.image}</span>
|
||||||
|
<h4>{site.name}</h4>
|
||||||
|
</div>
|
||||||
|
<p class="site-description">{site.description}</p>
|
||||||
|
|
||||||
|
{#if selectedSite?.id === site.id}
|
||||||
|
<div class="site-details">
|
||||||
|
<div class="features">
|
||||||
|
<h5>Caractéristiques :</h5>
|
||||||
|
<ul>
|
||||||
|
{#each site.features as feature}
|
||||||
|
<li>{feature}</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="observation-time">
|
||||||
|
<h5>🕐 Meilleur créneau :</h5>
|
||||||
|
<p>{site.bestTime}</p>
|
||||||
|
</div>
|
||||||
|
<div class="coordinates">
|
||||||
|
<h5>📍 Coordonnées GPS :</h5>
|
||||||
|
<p>{site.lat}°N, {Math.abs(site.lng)}°O</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Conseils d'observation -->
|
||||||
|
<div class="observation-tips" class:visible>
|
||||||
|
<h3>Conseils pour l'Observation</h3>
|
||||||
|
<div class="tips-grid">
|
||||||
|
<div class="tip-card">
|
||||||
|
<span class="tip-icon">🌙</span>
|
||||||
|
<h4>Phase Lunaire</h4>
|
||||||
|
<p>Privilégiez les nuits sans Lune ou avec un fin croissant</p>
|
||||||
|
</div>
|
||||||
|
<div class="tip-card">
|
||||||
|
<span class="tip-icon">☁️</span>
|
||||||
|
<h4>Météo</h4>
|
||||||
|
<p>Vérifiez l'absence de nuages à l'Est, surtout près de l'horizon</p>
|
||||||
|
</div>
|
||||||
|
<div class="tip-card">
|
||||||
|
<span class="tip-icon">⏰</span>
|
||||||
|
<h4>Horaire</h4>
|
||||||
|
<p>Arrivez 30 minutes avant pour habituer vos yeux à l'obscurité</p>
|
||||||
|
</div>
|
||||||
|
<div class="tip-card">
|
||||||
|
<span class="tip-icon">🔦</span>
|
||||||
|
<h4>Équipement</h4>
|
||||||
|
<p>Lampe rouge, jumelles optionnelles, vêtements chauds</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.map-section {
|
||||||
|
padding: var(--spacing-xl) 0;
|
||||||
|
background: var(--gradient-night);
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-wrapper {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: var(--spacing-lg);
|
||||||
|
margin-top: var(--spacing-lg);
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
transition: all 0.8s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-wrapper.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Carte Canvas */
|
||||||
|
.map-container {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 2rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guadeloupe-map {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Liste des sites */
|
||||||
|
.sites-list {
|
||||||
|
max-height: 600px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sites-list h3 {
|
||||||
|
color: var(--color-primary);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-card {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-card:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-card.active {
|
||||||
|
background: rgba(255, 215, 0, 0.1);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-icon {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header h4 {
|
||||||
|
color: var(--color-primary);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-description {
|
||||||
|
color: var(--color-light);
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-details {
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
animation: slideDown 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-details h5 {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-details ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-details li {
|
||||||
|
padding: 0.25rem 0;
|
||||||
|
padding-left: 1rem;
|
||||||
|
position: relative;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-details li::before {
|
||||||
|
content: "•";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observation-time, .coordinates {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observation-time p, .coordinates p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-light);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Conseils d'observation */
|
||||||
|
.observation-tips {
|
||||||
|
margin-top: var(--spacing-xl);
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
transition: all 0.8s ease-out 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observation-tips.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observation-tips h3 {
|
||||||
|
color: var(--color-primary);
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-card {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-icon {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-card h4 {
|
||||||
|
color: var(--color-primary);
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-card p {
|
||||||
|
color: var(--color-light);
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar personnalisée */
|
||||||
|
.sites-list::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sites-list::-webkit-scrollbar-track {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sites-list::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--color-primary);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
max-height: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
max-height: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.map-wrapper {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-container {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sites-list {
|
||||||
|
max-height: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips-grid {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+232
@@ -0,0 +1,232 @@
|
|||||||
|
import { writable, derived } from 'svelte/store';
|
||||||
|
|
||||||
|
// Store pour la langue actuelle
|
||||||
|
export const currentLanguage = writable('fr');
|
||||||
|
|
||||||
|
// Traductions
|
||||||
|
const translations = {
|
||||||
|
fr: {
|
||||||
|
// Navigation
|
||||||
|
nav_home: 'Accueil',
|
||||||
|
nav_about: 'À Propos',
|
||||||
|
nav_predictions: 'Prédictions',
|
||||||
|
nav_science: 'Science',
|
||||||
|
nav_culture: 'Culture',
|
||||||
|
nav_observatory: 'Observatoire',
|
||||||
|
|
||||||
|
// Hero
|
||||||
|
hero_title1: 'Sirius',
|
||||||
|
hero_title2: 'en Guadeloupe',
|
||||||
|
hero_subtitle: 'Wep Ronpet - L\'Ouverture de l\'Année Cosmique',
|
||||||
|
hero_description: 'Le rendez-vous millénaire entre Ciel, Science et Culture',
|
||||||
|
hero_date: 'Lever héliaque : ',
|
||||||
|
hero_date_value: '22 Juillet 2025',
|
||||||
|
hero_cta_predictions: 'Découvrir les Prédictions',
|
||||||
|
hero_cta_learn: 'En Savoir Plus',
|
||||||
|
|
||||||
|
// About
|
||||||
|
about_title: 'Le Lever Héliaque de Sirius',
|
||||||
|
about_subtitle: 'Un phénomène astronomique millénaire',
|
||||||
|
about_intro: 'Le lever héliaque de Sirius est un événement céleste qui a façonné les civilisations depuis des millénaires.',
|
||||||
|
// Features
|
||||||
|
feature_astronomy_title: 'Astronomie',
|
||||||
|
feature_astronomy_desc: 'Sirius, l\'étoile la plus brillante, guide de navigation céleste',
|
||||||
|
feature_culture_title: 'Culture',
|
||||||
|
feature_culture_desc: 'De l\'Égypte aux Dogons, un héritage universel',
|
||||||
|
feature_spirituality_title: 'Spiritualité',
|
||||||
|
feature_spirituality_desc: 'Renaissance, renouveau et connexion cosmique',
|
||||||
|
|
||||||
|
// Predictions
|
||||||
|
predictions_title: 'Prédictions 2025',
|
||||||
|
predictions_subtitle: 'Calculez la date exacte pour votre lieu d\'observation',
|
||||||
|
predictions_select_location: 'Sélectionnez votre lieu',
|
||||||
|
predictions_results_for: 'Résultats pour',
|
||||||
|
predictions_heliacal_rising: 'Lever héliaque',
|
||||||
|
predictions_best_observation: 'Meilleure observation',
|
||||||
|
predictions_visibility_duration: 'Durée de visibilité',
|
||||||
|
predictions_azimuth: 'Azimut au lever',
|
||||||
|
predictions_max_altitude: 'Altitude maximale',
|
||||||
|
predictions_magnitude: 'Magnitude apparente',
|
||||||
|
|
||||||
|
// Science
|
||||||
|
science_title: 'La Science du Lever Héliaque',
|
||||||
|
science_subtitle: 'Comprendre le phénomène astronomique',
|
||||||
|
|
||||||
|
// Culture
|
||||||
|
culture_title: 'Héritage Culturel',
|
||||||
|
culture_subtitle: 'Sirius à travers les civilisations',
|
||||||
|
culture_timeline_title: 'Chronologie du Savoir',
|
||||||
|
|
||||||
|
// Observatory
|
||||||
|
observatory_title: 'Guide d\'Observation',
|
||||||
|
observatory_subtitle: 'Tout pour observer Sirius en Guadeloupe',
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
footer_copyright: 'Projet réalisé avec ❤️ par',
|
||||||
|
footer_and: 'et'
|
||||||
|
},
|
||||||
|
|
||||||
|
en: {
|
||||||
|
// Navigation
|
||||||
|
nav_home: 'Home',
|
||||||
|
nav_about: 'About',
|
||||||
|
nav_predictions: 'Predictions',
|
||||||
|
nav_science: 'Science',
|
||||||
|
nav_culture: 'Culture',
|
||||||
|
nav_observatory: 'Observatory',
|
||||||
|
|
||||||
|
// Hero
|
||||||
|
hero_title1: 'Sirius',
|
||||||
|
hero_title2: 'in Guadeloupe',
|
||||||
|
hero_subtitle: 'Wep Ronpet - The Opening of the Cosmic Year',
|
||||||
|
hero_description: 'The millennial meeting between Sky, Science and Culture',
|
||||||
|
hero_date: 'Heliacal rising: ',
|
||||||
|
hero_date_value: 'July 22, 2025',
|
||||||
|
hero_cta_predictions: 'Discover Predictions',
|
||||||
|
hero_cta_learn: 'Learn More',
|
||||||
|
|
||||||
|
// About about_title: 'The Heliacal Rising of Sirius',
|
||||||
|
about_subtitle: 'A millennial astronomical phenomenon',
|
||||||
|
about_intro: 'The heliacal rising of Sirius is a celestial event that has shaped civilizations for millennia.',
|
||||||
|
|
||||||
|
// Features
|
||||||
|
feature_astronomy_title: 'Astronomy',
|
||||||
|
feature_astronomy_desc: 'Sirius, the brightest star, celestial navigation guide',
|
||||||
|
feature_culture_title: 'Culture',
|
||||||
|
feature_culture_desc: 'From Egypt to the Dogon, a universal heritage',
|
||||||
|
feature_spirituality_title: 'Spirituality',
|
||||||
|
feature_spirituality_desc: 'Renaissance, renewal and cosmic connection',
|
||||||
|
|
||||||
|
// Predictions
|
||||||
|
predictions_title: 'Predictions 2025',
|
||||||
|
predictions_subtitle: 'Calculate the exact date for your observation location',
|
||||||
|
predictions_select_location: 'Select your location',
|
||||||
|
predictions_results_for: 'Results for',
|
||||||
|
predictions_heliacal_rising: 'Heliacal rising',
|
||||||
|
predictions_best_observation: 'Best observation',
|
||||||
|
predictions_visibility_duration: 'Visibility duration',
|
||||||
|
predictions_azimuth: 'Azimuth at rising',
|
||||||
|
predictions_max_altitude: 'Maximum altitude',
|
||||||
|
predictions_magnitude: 'Apparent magnitude',
|
||||||
|
|
||||||
|
// Science
|
||||||
|
science_title: 'The Science of Heliacal Rising',
|
||||||
|
science_subtitle: 'Understanding the astronomical phenomenon',
|
||||||
|
|
||||||
|
// Culture
|
||||||
|
culture_title: 'Cultural Heritage',
|
||||||
|
culture_subtitle: 'Sirius through civilizations',
|
||||||
|
culture_timeline_title: 'Timeline of Knowledge',
|
||||||
|
|
||||||
|
// Observatory
|
||||||
|
observatory_title: 'Observation Guide',
|
||||||
|
observatory_subtitle: 'Everything to observe Sirius in Guadeloupe',
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
footer_copyright: 'Project made with ❤️ by',
|
||||||
|
footer_and: 'and'
|
||||||
|
},
|
||||||
|
|
||||||
|
ht: {
|
||||||
|
// Navigation
|
||||||
|
nav_home: 'Akèy',
|
||||||
|
nav_about: 'Konsènan',
|
||||||
|
nav_predictions: 'Prediksyon',
|
||||||
|
nav_science: 'Syans',
|
||||||
|
nav_culture: 'Kilti',
|
||||||
|
nav_observatory: 'Obsèvatwa',
|
||||||
|
|
||||||
|
// Hero
|
||||||
|
hero_title1: 'Sirius',
|
||||||
|
hero_title2: 'nan Gwadloup',
|
||||||
|
hero_subtitle: 'Wep Ronpet - Ouvèti Ane Kosmik la',
|
||||||
|
hero_description: 'Randevou milenè ant Syèl, Syans ak Kilti',
|
||||||
|
hero_date: 'Leve elyak: ',
|
||||||
|
hero_date_value: '22 Jiyè 2025',
|
||||||
|
hero_cta_predictions: 'Dekouvri Prediksyon yo', hero_cta_learn: 'Aprann Plis',
|
||||||
|
|
||||||
|
// About
|
||||||
|
about_title: 'Leve Elyak Sirius la',
|
||||||
|
about_subtitle: 'Yon fenomèn astwonomik milenè',
|
||||||
|
about_intro: 'Leve elyak Sirius se yon evènman selès ki fòme sivilizasyon yo depi plizyè milye ane.',
|
||||||
|
|
||||||
|
// Features
|
||||||
|
feature_astronomy_title: 'Astwonomi',
|
||||||
|
feature_astronomy_desc: 'Sirius, zetwal ki pi klere a, gid navigasyon selès',
|
||||||
|
feature_culture_title: 'Kilti',
|
||||||
|
feature_culture_desc: 'Depi Lejip rive Dogon yo, yon eritaj inivèsèl',
|
||||||
|
feature_spirituality_title: 'Espiritwalite',
|
||||||
|
feature_spirituality_desc: 'Renesans, renouvèlman ak koneksyon kosmik',
|
||||||
|
|
||||||
|
// Predictions
|
||||||
|
predictions_title: 'Prediksyon 2025',
|
||||||
|
predictions_subtitle: 'Kalkile dat egzak pou kote w ap obsève a',
|
||||||
|
predictions_select_location: 'Chwazi kote ou ye a',
|
||||||
|
predictions_results_for: 'Rezilta pou',
|
||||||
|
predictions_heliacal_rising: 'Leve elyak',
|
||||||
|
predictions_best_observation: 'Pi bon obsèvasyon',
|
||||||
|
predictions_visibility_duration: 'Dire vizibilite',
|
||||||
|
predictions_azimuth: 'Azimit nan leve',
|
||||||
|
predictions_max_altitude: 'Altitid maksimòm',
|
||||||
|
predictions_magnitude: 'Mayitid aparan',
|
||||||
|
|
||||||
|
// Science
|
||||||
|
science_title: 'Syans Leve Elyak la',
|
||||||
|
science_subtitle: 'Konprann fenomèn astwonomik la',
|
||||||
|
|
||||||
|
// Culture
|
||||||
|
culture_title: 'Eritaj Kiltirèl',
|
||||||
|
culture_subtitle: 'Sirius atravè sivilizasyon yo',
|
||||||
|
culture_timeline_title: 'Kwonoloji Konesans lan',
|
||||||
|
|
||||||
|
// Observatory
|
||||||
|
observatory_title: 'Gid Obsèvasyon',
|
||||||
|
observatory_subtitle: 'Tout bagay pou obsève Sirius nan Gwadloup',
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
footer_copyright: 'Pwojè realize avèk ❤️ pa',
|
||||||
|
footer_and: 'ak'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Store dérivé pour obtenir les traductions de la langue actuelle
|
||||||
|
export const t = derived(
|
||||||
|
currentLanguage,
|
||||||
|
$currentLanguage => key => {
|
||||||
|
return translations[$currentLanguage]?.[key] || translations['fr'][key] || key;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Fonction pour changer de langue
|
||||||
|
export function setLanguage(lang) {
|
||||||
|
if (translations[lang]) {
|
||||||
|
currentLanguage.set(lang);
|
||||||
|
if (typeof localStorage !== 'undefined') {
|
||||||
|
localStorage.setItem('preferred-language', lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Initialiser avec la langue sauvegardée ou détecter la langue du navigateur
|
||||||
|
export function initLanguage() {
|
||||||
|
if (typeof localStorage !== 'undefined') {
|
||||||
|
const saved = localStorage.getItem('preferred-language');
|
||||||
|
if (saved && translations[saved]) {
|
||||||
|
currentLanguage.set(saved);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof navigator !== 'undefined') {
|
||||||
|
const browserLang = navigator.language.slice(0, 2);
|
||||||
|
if (translations[browserLang]) {
|
||||||
|
currentLanguage.set(browserLang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Liste des langues disponibles
|
||||||
|
export const availableLanguages = [
|
||||||
|
{ code: 'fr', name: 'Français', flag: '🇫🇷' },
|
||||||
|
{ code: 'en', name: 'English', flag: '🇬🇧' },
|
||||||
|
{ code: 'ht', name: 'Kreyòl Ayisyen', flag: '🇭🇹' }
|
||||||
|
];
|
||||||
@@ -3,4 +3,5 @@ import { svelte } from '@sveltejs/vite-plugin-svelte'
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [svelte()],
|
plugins: [svelte()],
|
||||||
|
base: '/gwada-sirius/', // Nom de ton repository
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user