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
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
# Fichiers de build
|
||||
dist/
|
||||
build/
|
||||
.svelte-kit/
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
# Dépendances
|
||||
node_modules/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Environment variables
|
||||
# Fichiers d'environnement
|
||||
.env
|
||||
.env.local
|
||||
.env.production
|
||||
|
||||
# Build files
|
||||
build/
|
||||
.svelte-kit/
|
||||
# Logs
|
||||
*.log
|
||||
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
|
||||
- **Guide scientifique** : Comprenez la mécanique céleste du phénomène
|
||||
- **Patrimoine culturel** : Découvrez l'importance de Sirius dans les cultures africaines
|
||||
- **Guide pratique** : Instructions détaillées pour réussir votre observation
|
||||
- **Visualisation interactive** : Globe animé montrant la progression mondiale du phénomène
|
||||
- **Svelte** - Framework JavaScript réactif
|
||||
- **Canvas 2D** - Pour les animations et visualisations
|
||||
- **GeoJSON** - Pour les cartes interactives
|
||||
- **i18n** - Support multilingue (Français, English, Kreyòl Ayisyen)
|
||||
|
||||
## 🛠️ Technologies
|
||||
|
||||
- **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
|
||||
## 📦 Installation
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
# Installer les dépendances
|
||||
@@ -35,41 +33,39 @@ npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 📦 Build
|
||||
## 🌍 Fonctionnalités
|
||||
|
||||
```bash
|
||||
# Créer une version de production
|
||||
npm run build
|
||||
|
||||
# Prévisualiser la version de production
|
||||
npm run preview
|
||||
```
|
||||
- **Calculateur de prédictions** : Dates précises pour 7 sites d'observation
|
||||
- **Globe interactif** : Visualisation de la trajectoire de Sirius
|
||||
- **Carte des sites** : Localisation des meilleurs points d'observation
|
||||
- **Multilingue** : Interface en français, anglais et créole haïtien
|
||||
- **Responsive** : Optimisé pour mobile et desktop
|
||||
- **Animations** : Ciel étoilé animé et patterns africains
|
||||
|
||||
## 🎨 Design
|
||||
|
||||
Le design s'inspire des motifs géométriques africains (Kente, Bogolan, Adinkra) avec une palette de couleurs panafricaine et caribéenne :
|
||||
- Or (Sirius, soleil)
|
||||
- Rouge profond
|
||||
- Vert forêt
|
||||
- Bleu nuit profond
|
||||
Le design s'inspire des motifs africains (Kente, Adinkra, Bogolan) avec une palette de couleurs symbolique :
|
||||
- Or (#FFD700) - Sirius et la lumière divine
|
||||
- Rouge (#FF6B35) - Force et énergie vitale
|
||||
- Vert (#4CAF50) - Nature et renouveau
|
||||
- Bleu nuit (#0A1628) - Ciel nocturne
|
||||
|
||||
## 📱 Mobile First
|
||||
## 👥 Crédits
|
||||
|
||||
L'application est conçue en priorité pour les appareils mobiles avec :
|
||||
- Navigation responsive
|
||||
- Touch-friendly
|
||||
- Performance optimisée
|
||||
Projet réalisé avec ❤️ par :
|
||||
- [cyber-mawonaj](https://github.com/cyber-mawonaj)
|
||||
- [OKI](https://o-k-i.net)
|
||||
|
||||
## 📜 Licence
|
||||
|
||||
Ce projet est sous licence MIT - voir le fichier [LICENSE](LICENSE) pour plus de détails.
|
||||
|
||||
## 🙏 Remerciements
|
||||
|
||||
- Données astronomiques calculées avec Stellarium
|
||||
- Inspiré du rapport "Sirius en Guadeloupe : Guide Astronomique et Culturel"
|
||||
- Patrimoine culturel africain et caribéen
|
||||
|
||||
## 📄 Licence
|
||||
|
||||
Ce projet est sous licence MIT.
|
||||
- Les anciens Égyptiens pour leur sagesse astronomique
|
||||
- Les Dogons du Mali pour avoir préservé ces connaissances
|
||||
- La communauté guadeloupéenne pour perpétuer ces traditions
|
||||
|
||||
---
|
||||
|
||||
*"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>
|
||||
import { onMount } from 'svelte';
|
||||
import { initLanguage } from './lib/i18n.js';
|
||||
import Hero from './components/Hero.svelte';
|
||||
import Navigation from './components/Navigation.svelte';
|
||||
import About from './components/About.svelte';
|
||||
@@ -7,15 +8,19 @@
|
||||
import Science from './components/Science.svelte';
|
||||
import Culture from './components/Culture.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 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 activeSection = 'home';
|
||||
|
||||
onMount(() => {
|
||||
// Initialiser la langue
|
||||
initLanguage();
|
||||
|
||||
const handleScroll = () => {
|
||||
scrollY = window.scrollY;
|
||||
|
||||
@@ -45,10 +50,11 @@
|
||||
<About />
|
||||
<Predictions />
|
||||
<Science />
|
||||
<Calculations />
|
||||
<Dogon />
|
||||
<Culture />
|
||||
<Observatory />
|
||||
<Globe />
|
||||
<GlobeOpenGlobus />
|
||||
<ObservationMap />
|
||||
<GlobeGeoJSON />
|
||||
</main>
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from '../lib/i18n.js';
|
||||
|
||||
let visible = false;
|
||||
|
||||
@@ -71,8 +72,8 @@
|
||||
{#each features as feature, i}
|
||||
<div class="feature-card card" style="animation-delay: {i * 0.2}s">
|
||||
<div class="feature-icon">{feature.icon}</div>
|
||||
<h3>{feature.title}</h3>
|
||||
<p>{feature.description}</p>
|
||||
<h3>{$t(feature.title)}</h3>
|
||||
<p>{$t(feature.description)}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -165,6 +166,17 @@
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.features-grid {
|
||||
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);
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.timeline-items {
|
||||
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>
|
||||
import { t } from '../lib/i18n.js';
|
||||
const currentYear = new Date().getFullYear();
|
||||
</script>
|
||||
|
||||
@@ -57,6 +58,14 @@
|
||||
|
||||
<div class="footer-bottom">
|
||||
<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">
|
||||
"Les étoiles sont les ancêtres qui veillent sur nous" - Proverbe africain
|
||||
</p>
|
||||
@@ -121,6 +130,27 @@
|
||||
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 {
|
||||
margin-top: 0.5rem;
|
||||
font-style: italic;
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
|
||||
// Rotation automatique
|
||||
if (!isDragging) {
|
||||
rotation += 0.002;
|
||||
rotation -= 0.002;
|
||||
}
|
||||
|
||||
requestAnimationFrame(draw);
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
async function initGlobe() {
|
||||
// Charger OpenGlobus depuis CDN
|
||||
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 = () => {
|
||||
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>
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from '../lib/i18n.js';
|
||||
|
||||
let starsCanvas;
|
||||
let mouseX = 0;
|
||||
let mouseY = 0;
|
||||
let mouseX = 0.5;
|
||||
let mouseY = 0.5;
|
||||
|
||||
onMount(() => {
|
||||
const ctx = starsCanvas.getContext('2d');
|
||||
const stars = [];
|
||||
const starCount = 150;
|
||||
|
||||
// Créer les étoiles
|
||||
for (let i = 0; i < 200; i++) {
|
||||
stars.push({
|
||||
x: Math.random() * starsCanvas.width,
|
||||
y: Math.random() * starsCanvas.height,
|
||||
size: Math.random() * 2,
|
||||
speed: Math.random() * 0.5 + 0.1,
|
||||
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;
|
||||
// Attendre que le DOM soit prêt
|
||||
setTimeout(() => {
|
||||
// Redimensionner le canvas
|
||||
const resize = () => {
|
||||
const hero = starsCanvas.parentElement;
|
||||
starsCanvas.width = hero.offsetWidth;
|
||||
starsCanvas.height = hero.offsetHeight;
|
||||
|
||||
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));
|
||||
});
|
||||
// Réinitialiser les étoiles après redimensionnement
|
||||
if (stars.length === 0) {
|
||||
for (let i = 0; i < starCount; i++) {
|
||||
stars.push({
|
||||
x: Math.random() * starsCanvas.width,
|
||||
y: Math.random() * starsCanvas.height,
|
||||
size: Math.random() * 2,
|
||||
brightness: Math.random(),
|
||||
speed: 0.1 + Math.random() * 0.5
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
// Redimensionner le canvas
|
||||
const resize = () => {
|
||||
starsCanvas.width = window.innerWidth;
|
||||
starsCanvas.height = window.innerHeight;
|
||||
};
|
||||
|
||||
resize();
|
||||
animate();
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', resize);
|
||||
};
|
||||
resize();
|
||||
window.addEventListener('resize', resize);
|
||||
|
||||
// Animation des étoiles
|
||||
const animate = () => {
|
||||
ctx.clearRect(0, 0, starsCanvas.width, starsCanvas.height);
|
||||
|
||||
// Effet de parallaxe avec la souris
|
||||
const offsetX = (mouseX - 0.5) * 20;
|
||||
const offsetY = (mouseY - 0.5) * 20;
|
||||
|
||||
stars.forEach(star => {
|
||||
// Mouvement subtil
|
||||
star.x += Math.sin(Date.now() * 0.0001 * star.speed) * 0.5;
|
||||
star.y += Math.cos(Date.now() * 0.0001 * star.speed) * 0.3;
|
||||
|
||||
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) => {
|
||||
mouseX = e.clientX / window.innerWidth;
|
||||
mouseY = e.clientY / window.innerHeight;
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
mouseX = (e.clientX - rect.left) / rect.width;
|
||||
mouseY = (e.clientY - rect.top) / rect.height;
|
||||
};
|
||||
</script>
|
||||
|
||||
<section id="home" class="hero" on:mousemove={handleMouseMove}>
|
||||
<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="container">
|
||||
<div class="hero-text">
|
||||
<h1 class="hero-title">
|
||||
<span class="title-line">Sirius</span>
|
||||
<span class="title-line">en Guadeloupe</span>
|
||||
<span class="title-line">{$t('hero_title1')}</span>
|
||||
<span class="title-line">{$t('hero_title2')}</span>
|
||||
</h1>
|
||||
<p class="hero-subtitle">
|
||||
Wep Ronpet - L'Ouverture de l'Année Cosmique
|
||||
{$t('hero_subtitle')}
|
||||
</p>
|
||||
<p class="hero-description">
|
||||
Le rendez-vous millénaire entre Ciel, Science et Culture
|
||||
{$t('hero_description')}
|
||||
</p>
|
||||
<p class="hero-date">
|
||||
Lever héliaque : <strong>22 Juillet 2025</strong>
|
||||
{$t('hero_date')}<strong>{$t('hero_date_value')}</strong>
|
||||
</p>
|
||||
<div class="hero-cta">
|
||||
<a href="#predictions" class="btn btn-primary">
|
||||
Découvrir les Prédictions
|
||||
{$t('hero_cta_predictions')}
|
||||
</a>
|
||||
<a href="#about" class="btn btn-outline">
|
||||
En Savoir Plus
|
||||
{$t('hero_cta_learn')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero-visual">
|
||||
<div class="sirius-glow"></div>
|
||||
<div class="ankh-symbol">☥</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scroll-indicator">
|
||||
<span>Défiler pour explorer</span>
|
||||
<div class="scroll-arrow">↓</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.hero {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
background: var(--gradient-dawn);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
@@ -130,6 +139,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero-pattern {
|
||||
@@ -138,36 +148,107 @@
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.1;
|
||||
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 {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
padding: var(--spacing-xl) 0;
|
||||
}
|
||||
|
||||
.hero-content .container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4rem;
|
||||
align-items: center;
|
||||
.hero-text {
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: clamp(3rem, 10vw, 6rem);
|
||||
line-height: 1;
|
||||
font-size: clamp(3rem, 8vw, 5rem);
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--color-light);
|
||||
}
|
||||
|
||||
.title-line {
|
||||
display: block;
|
||||
animation: fadeInUp 1s ease-out;
|
||||
animation-fill-mode: both;
|
||||
opacity: 0;
|
||||
animation: fadeInUp 1s ease-out forwards;
|
||||
}
|
||||
|
||||
.title-line:nth-child(2) {
|
||||
color: var(--color-primary);
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
@@ -195,10 +276,16 @@
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.hero-date strong {
|
||||
color: var(--color-primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.hero-cta {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
animation: fadeInUp 1s ease-out 0.8s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
@@ -214,44 +301,6 @@
|
||||
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 {
|
||||
from {
|
||||
opacity: 0;
|
||||
@@ -263,28 +312,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(10px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile */
|
||||
@media (max-width: 768px) {
|
||||
.hero-content .container {
|
||||
grid-template-columns: 1fr;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-visual {
|
||||
display: none;
|
||||
.hero-title {
|
||||
font-size: clamp(2.5rem, 10vw, 4rem);
|
||||
}
|
||||
|
||||
.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>
|
||||
import { t } from '../lib/i18n.js';
|
||||
import LanguageSelector from './LanguageSelector.svelte';
|
||||
|
||||
export let activeSection = 'home';
|
||||
|
||||
let isMenuOpen = false;
|
||||
@@ -8,12 +11,12 @@
|
||||
};
|
||||
|
||||
const menuItems = [
|
||||
{ id: 'home', label: 'Accueil', icon: '🌟' },
|
||||
{ id: 'about', label: 'Découvrir', icon: '✨' },
|
||||
{ id: 'predictions', label: 'Prédictions', icon: '📅' },
|
||||
{ id: 'science', label: 'Science', icon: '🔭' },
|
||||
{ id: 'culture', label: 'Culture', icon: '🌍' },
|
||||
{ id: 'observatory', label: 'Observer', icon: '👁️' }
|
||||
{ id: 'home', label: 'nav_home', icon: '🌟' },
|
||||
{ id: 'about', label: 'nav_about', icon: '✨' },
|
||||
{ id: 'predictions', label: 'nav_predictions', icon: '📅' },
|
||||
{ id: 'science', label: 'nav_science', icon: '🔭' },
|
||||
{ id: 'culture', label: 'nav_culture', icon: '🌍' },
|
||||
{ id: 'observatory', label: 'nav_observatory', icon: '👁️' }
|
||||
];
|
||||
|
||||
const scrollToSection = (id) => {
|
||||
@@ -43,11 +46,13 @@
|
||||
on:click|preventDefault={() => scrollToSection(item.id)}
|
||||
>
|
||||
<span class="nav-icon">{item.icon}</span>
|
||||
<span class="nav-label">{item.label}</span>
|
||||
<span class="nav-label">{$t(item.label)}</span>
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<LanguageSelector />
|
||||
</div>
|
||||
</div>
|
||||
</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({
|
||||
plugins: [svelte()],
|
||||
base: '/gwada-sirius/', // Nom de ton repository
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user